mirror of https://github.com/coder/code-server.git
160 lines
8.6 KiB
Diff
160 lines
8.6 KiB
Diff
Add VSCODE_PROXY_URI environment variable
|
|
|
|
This can be used by extensions to open a port and access it through the proxy.
|
|
|
|
It is available in the terminal as well.
|
|
|
|
This can be tested using printenv in the terminal and by using the
|
|
codeServerTest.proxyUri command through the test extension (copy it into your
|
|
extensions, use --extensions-dir, or symlink it).
|
|
|
|
This has e2e tests.
|
|
|
|
For the `asExternalUri` changes, you'll need to test manually by:
|
|
1. running code-server with the test extension
|
|
2. Command Palette > code-server: asExternalUri test
|
|
3. input a url like http://localhost:3000
|
|
4. it should show a notification and show output as <code-server>/proxy/3000
|
|
|
|
Do the same thing but set `VSCODE_PROXY_URI: "https://{{port}}-main-workspace-name-user-name.coder.com"`
|
|
and the output should replace `{{port}}` with port used in input url.
|
|
|
|
This also enables the forwared ports view panel by default.
|
|
|
|
Lastly, it adds a tunnelProvider so that ports are forwarded using code-server's
|
|
built-in proxy. You can test this by starting a server i.e. `python3 -m
|
|
http.server` and it should show a notification and show up in the ports panel
|
|
using the /proxy/port.
|
|
|
|
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
|
|
@@ -59,6 +59,7 @@ export interface IProductConfiguration {
|
|
readonly rootEndpoint?: string
|
|
readonly updateEndpoint?: string
|
|
readonly logoutEndpoint?: string
|
|
+ readonly proxyEndpointTemplate?: string
|
|
|
|
readonly version: string;
|
|
readonly date?: string;
|
|
Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
|
+++ code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
|
|
@@ -34,7 +34,7 @@ export class RemoteAuthorityResolverServ
|
|
isWorkbenchOptionsBasedResolution: boolean,
|
|
connectionToken: Promise<string> | string | undefined,
|
|
resourceUriProvider: ((uri: URI) => URI) | undefined,
|
|
- @IProductService productService: IProductService,
|
|
+ @IProductService private readonly productService: IProductService,
|
|
@ILogService private readonly _logService: ILogService,
|
|
) {
|
|
super();
|
|
@@ -85,9 +85,14 @@ export class RemoteAuthorityResolverServ
|
|
const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken);
|
|
performance.mark(`code/didResolveConnectionToken/${authorityPrefix}`);
|
|
this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`);
|
|
+ let options: ResolvedOptions | undefined;
|
|
+ if (this.productService.proxyEndpointTemplate) {
|
|
+ const proxyUrl = new URL(this.productService.proxyEndpointTemplate, mainWindow.location.href);
|
|
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
|
|
+ }
|
|
const defaultPort = (/^https:/.test(mainWindow.location.href) ? 443 : 80);
|
|
const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort);
|
|
- const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken } };
|
|
+ const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken }, options };
|
|
RemoteAuthorities.set(authority, host, port);
|
|
this._cache.set(authority, result);
|
|
this._onDidChangeConnectionData.fire();
|
|
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
|
|
@@ -312,6 +312,7 @@ export class WebClientServer {
|
|
rootEndpoint: base,
|
|
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
|
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
|
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
|
|
embedderIdentifier: 'server-distro',
|
|
extensionsGallery: this._productService.extensionsGallery,
|
|
};
|
|
Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
|
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
|
|
@@ -271,7 +271,7 @@ export async function createTerminalEnvi
|
|
|
|
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
|
// variables
|
|
- sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
|
|
+ sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI', 'VSCODE_PROXY_URI');
|
|
|
|
// Merge config (settings) and ShellLaunchConfig environments
|
|
mergeEnvironments(env, allowedEnvFromConfig);
|
|
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
|
|
@@ -19,6 +19,7 @@ import { ISecretStorageProvider } from '
|
|
import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window';
|
|
import type { IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/browser/web.api';
|
|
import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService';
|
|
+import { extractLocalHostUriMetaDataForPortMapping, TunnelOptions, TunnelCreationOptions } from 'vs/platform/tunnel/common/tunnel';
|
|
import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
|
|
import { create } from 'vs/workbench/workbench.web.main';
|
|
|
|
@@ -571,6 +572,39 @@ class WorkspaceProvider implements IWork
|
|
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
|
|
workspaceProvider: WorkspaceProvider.create(config),
|
|
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
|
|
+ resolveExternalUri: (uri: URI): Promise<URI> => {
|
|
+ let resolvedUri = uri
|
|
+ const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
|
|
+ if (localhostMatch && resolvedUri.authority !== location.host) {
|
|
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
|
|
+ const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
|
|
+ .replace('{{port}}', localhostMatch.port.toString())
|
|
+ .replace('{{host}}', window.location.host)
|
|
+ resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
|
|
+ } else {
|
|
+ throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
|
|
+ }
|
|
+ }
|
|
+ // If not localhost, return unmodified.
|
|
+ return Promise.resolve(resolvedUri)
|
|
+ },
|
|
+ tunnelProvider: {
|
|
+ tunnelFactory: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => {
|
|
+ const onDidDispose: Emitter<void> = new Emitter();
|
|
+ let isDisposed = false;
|
|
+ return Promise.resolve({
|
|
+ remoteAddress: tunnelOptions.remoteAddress,
|
|
+ localAddress: `localhost:${tunnelOptions.remoteAddress.port}`,
|
|
+ onDidDispose: onDidDispose.event,
|
|
+ dispose: () => {
|
|
+ if (!isDisposed) {
|
|
+ isDisposed = true;
|
|
+ onDidDispose.fire();
|
|
+ }
|
|
+ }
|
|
+ })
|
|
+ }
|
|
+ },
|
|
secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath
|
|
? undefined /* with a remote without embedder-preferred storage, store on the remote */
|
|
: new LocalStorageSecretStorageProvider(secretStorageCrypto),
|
|
Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
|
|
+++ code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
|
|
@@ -81,7 +81,7 @@ export class ForwardedPortsView extends
|
|
this.contextKeyListener = undefined;
|
|
}
|
|
|
|
- const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
|
|
+ const viewEnabled: boolean = true;
|
|
|
|
if (viewEnabled) {
|
|
const viewContainer = await this.getViewContainer();
|