Skip to content

Commit 075749d

Browse files
committed
Add Url callback provider
1 parent abd053f commit 075749d

File tree

1 file changed

+105
-2
lines changed

1 file changed

+105
-2
lines changed

src/vs/gitpod/browser/workbench/workbench.ts

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
import type { IDEFrontendState } from '@gitpod/gitpod-protocol/lib/ide-frontend-service';
99
import type { Status, TunnelStatus } from '@gitpod/local-app-api-grpcweb';
1010
import { isStandalone } from 'vs/base/browser/browser';
11+
import { streamToBuffer } from 'vs/base/common/buffer';
12+
import { CancellationToken } from 'vs/base/common/cancellation';
1113
import { Emitter, Event } from 'vs/base/common/event';
1214
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
1315
import { Schemas } from 'vs/base/common/network';
1416
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';
1620
import { localize } from 'vs/nls';
1721
import { parseLogLevel } from 'vs/platform/log/common/log';
1822
import product from 'vs/platform/product/common/product';
@@ -21,7 +25,7 @@ import { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from '
2125
import { extractLocalHostUriMetaDataForPortMapping, isLocalhost } from 'vs/platform/remote/common/tunnel';
2226
import { ColorScheme } from 'vs/platform/theme/common/theme';
2327
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';
2529

2630
const loadingGrpc = import('@improbable-eng/grpc-web');
2731
const 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+
3961
class 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+
117219
class 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

Comments
 (0)