8
8
import type { IDEFrontendState } from '@gitpod/gitpod-protocol/lib/ide-frontend-service' ;
9
9
import type { Status , TunnelStatus } from '@gitpod/local-app-api-grpcweb' ;
10
10
import { isStandalone } from 'vs/base/browser/browser' ;
11
+ import { streamToBuffer } from 'vs/base/common/buffer' ;
12
+ import { CancellationToken } from 'vs/base/common/cancellation' ;
11
13
import { Emitter , Event } from 'vs/base/common/event' ;
12
14
import { Disposable , DisposableStore , IDisposable } from 'vs/base/common/lifecycle' ;
13
15
import { Schemas } from 'vs/base/common/network' ;
14
16
import { isEqual } from 'vs/base/common/resources' ;
15
- import { URI } from 'vs/base/common/uri' ;
17
+ import { URI , UriComponents } from 'vs/base/common/uri' ;
18
+ import { generateUuid } from 'vs/base/common/uuid' ;
19
+ import { request } from 'vs/base/parts/request/browser/request' ;
16
20
import { localize } from 'vs/nls' ;
17
21
import { parseLogLevel } from 'vs/platform/log/common/log' ;
18
22
import product from 'vs/platform/product/common/product' ;
@@ -21,7 +25,7 @@ import { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from '
21
25
import { extractLocalHostUriMetaDataForPortMapping , isLocalhost } from 'vs/platform/remote/common/tunnel' ;
22
26
import { ColorScheme } from 'vs/platform/theme/common/theme' ;
23
27
import { isFolderToOpen , isWorkspaceToOpen } from 'vs/platform/windows/common/windows' ;
24
- import { commands , create , ICommand , ICredentialsProvider , IHomeIndicator , ITunnel , ITunnelProvider , IWorkspace , IWorkspaceProvider } from 'vs/workbench/workbench.web.api' ;
28
+ import { commands , create , ICommand , ICredentialsProvider , IHomeIndicator , ITunnel , ITunnelProvider , IURLCallbackProvider , IWorkspace , IWorkspaceProvider } from 'vs/workbench/workbench.web.api' ;
25
29
26
30
const loadingGrpc = import ( '@improbable-eng/grpc-web' ) ;
27
31
const loadingLocalApp = ( async ( ) => {
@@ -36,6 +40,24 @@ interface ICredential {
36
40
password : string ;
37
41
}
38
42
43
+ function doCreateUri ( path : string , queryValues : Map < string , string > ) : URI {
44
+ let query : string | undefined = undefined ;
45
+
46
+ if ( queryValues ) {
47
+ let index = 0 ;
48
+ queryValues . forEach ( ( value , key ) => {
49
+ if ( ! query ) {
50
+ query = '' ;
51
+ }
52
+
53
+ const prefix = ( index ++ === 0 ) ? '' : '&' ;
54
+ query += `${ prefix } ${ key } =${ encodeURIComponent ( value ) } ` ;
55
+ } ) ;
56
+ }
57
+
58
+ return URI . parse ( window . location . href ) . with ( { path, query } ) ;
59
+ }
60
+
39
61
class LocalStorageCredentialsProvider implements ICredentialsProvider {
40
62
41
63
static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider' ;
@@ -114,6 +136,86 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
114
136
115
137
}
116
138
139
+ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider {
140
+
141
+ static readonly FETCH_INTERVAL = 500 ; // fetch every 500ms
142
+ static readonly FETCH_TIMEOUT = 5 * 60 * 1000 ; // ...but stop after 5min
143
+
144
+ static readonly QUERY_KEYS = {
145
+ REQUEST_ID : 'vscode-requestId' ,
146
+ SCHEME : 'vscode-scheme' ,
147
+ AUTHORITY : 'vscode-authority' ,
148
+ PATH : 'vscode-path' ,
149
+ QUERY : 'vscode-query' ,
150
+ FRAGMENT : 'vscode-fragment'
151
+ } ;
152
+
153
+ private readonly _onCallback = this . _register ( new Emitter < URI > ( ) ) ;
154
+ readonly onCallback = this . _onCallback . event ;
155
+
156
+ create ( options ?: Partial < UriComponents > ) : URI {
157
+ const queryValues : Map < string , string > = new Map ( ) ;
158
+
159
+ const requestId = generateUuid ( ) ;
160
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . REQUEST_ID , requestId ) ;
161
+
162
+ const { scheme, authority, path, query, fragment } = options ? options : { scheme : undefined , authority : undefined , path : undefined , query : undefined , fragment : undefined } ;
163
+
164
+ if ( scheme ) {
165
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . SCHEME , scheme ) ;
166
+ }
167
+
168
+ if ( authority ) {
169
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . AUTHORITY , authority ) ;
170
+ }
171
+
172
+ if ( path ) {
173
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . PATH , path ) ;
174
+ }
175
+
176
+ if ( query ) {
177
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . QUERY , query ) ;
178
+ }
179
+
180
+ if ( fragment ) {
181
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . FRAGMENT , fragment ) ;
182
+ }
183
+
184
+ // Start to poll on the callback being fired
185
+ this . periodicFetchCallback ( requestId , Date . now ( ) ) ;
186
+
187
+ return doCreateUri ( '/callback' , queryValues ) ;
188
+ }
189
+
190
+ private async periodicFetchCallback ( requestId : string , startTime : number ) : Promise < void > {
191
+
192
+ // Ask server for callback results
193
+ const queryValues : Map < string , string > = new Map ( ) ;
194
+ queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . REQUEST_ID , requestId ) ;
195
+
196
+ const result = await request ( {
197
+ url : doCreateUri ( '/fetch-callback' , queryValues ) . toString ( true )
198
+ } , CancellationToken . None ) ;
199
+
200
+ // Check for callback results
201
+ const content = await streamToBuffer ( result . stream ) ;
202
+ if ( content . byteLength > 0 ) {
203
+ try {
204
+ this . _onCallback . fire ( URI . revive ( JSON . parse ( content . toString ( ) ) ) ) ;
205
+ } catch ( error ) {
206
+ console . error ( error ) ;
207
+ }
208
+
209
+ return ; // done
210
+ }
211
+
212
+ // Continue fetching unless we hit the timeout
213
+ if ( Date . now ( ) - startTime < PollingURLCallbackProvider . FETCH_TIMEOUT ) {
214
+ setTimeout ( ( ) => this . periodicFetchCallback ( requestId , startTime ) , PollingURLCallbackProvider . FETCH_INTERVAL ) ;
215
+ }
216
+ }
217
+ }
218
+
117
219
class WorkspaceProvider implements IWorkspaceProvider {
118
220
119
221
static QUERY_PARAM_EMPTY_WINDOW = 'ew' ;
@@ -720,6 +822,7 @@ async function doStart(): Promise<IDisposable> {
720
822
developmentOptions : {
721
823
logLevel : logLevel ? parseLogLevel ( logLevel ) : undefined
722
824
} ,
825
+ urlCallbackProvider : new PollingURLCallbackProvider ( ) ,
723
826
credentialsProvider,
724
827
productConfiguration : {
725
828
linkProtectionTrustedDomains : [
0 commit comments