2022-03-22 21:07:14 +01:00
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.
2022-10-14 18:08:58 +02:00
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.
2022-10-24 20:11:44 +02:00
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.
2022-03-22 21:07:14 +01:00
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
@@ -35,6 +35,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
2022-06-21 23:51:46 +02:00
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/comm
2022-03-22 21:07:14 +01:00
import { RemoteAuthorities } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
2022-06-21 23:51:46 +02:00
import { IProductService } from 'vs/platform/product/common/productService';
2022-03-22 21:07:14 +01:00
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
2022-08-17 03:26:19 +02:00
import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts';
2022-03-22 21:07:14 +01:00
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
2022-09-09 18:28:54 +02:00
@@ -23,7 +23,7 @@ export class RemoteAuthorityResolverServ
private readonly _connectionToken: Promise<string> | string | undefined;
2022-03-22 21:07:14 +01:00
private readonly _connectionTokens: Map<string, string>;
2022-09-09 18:28:54 +02:00
- constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) {
+ constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, private readonly proxyEndpointTemplate?: string) {
2022-03-22 21:07:14 +01:00
super();
this._connectionToken = connectionToken;
2022-09-09 18:28:54 +02:00
this._connectionTokens = new Map<string, string>();
@@ -61,9 +61,14 @@ export class RemoteAuthorityResolverServ
2022-03-22 21:07:14 +01:00
2022-09-09 18:28:54 +02:00
private async _doResolveAuthority(authority: string): Promise<ResolverResult> {
const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken);
2022-08-17 03:26:19 +02:00
+ let options: ResolvedOptions | undefined;
2022-03-22 21:07:14 +01:00
+ if (this.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href);
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
+ }
2022-08-17 03:26:19 +02:00
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort);
2022-09-09 18:28:54 +02:00
- const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken } };
+ const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken }, options };
RemoteAuthorities.set(authority, result.authority.host, result.authority.port);
this._cache.set(authority, result);
this._onDidChangeConnectionData.fire();
2022-03-22 21:07:14 +01:00
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
2022-08-17 03:26:19 +02:00
@@ -314,6 +314,7 @@ export class WebClientServer {
2022-06-21 23:51:46 +02:00
rootEndpoint: base,
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
2022-08-11 20:26:47 +02:00
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
2022-10-24 20:11:44 +02:00
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
2022-06-21 23:51:46 +02:00
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
},
2022-03-22 21:07:14 +01:00
Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
2022-11-03 22:48:59 +01:00
@@ -248,7 +248,7 @@ export class BrowserMain extends Disposa
2022-03-22 21:07:14 +01:00
// Remote
const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName);
2022-06-21 23:51:46 +02:00
- const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider);
+ const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider, this.configuration.productConfiguration?.proxyEndpointTemplate);
2022-03-22 21:07:14 +01:00
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
// Signing
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
2022-11-03 22:48:59 +01:00
@@ -381,7 +381,7 @@ export async function createTerminalEnvi
2022-03-22 21:07:14 +01:00
// 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);
2022-10-14 18:08:58 +02:00
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
2022-10-18 01:30:39 +02:00
@@ -21,6 +21,7 @@ import type { ICredentialsProvider } fro
import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api';
import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService';
2022-10-24 20:11:44 +02:00
+import { extractLocalHostUriMetaDataForPortMapping, TunnelOptions, TunnelCreationOptions } from 'vs/platform/tunnel/common/tunnel';
2022-10-14 18:08:58 +02:00
interface ICredential {
service: string;
2022-10-24 20:11:44 +02:00
@@ -511,6 +512,38 @@ function doCreateUri(path: string, query
2022-10-14 18:08:58 +02:00
} : undefined,
workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
- credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider() // with a remote, we don't use a local credentials provider
+ credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider(), // with a remote, we don't use a local credentials provider
+ resolveExternalUri: (uri: URI): Promise<URI> => {
+ let resolvedUri = uri
2022-10-24 20:11:44 +02:00
+ const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
2022-10-14 18:08:58 +02:00
+
2022-10-24 20:11:44 +02:00
+ if (localhostMatch && resolvedUri.authority !== location.host) {
2022-10-14 18:08:58 +02:00
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
+ resolvedUri = URI.parse(new URL(config.productConfiguration.proxyEndpointTemplate.replace('{{port}}', localhostMatch.port.toString()), 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)
2022-10-24 20:11:44 +02:00
+ },
+ 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();
+ }
+ }
+ })
+ }
2022-10-14 18:08:58 +02:00
+ }
});
})();
2022-10-24 20:11:44 +02:00
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
@@ -73,7 +73,7 @@ export class ForwardedPortsView extends
this.contextKeyListener = undefined;
}
- const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
+ const viewEnabled: boolean = true;
if (this.environmentService.remoteAuthority && viewEnabled) {
const viewContainer = await this.getViewContainer();