Skip to content

Commit b389ce3

Browse files
committed
handle extension resource
1 parent 16feb68 commit b389ce3

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

src/vs/server/node/webClientServer.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ import { FileAccess, connectionTokenCookieName, connectionTokenQueryName } from
1919
import { generateUuid } from 'vs/base/common/uuid';
2020
import { IProductService } from 'vs/platform/product/common/productService';
2121
import { ServerConnectionToken, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken';
22+
import { IRequestService } from 'vs/platform/request/common/request';
23+
import { IHeaders } from 'vs/base/parts/request/common/request';
24+
import { CancellationToken } from 'vs/base/common/cancellation';
25+
import { URI } from 'vs/base/common/uri';
26+
import { streamToBuffer } from 'vs/base/common/buffer';
27+
import { IProductConfiguration } from 'vs/base/common/product';
2228

2329
const textMimeType = {
2430
'.html': 'text/html',
@@ -77,7 +83,8 @@ export class WebClientServer {
7783
private readonly _connectionToken: ServerConnectionToken,
7884
@IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService,
7985
@ILogService private readonly _logService: ILogService,
80-
@IProductService private readonly _productService: IProductService
86+
@IRequestService private readonly _requestService: IRequestService,
87+
@IProductService private readonly _productService: IProductService,
8188
) { }
8289

8390
async handle(req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
@@ -92,6 +99,10 @@ export class WebClientServer {
9299
// always serve static requests, even without a token
93100
return this._handleStatic(req, res, parsedUrl);
94101
}
102+
if (/^\/extensionResource\//.test(pathname)) {
103+
// always serve static requests, even without a token
104+
return this._handleExtensionResource(req, res, parsedUrl);
105+
}
95106
if (pathname === '/') {
96107
// the token handling is done inside the handler
97108
return this._handleRoot(req, res, parsedUrl);
@@ -128,6 +139,38 @@ export class WebClientServer {
128139
return serveFile(this._logService, req, res, filePath, headers);
129140
}
130141

142+
/**
143+
* Handle HTTP requests for /static/*
144+
*/
145+
private async _handleExtensionResource(req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
146+
const normalizedPathname = decodeURIComponent(parsedUrl.pathname!); // support paths that are uri-encoded (e.g. spaces => %20)
147+
const path = normalize(normalizedPathname.substr('/extensionResource/'.length));
148+
const url = URI.parse(path).with({ scheme: 'https', authority: path.substring(0, path.indexOf('/')), path: path.substring(path.indexOf('/') + 1) });
149+
150+
const headers: IHeaders = {};
151+
for (const header of req.rawHeaders) {
152+
if (req.headers[header]) {
153+
headers[header] = req.headers[header]![0];
154+
}
155+
}
156+
157+
const context = await this._requestService.request({
158+
type: 'GET',
159+
url: url.toString(true),
160+
headers
161+
}, CancellationToken.None);
162+
163+
if (context.res.statusCode && context.res.statusCode !== 200) {
164+
this._logService.info(`Request to '${url.toString(true)}' failed with status code ${context.res.statusCode}`);
165+
throw new Error(`Server returned ${context.res.statusCode}`);
166+
}
167+
168+
const responseHeaders = context.res.headers;
169+
res.writeHead(200, responseHeaders);
170+
const buffer = await streamToBuffer(context.stream);
171+
return res.end(buffer.buffer);
172+
}
173+
131174
/**
132175
* Handle HTTP requests for /
133176
*/
@@ -183,12 +226,16 @@ export class WebClientServer {
183226
accessToken: this._environmentService.args['github-auth'],
184227
scopes: [['user:email'], ['repo']]
185228
} : undefined;
229+
const resourceUrlTemplate = this._productService.extensionsGallery ? URI.parse(this._productService.extensionsGallery.resourceUrlTemplate) : undefined;
186230
const data = (await util.promisify(fs.readFile)(filePath)).toString()
187231
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({
188232
remoteAuthority,
189233
_wrapWebWorkerExtHostInIframe,
190234
developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined },
191235
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
236+
productConfiguration: <Partial<IProductConfiguration>>{
237+
extensionsGallery: resourceUrlTemplate ? { ...this._productService.extensionsGallery, 'resourceUrlTemplate': resourceUrlTemplate.with({ scheme: 'http', authority: remoteAuthority, path: `extensionResource/${resourceUrlTemplate.authority}${resourceUrlTemplate.path}` }).toString(true) } : undefined
238+
}
192239
})))
193240
.replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '');
194241

0 commit comments

Comments
 (0)