code-server/patches/base-path.diff
Asher 5ce99f8d1c
chore: update Code to 1.68 (#5263)
* chore: update Code to 1.67

Was able to remove our changes to common/webview.ts since they are
upstream now.

Other than that no serious changes, just context diffs.

* chore: update Code to 1.68

- Upstream moved the web socket endpoint so change the Express route
  from / to *.  That will let web sockets work at any endpoint.
- Everything in the workbench config is basically the same but
  de-indented (upstream extracted it into a separate object which
  resulted in a de-indent), the ordering is slightly different, and
  instead of vscodeBase we now need vscodeBase + this._staticRoute since
  everything is served from a sub-path now.
- Move manifest link back to the root since that is where we host our
  manifest.
- Change RemoteAuthoritiesImpl to use the same path building method as
  in other places (+ instead of using URI.parse/join).
- Use existing host/port in RemoteAuthoritiesImpl and
  BrowserSocketFactory instead of patching them to use window.location
  (these are set from window.location to begin with so it should be the
  same result but with less patching).
- Since BrowserSocketFactory includes a sub-path now (endpoints were
  changed upstream to serve from /quality/commit instead of from the
  root) the patch there has changed to prepend the base to that
  path (instead of using the base directly).
- The workbench HTML now natively supports a base URL in the form of
  WORKBENCH_WEB_BASE_URL so no need for VS_BASE patches there anymore.
- Upstream added type="image/x-icon" so I did as well.
- Move the language patch to the end of the series so it is easier to
  eventually remove.
- Remove the existing NLS config in favor of one that supports
  extensions.
- Upstream deleted webview main.js and inlined it into the HTML so move
  that code (the parent origin check) into both those HTML files
  (index.html and index-no-csp.html).
- The remaining diff is from changes to the surrounding context or a
  line was changed slightly by upstream (for example renamed files or
  new arguments like to the remote authority resolver).

* fix: modify product.json before building

Code injects this into the client during the build process so it needs
to be updated before we build.

* fix: update inline script nonces

* Update HTML base path test

* fix: missing commit

Code overrides it with nothing.

The date is also already injected.

* fix: web extensions breaking when the commit changes

By just using the marketplace directly instead of going through the
backend.  I am not sure what the point is when searching extensions
already goes directly to the marketplace anyway.

But also remove the prefix that breaks this as well because otherwise
existing installations will break.
2022-06-21 21:51:46 +00:00

286 lines
14 KiB
Diff

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
@@ -157,7 +157,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" />
</head>
<body aria-label="">
@@ -39,7 +39,7 @@
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/loader.js"></script>
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/webPackagePaths.js"></script>
<script>
- const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location.origin).toString();
+ const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location).toString();
Object.keys(self.webPackagePaths).map(function (key, index) {
self.webPackagePaths[key] = `${baseUrl}/remote/web/node_modules/${key}/${self.webPackagePaths[key]}`;
});
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 data-name="vs/workbench/workbench.web.main" rel="stylesheet" href="{{WORKBENCH_WEB_BASE_URL}}/out/vs/workbench/workbench.web.main.css">
</head>
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
@@ -274,6 +274,7 @@ export class BrowserSocketFactory implem
connect(host: string, port: number, path: string, query: string, debugLabel: string, callback: IConnectCallback): void {
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
const socket = this._webSocketFactory.create(`${webSocketSchema}://${/:/.test(host) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
const errorListener = socket.onError((err) => callback(err, undefined));
socket.onOpen(() => {
@@ -282,6 +283,3 @@ export class BrowserSocketFactory implem
});
}
}
-
-
-
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
@@ -267,14 +267,10 @@ export class WebClientServer {
return res.end();
}
- let originalHost = req.headers['x-original-host'];
- if (Array.isArray(originalHost)) {
- originalHost = originalHost[0];
- }
- const remoteAuthority = originalHost || req.headers.host;
- if (!remoteAuthority) {
- return serveError(req, res, 400, `Bad request.`);
- }
+ // It is not possible to reliably detect the remote authority on the server
+ // in all cases. Set this to something invalid to make sure we catch code
+ // that is using this when it should not.
+ const remoteAuthority = 'remote';
function asJSON(value: unknown): string {
return JSON.stringify(value).replace(/"/g, '&quot;');
@@ -297,6 +293,8 @@ export class WebClientServer {
scopes: [['user:email'], ['repo']]
} : undefined;
+ const base = relativeRoot(getOriginalUrl(req))
+ const vscodeBase = relativePath(getOriginalUrl(req))
const workbenchWebConfiguration = {
remoteAuthority,
@@ -308,6 +306,7 @@ export class WebClientServer {
workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']),
productConfiguration: <Partial<IProductConfiguration>>{
codeServerVersion: this._productService.codeServerVersion,
+ rootEndpoint: base,
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
...this._productService.extensionsGallery,
@@ -328,8 +327,10 @@ export class WebClientServer {
const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
- WORKBENCH_WEB_BASE_URL: this._staticRoute,
- WORKBENCH_NLS_BASE_URL: nlsBaseUrl ? `${nlsBaseUrl}${this._productService.commit}/${this._productService.version}/` : '',
+ WORKBENCH_WEB_BASE_URL: vscodeBase + this._staticRoute,
+ WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${this._productService.commit}/${this._productService.version}/` : ''),
+ BASE: base,
+ VS_BASE: vscodeBase,
};
@@ -419,3 +420,70 @@ export class WebClientServer {
return 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
@@ -32,6 +32,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
@@ -485,6 +485,7 @@ function doCreateUri(path: string, query
});
}
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
return URI.parse(window.location.href).with({ path, query });
}
@@ -496,7 +497,7 @@ function doCreateUri(path: string, query
if (!configElement || !configElementAttribute) {
throw new Error('Missing web configuration element');
}
- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute);
+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host }
// Create workbench
create(document.body, {
Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
@@ -16,7 +16,6 @@ import { getServiceMachineId } from 'vs/
import { IStorageService } from 'vs/platform/storage/common/storage';
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { RemoteAuthorities } from 'vs/base/common/network';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -75,7 +74,7 @@ export abstract class AbstractExtensionR
public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined {
if (this._extensionGalleryResourceUrlTemplate) {
const uri = URI.parse(format2(this._extensionGalleryResourceUrlTemplate, { publisher: galleryExtension.publisher, name: galleryExtension.name, version: galleryExtension.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;
}