/
githubmirror
/
code-server
Обзор
Документация
Войти
/
githubmirror
/
code-server
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4.117.0
patches/base-path.diff
307 строк
15 KB
Olivier Benz
Update Code to 1.114.0 (#7735)
04 апр 2026, 01:07
Не верифицирован
04 апр 2026, 01:07
5fe286e
Код
Авторство
О чём код?
Add base path support Some users will host code-server behind a path-rewriting reverse proxy, for example domain.tld/my/base/path. This patch adds support for that since Code assumes everything is on / by default. To test this serve code-server behind a reverse proxy with a path like /code. Index: code-server/lib/vscode/src/vs/base/common/network.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/network.ts +++ code-server/lib/vscode/src/vs/base/common/network.ts @@ -245,7 +245,9 @@ class RemoteAuthoritiesImpl { return URI.from({ scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, authority: `${host}:${port}`, - path: this._remoteResourcesPath, + path: platform.isWeb + ? (window.location.pathname + "/" + this._remoteResourcesPath).replace(/\/\/+/g, "/") + : this._remoteResourcesPath, query }); } Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html @@ -11,8 +11,8 @@ <meta name="mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-title" content="Code"> - <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" /> - <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" /> + <link rel="apple-touch-icon" sizes="192x192" href="{{BASE}}/_static/src/browser/media/pwa-icon-192.png" /> + <link rel="apple-touch-icon" sizes="512x512" href="{{BASE}}/_static/src/browser/media/pwa-icon-512.png" /> <!-- Disable pinch zooming --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> @@ -27,9 +27,9 @@ <meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}"> <!-- Workbench Icon/Manifest/CSS --> - <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" /> - <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" type="image/x-icon" /> - <link rel="manifest" href="/manifest.json" crossorigin="use-credentials" /> + <link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" /> + <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" type="image/x-icon" /> + <link rel="manifest" href="{{VS_BASE}}/manifest.json" crossorigin="use-credentials" /> <style id="vscode-css-modules" type="text/css" media="screen"></style> </head> @@ -39,7 +39,7 @@ <!-- Startup (do not modify order of script tags!) --> <script> - const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location.origin).toString(); + const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location).toString(); globalThis._VSCODE_FILE_ROOT = baseUrl + '/out/'; </script> <script> Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html @@ -11,8 +11,8 @@ <meta name="mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-title" content="Code"> - <link rel="apple-touch-icon" sizes="192x192" href="/_static/src/browser/media/pwa-icon-192.png" /> - <link rel="apple-touch-icon" sizes="512x512" href="/_static/src/browser/media/pwa-icon-512.png" /> + <link rel="apple-touch-icon" sizes="192x192" href="{{BASE}}/_static/src/browser/media/pwa-icon-192.png" /> + <link rel="apple-touch-icon" sizes="512x512" href="{{BASE}}/_static/src/browser/media/pwa-icon-512.png" /> <!-- Disable pinch zooming --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> @@ -24,9 +24,9 @@ <meta id="vscode-workbench-auth-session" data-settings="{{WORKBENCH_AUTH_SESSION}}"> <!-- Workbench Icon/Manifest/CSS --> - <link rel="icon" href="/_static/src/browser/media/favicon-dark-support.svg" /> - <link rel="alternate icon" href="/_static/src/browser/media/favicon.ico" type="image/x-icon" /> - <link rel="manifest" href="/manifest.json" crossorigin="use-credentials" /> + <link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" /> + <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" type="image/x-icon" /> + <link rel="manifest" href="{{VS_BASE}}/manifest.json" crossorigin="use-credentials" /> <link rel="stylesheet" href="{{WORKBENCH_WEB_BASE_URL}}/out/vs/code/browser/workbench/workbench.css"> </head> @@ -36,7 +36,7 @@ <!-- Startup (do not modify order of script tags!) --> <script> - const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location.origin).toString(); + const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location).toString(); globalThis._VSCODE_FILE_ROOT = baseUrl + '/out/'; </script> <script> Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts +++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -282,6 +282,7 @@ export class BrowserSocketFactory implem connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> { return new Promise<ISocket>((resolve, reject) => { const webSocketSchema = (/^https:/.test(mainWindow.location.href) ? 'wss' : 'ws'); + path = (mainWindow.location.pathname + "/" + path).replace(/\/\/+/g, "/") const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); const disposables = new DisposableStore(); disposables.add(socket.onError(reject)); Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts @@ -265,7 +265,9 @@ export class WebClientServer { }; // Prefix routes with basePath for clients - const basePath = getFirstHeader('x-forwarded-prefix') || this._basePath; + const rootBase = relativeRoot(getOriginalUrl(req)) + const vscodeBase = relativePath(getOriginalUrl(req)) + const basePath = vscodeBase || getFirstHeader('x-forwarded-prefix') || this._basePath; const queryConnectionToken = parsedUrl.query[connectionTokenQueryName]; if (typeof queryConnectionToken === 'string') { @@ -304,10 +306,14 @@ export class WebClientServer { }; const useTestResolver = (!this._environmentService.isBuilt && this._environmentService.args['use-test-resolver']); + // For now we are getting the remote authority from the client to avoid + // needing specific configuration for reverse proxies to work. Set this to + // something invalid to make sure we catch code that is using this value + // from the backend when it should not. let remoteAuthority = ( useTestResolver ? 'test+test' - : (getFirstHeader('x-original-host') || getFirstHeader('x-forwarded-host') || req.headers.host) + : 'remote' ); if (!remoteAuthority) { return serveError(req, res, 400, `Bad request.`); @@ -354,6 +360,7 @@ export class WebClientServer { const productConfiguration: Partial<Mutable<IProductConfiguration>> = { codeServerVersion: this._productService.codeServerVersion, + rootEndpoint: rootBase, embedderIdentifier: 'server-distro', extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? { ...this._productService.extensionsGallery, @@ -407,7 +414,9 @@ export class WebClientServer { WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '', WORKBENCH_WEB_BASE_URL: staticRoute, WORKBENCH_NLS_URL, - WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js` + WORKBENCH_NLS_FALLBACK_URL: `${staticRoute}/out/nls.messages.js`, + BASE: rootBase, + VS_BASE: basePath, }; // DEV --------------------------------------------------------------------------------------- @@ -444,7 +453,7 @@ export class WebClientServer { 'default-src \'self\';', 'img-src \'self\' https: data: blob:;', 'media-src \'self\';', - `script-src 'self' 'unsafe-eval' ${WORKBENCH_NLS_BASE_URL ?? ''} blob: 'nonce-1nline-m4p' ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' 'sha256-/r7rqQ+yrxt57sxLuQ6AMYcy/lUpvAIzHjIJt/OeLWU=' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html + `script-src 'self' 'unsafe-eval' ${WORKBENCH_NLS_BASE_URL ?? ''} blob: 'nonce-1nline-m4p' ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' 'sha256-/r7rqQ+yrxt57sxLuQ6AMYcy/lUpvAIzHjIJt/OeLWU=' ${useTestResolver ? '' : ``};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html 'child-src \'self\';', `frame-src 'self' https://*.vscode-cdn.net data:;`, 'worker-src \'self\' data: blob:;', @@ -517,3 +526,70 @@ export class WebClientServer { return void res.end(data); } } + +/** + * Remove extra slashes in a URL. + * + * This is meant to fill the job of `path.join` so you can concatenate paths and + * then normalize out any extra slashes. + * + * If you are using `path.join` you do not need this but note that `path` is for + * file system paths, not URLs. + */ +export const normalizeUrlPath = (url: string, keepTrailing = false): string => { + return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "") +} + +/** + * Get the relative path that will get us to the root of the page. For each + * slash we need to go up a directory. Will not have a trailing slash. + * + * For example: + * + * / => . + * /foo => . + * /foo/ => ./.. + * /foo/bar => ./.. + * /foo/bar/ => ./../.. + * + * All paths must be relative in order to work behind a reverse proxy since we + * we do not know the base path. Anything that needs to be absolute (for + * example cookies) must get the base path from the frontend. + * + * All relative paths must be prefixed with the relative root to ensure they + * work no matter the depth at which they happen to appear. + * + * For Express `req.originalUrl` should be used as they remove the base from the + * standard `url` property making it impossible to get the true depth. + */ +export const relativeRoot = (originalUrl: string): string => { + const depth = (originalUrl.split("?", 1)[0].match(/\//g) || []).length + return normalizeUrlPath("./" + (depth > 1 ? "../".repeat(depth - 1) : "")) +} + +/** + * Get the relative path to the current resource. + * + * For example: + * + * / => . + * /foo => ./foo + * /foo/ => . + * /foo/bar => ./bar + * /foo/bar/ => . + */ +export const relativePath = (originalUrl: string): string => { + const parts = originalUrl.split("?", 1)[0].split("/") + return normalizeUrlPath("./" + parts[parts.length - 1]) +} + +/** + * code-server serves Code using Express. Express removes the base from the url + * and puts the original in `originalUrl` so we must use this to get the correct + * depth. Code is not aware it is behind Express so the types do not match. We + * may want to continue moving code into Code and eventually remove the Express + * wrapper or move the web server back into code-server. + */ +export const getOriginalUrl = (req: http.IncomingMessage): string => { + return (req as any).originalUrl || req.url +} Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts @@ -66,6 +66,7 @@ export type ExtensionVirtualWorkspaceSup export interface IProductConfiguration { readonly codeServerVersion?: string + readonly rootEndpoint?: string readonly version: string; readonly date?: string; Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts @@ -339,7 +339,8 @@ class LocalStorageURLCallbackProvider ex this.startListening(); } - return URI.parse(mainWindow.location.href).with({ path: this._callbackRoute, query: queryParams.join('&') }); + const path = (mainWindow.location.pathname + "/" + this._callbackRoute).replace(/\/\/+/g, "/"); + return URI.parse(mainWindow.location.href).with({ path: path, query: queryParams.join('&') }); } private startListening(): void { @@ -584,17 +585,6 @@ class WorkspaceProvider implements IWork } } -function readCookie(name: string): string | undefined { - const cookies = document.cookie.split('; '); - for (const cookie of cookies) { - if (cookie.startsWith(name + '=')) { - return cookie.substring(name.length + 1); - } - } - - return undefined; -} - (function () { // Find config by checking for DOM @@ -604,8 +594,8 @@ function readCookie(name: string): strin if (!configElement || !configElementAttribute) { throw new Error('Missing web configuration element'); } - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute); - const secretStorageKeyPath = readCookie('vscode-secret-key-path'); + const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host } + const secretStorageKeyPath = (window.location.pathname + "/mint-key").replace(/\/\/+/g, "/"); const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported() ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto(); Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts +++ code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts @@ -120,7 +120,7 @@ export abstract class AbstractExtensionR : version, path: 'extension' })); - return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri; + return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri; } return undefined; }