mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-28 23:58:23 +01:00
and show those errors to the user if the port forward was user initiated.
86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
import { Schemas } from 'vs/base/common/network';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
|
|
import { extractLocalHostUriMetaDataForPortMapping, ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel';
|
|
|
|
export interface IWebviewPortMapping {
|
|
readonly webviewPort: number;
|
|
readonly extensionHostPort: number;
|
|
}
|
|
|
|
/**
|
|
* Manages port mappings for a single webview.
|
|
*/
|
|
export class WebviewPortMappingManager implements IDisposable {
|
|
|
|
private readonly _tunnels = new Map<number, RemoteTunnel>();
|
|
|
|
constructor(
|
|
private readonly _getExtensionLocation: () => URI | undefined,
|
|
private readonly _getMappings: () => readonly IWebviewPortMapping[],
|
|
private readonly tunnelService: ITunnelService
|
|
) { }
|
|
|
|
public async getRedirect(resolveAuthority: IAddress | null | undefined, url: string): Promise<string | undefined> {
|
|
const uri = URI.parse(url);
|
|
const requestLocalHostInfo = extractLocalHostUriMetaDataForPortMapping(uri);
|
|
if (!requestLocalHostInfo) {
|
|
return undefined;
|
|
}
|
|
|
|
for (const mapping of this._getMappings()) {
|
|
if (mapping.webviewPort === requestLocalHostInfo.port) {
|
|
const extensionLocation = this._getExtensionLocation();
|
|
if (extensionLocation && extensionLocation.scheme === Schemas.vscodeRemote) {
|
|
const tunnel = resolveAuthority && await this.getOrCreateTunnel(resolveAuthority, mapping.extensionHostPort);
|
|
if (tunnel) {
|
|
if (tunnel.tunnelLocalPort === mapping.webviewPort) {
|
|
return undefined;
|
|
}
|
|
return encodeURI(uri.with({
|
|
authority: `127.0.0.1:${tunnel.tunnelLocalPort}`,
|
|
}).toString(true));
|
|
}
|
|
}
|
|
|
|
if (mapping.webviewPort !== mapping.extensionHostPort) {
|
|
return encodeURI(uri.with({
|
|
authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}`
|
|
}).toString(true));
|
|
}
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
async dispose() {
|
|
for (const tunnel of this._tunnels.values()) {
|
|
await tunnel.dispose();
|
|
}
|
|
this._tunnels.clear();
|
|
}
|
|
|
|
private async getOrCreateTunnel(remoteAuthority: IAddress, remotePort: number): Promise<RemoteTunnel | undefined> {
|
|
const existing = this._tunnels.get(remotePort);
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
const tunnelOrError = await this.tunnelService.openTunnel({ getAddress: async () => remoteAuthority }, undefined, remotePort);
|
|
let tunnel: RemoteTunnel | undefined;
|
|
if (typeof tunnelOrError === 'string') {
|
|
tunnel = undefined;
|
|
}
|
|
if (tunnel) {
|
|
this._tunnels.set(remotePort, tunnel);
|
|
}
|
|
return tunnel;
|
|
}
|
|
}
|