88import type { IDEFrontendState } from '@gitpod/gitpod-protocol/lib/ide-frontend-service' ;
99import type { Status , TunnelStatus } from '@gitpod/local-app-api-grpcweb' ;
1010import { isStandalone } from 'vs/base/browser/browser' ;
11+ import { streamToBuffer } from 'vs/base/common/buffer' ;
12+ import { CancellationToken } from 'vs/base/common/cancellation' ;
1113import { Emitter , Event } from 'vs/base/common/event' ;
1214import { Disposable , DisposableStore , IDisposable } from 'vs/base/common/lifecycle' ;
1315import { Schemas } from 'vs/base/common/network' ;
1416import { 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' ;
1620import { localize } from 'vs/nls' ;
1721import { parseLogLevel } from 'vs/platform/log/common/log' ;
1822import product from 'vs/platform/product/common/product' ;
@@ -21,7 +25,7 @@ import { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from '
2125import { extractLocalHostUriMetaDataForPortMapping , isLocalhost } from 'vs/platform/remote/common/tunnel' ;
2226import { ColorScheme } from 'vs/platform/theme/common/theme' ;
2327import { 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' ;
2529
2630const loadingGrpc = import ( '@improbable-eng/grpc-web' ) ;
2731const loadingLocalApp = ( async ( ) => {
@@ -36,6 +40,24 @@ interface ICredential {
3640 password : string ;
3741}
3842
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+
3961class LocalStorageCredentialsProvider implements ICredentialsProvider {
4062
4163 static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider' ;
@@ -114,6 +136,86 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
114136
115137}
116138
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+
117219class WorkspaceProvider implements IWorkspaceProvider {
118220
119221 static QUERY_PARAM_EMPTY_WINDOW = 'ew' ;
@@ -720,6 +822,7 @@ async function doStart(): Promise<IDisposable> {
720822 developmentOptions : {
721823 logLevel : logLevel ? parseLogLevel ( logLevel ) : undefined
722824 } ,
825+ urlCallbackProvider : new PollingURLCallbackProvider ( ) ,
723826 credentialsProvider,
724827 productConfiguration : {
725828 linkProtectionTrustedDomains : [
0 commit comments