diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index f75e1e8dab8..395d47db616 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -97,7 +97,13 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun } async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise { - const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source, false); + const tunnel = await this.remoteExplorerService.forward({ + remote: tunnelOptions.remoteAddress, + local: tunnelOptions.localAddressPort, + name: tunnelOptions.label, + source, + elevateIfNeeded: false + }); if (tunnel) { if (!this.elevateionRetry && (tunnelOptions.localAddressPort !== undefined) @@ -121,7 +127,13 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun run: async () => { this.elevateionRetry = true; await this.remoteExplorerService.close({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }); - await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source, true); + await this.remoteExplorerService.forward({ + remote: tunnelOptions.remoteAddress, + local: tunnelOptions.localAddressPort, + name: tunnelOptions.label, + source, + elevateIfNeeded: true + }); this.elevateionRetry = false; } }]); diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 5487dac973f..1b7ac340ee6 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -380,7 +380,11 @@ class OnAutoForwardedAction extends Disposable { label: nls.localize('remote.tunnelsView.elevationButton', "Use Port {0} as Sudo...", tunnel.tunnelRemotePort), run: async () => { await this.remoteExplorerService.close({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }); - const newTunnel = await this.remoteExplorerService.forward({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, tunnel.tunnelRemotePort, undefined, undefined, true, undefined, false); + const newTunnel = await this.remoteExplorerService.forward({ + remote: { host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, + local: tunnel.tunnelRemotePort, + elevateIfNeeded: true + }, false); if (!newTunnel) { return; } @@ -461,7 +465,7 @@ class OutputAutomaticPortForwarding extends Disposable { if (this.privilegedOnly() && !isPortPrivileged(localUrl.port, (await this.remoteAgentService.getEnvironment())?.os)) { return; } - const forwarded = await this.remoteExplorerService.forward(localUrl, undefined, undefined, undefined, undefined, undefined, false, attributes ?? null); + const forwarded = await this.remoteExplorerService.forward({ remote: localUrl }, false, attributes ?? null); if (forwarded) { this.notifier.doAction([forwarded]); } @@ -589,7 +593,7 @@ class ProcAutomaticPortForwarding extends Disposable { if (portAttributes?.onAutoForward === OnPortForward.Ignore) { continue; } - const forwarded = await this.remoteExplorerService.forward(value, undefined, undefined, undefined, undefined, undefined, false, portAttributes ?? null); + const forwarded = await this.remoteExplorerService.forward({ remote: value }, false, portAttributes ?? null); if (!alreadyForwarded && forwarded) { this.autoForwarded.add(address); } else if (forwarded) { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index f5b8ff40a7a..33ca133cd85 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -1077,7 +1077,10 @@ export namespace ForwardPortAction { remoteExplorerService.setEditable(undefined, TunnelEditId.New, null); let parsed: { host: string, port: number } | undefined; if (success && (parsed = parseAddress(value))) { - remoteExplorerService.forward({ host: parsed.host, port: parsed.port }, undefined, undefined, undefined, true).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); + remoteExplorerService.forward({ + remote: { host: parsed.host, port: parsed.port }, + elevateIfNeeded: true + }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } }, validationMessage: (value) => validateInput(remoteExplorerService, value, tunnelService.canElevate), @@ -1100,7 +1103,10 @@ export namespace ForwardPortAction { }); let parsed: { host: string, port: number } | undefined; if (value && (parsed = parseAddress(value))) { - remoteExplorerService.forward({ host: parsed.host, port: parsed.port }, undefined, undefined, undefined, true).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); + remoteExplorerService.forward({ + remote: { host: parsed.host, port: parsed.port }, + elevateIfNeeded: true + }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } }; } @@ -1342,7 +1348,12 @@ namespace ChangeLocalPortAction { if (success) { await remoteExplorerService.close({ host: context.remoteHost, port: context.remotePort }); const numberValue = Number(value); - const newForward = await remoteExplorerService.forward({ host: context.remoteHost, port: context.remotePort }, numberValue, context.name, undefined, true); + const newForward = await remoteExplorerService.forward({ + remote: { host: context.remoteHost, port: context.remotePort }, + local: numberValue, + name: context.name, + elevateIfNeeded: true + }); if (newForward && newForward.tunnelLocalPort !== numberValue) { notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort ?? newForward.localAddress)); } @@ -1365,7 +1376,13 @@ namespace MakePortPublicAction { if (arg instanceof TunnelItem) { const remoteExplorerService = accessor.get(IRemoteExplorerService); await remoteExplorerService.close({ host: arg.remoteHost, port: arg.remotePort }); - return remoteExplorerService.forward({ host: arg.remoteHost, port: arg.remotePort }, arg.localPort, arg.name, undefined, true, true); + return remoteExplorerService.forward({ + remote: { host: arg.remoteHost, port: arg.remotePort }, + local: arg.localPort, + name: arg.name, + elevateIfNeeded: true, + isPublic: true + }); } }; } @@ -1380,7 +1397,13 @@ namespace MakePortPrivateAction { if (arg instanceof TunnelItem) { const remoteExplorerService = accessor.get(IRemoteExplorerService); await remoteExplorerService.close({ host: arg.remoteHost, port: arg.remotePort }); - return remoteExplorerService.forward({ host: arg.remoteHost, port: arg.remotePort }, arg.localPort, arg.name, undefined, true, false); + return remoteExplorerService.forward({ + remote: { host: arg.remoteHost, port: arg.remotePort }, + local: arg.localPort, + name: arg.name, + elevateIfNeeded: true, + isPublic: false + }); } }; } diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 905677314d4..0c860a122f8 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -74,6 +74,15 @@ export enum TunnelEditId { LocalPort = 3 } +interface TunnelProperties { + remote: { host: string, port: number }, + local?: number, + name?: string, + source?: string, + elevateIfNeeded?: boolean, + isPublic?: boolean +} + export interface Tunnel { remoteHost: string; remotePort: number; @@ -504,7 +513,12 @@ export class TunnelModel extends Disposable { this.logService.trace(`ForwardedPorts: (TunnelModel) restoring ports ${tunnels.map(tunnel => tunnel.remotePort).join(', ')}`); for (let tunnel of tunnels) { if (!mapHasAddressLocalhostOrAllInterfaces(this.detected, tunnel.remoteHost, tunnel.remotePort)) { - await this.forward({ host: tunnel.remoteHost, port: tunnel.remotePort }, tunnel.localPort, tunnel.name, undefined, undefined, tunnel.privacy === TunnelPrivacy.Public); + await this.forward({ + remote: { host: tunnel.remoteHost, port: tunnel.remotePort }, + local: tunnel.localPort, + name: tunnel.name, + isPublic: tunnel.privacy === TunnelPrivacy.Public + }); } } } @@ -551,11 +565,10 @@ export class TunnelModel extends Disposable { return this.dialogService.show(Severity.Info, mismatchString); } - async forward(remote: { host: string, port: number }, local?: number, name?: string, source?: string, elevateIfNeeded?: boolean, - isPublic?: boolean, restore: boolean = true, attributes?: Attributes | null): Promise { - const existingTunnel = mapHasAddressLocalhostOrAllInterfaces(this.forwarded, remote.host, remote.port); - attributes = attributes ?? ((attributes !== null) ? (await this.getAttributes([remote.port]))?.get(remote.port) : undefined); - const localPort = (local !== undefined) ? local : remote.port; + async forward(tunnelProperties: TunnelProperties, restore: boolean = true, attributes?: Attributes | null): Promise { + const existingTunnel = mapHasAddressLocalhostOrAllInterfaces(this.forwarded, tunnelProperties.remote.host, tunnelProperties.remote.port); + attributes = attributes ?? ((attributes !== null) ? (await this.getAttributes([tunnelProperties.remote.port]))?.get(tunnelProperties.remote.port) : undefined); + const localPort = (tunnelProperties.local !== undefined) ? tunnelProperties.local : tunnelProperties.remote.port; if (!existingTunnel) { const authority = this.environmentService.remoteAuthority; @@ -563,11 +576,11 @@ export class TunnelModel extends Disposable { getAddress: async () => { return (await this.remoteAuthorityResolverService.resolveAuthority(authority)).authority; } } : undefined; - const key = makeAddress(remote.host, remote.port); + const key = makeAddress(tunnelProperties.remote.host, tunnelProperties.remote.port); this.inProgress.set(key, true); - const tunnel = await this.tunnelService.openTunnel(addressProvider, remote.host, remote.port, localPort, (!elevateIfNeeded) ? attributes?.elevateIfNeeded : elevateIfNeeded, isPublic, attributes?.protocol); + const tunnel = await this.tunnelService.openTunnel(addressProvider, tunnelProperties.remote.host, tunnelProperties.remote.port, localPort, (!tunnelProperties.elevateIfNeeded) ? attributes?.elevateIfNeeded : tunnelProperties.elevateIfNeeded, tunnelProperties.isPublic, attributes?.protocol); if (tunnel && tunnel.localAddress) { - const matchingCandidate = mapHasAddressLocalhostOrAllInterfaces(this._candidates ?? new Map(), remote.host, remote.port); + const matchingCandidate = mapHasAddressLocalhostOrAllInterfaces(this._candidates ?? new Map(), tunnelProperties.remote.host, tunnelProperties.remote.port); const protocol = (tunnel.protocol ? ((tunnel.protocol === TunnelProtocol.Https) ? TunnelProtocol.Https : TunnelProtocol.Http) : (attributes?.protocol ?? TunnelProtocol.Http)); @@ -575,7 +588,7 @@ export class TunnelModel extends Disposable { remoteHost: tunnel.tunnelRemoteHost, remotePort: tunnel.tunnelRemotePort, localPort: tunnel.tunnelLocalPort, - name: attributes?.label ?? name, + name: attributes?.label ?? tunnelProperties.name, closeable: true, localAddress: tunnel.localAddress, protocol, @@ -583,7 +596,7 @@ export class TunnelModel extends Disposable { runningProcess: matchingCandidate?.detail, hasRunningProcess: !!matchingCandidate, pid: matchingCandidate?.pid, - source, + source: tunnelProperties.source, privacy: this.makeTunnelPrivacy(tunnel.public), userForwarded: restore }; @@ -596,16 +609,16 @@ export class TunnelModel extends Disposable { return tunnel; } } else { - const newName = attributes?.label ?? name; + const newName = attributes?.label ?? tunnelProperties.name; if (newName !== existingTunnel.name) { existingTunnel.name = newName; this._onForwardPort.fire(); } if ((attributes?.protocol || (existingTunnel.protocol !== TunnelProtocol.Http)) && (attributes?.protocol !== existingTunnel.protocol)) { await this.close(existingTunnel.remoteHost, existingTunnel.remotePort); - await this.forward({ host: existingTunnel.remoteHost, port: existingTunnel.remotePort }, local, name, source, elevateIfNeeded, isPublic, restore, attributes); + await this.forward(tunnelProperties, restore, attributes); } - return mapHasAddressLocalhostOrAllInterfaces(this.remoteTunnels, remote.host, remote.port); + return mapHasAddressLocalhostOrAllInterfaces(this.remoteTunnels, tunnelProperties.remote.host, tunnelProperties.remote.port); } } @@ -740,7 +753,12 @@ export class TunnelModel extends Disposable { for (const forwarded of tunnels) { const attributes = allAttributes.get(forwarded.remotePort); if ((attributes?.protocol || (forwarded.protocol !== TunnelProtocol.Http)) && (attributes?.protocol !== forwarded.protocol)) { - await this.forward({ host: forwarded.remoteHost, port: forwarded.remotePort }, forwarded.localPort, forwarded.name, forwarded.source, undefined, undefined, undefined, attributes); + await this.forward({ + remote: { host: forwarded.remoteHost, port: forwarded.remotePort }, + local: forwarded.localPort, + name: forwarded.name, + source: forwarded.source + }, undefined, attributes); } if (!attributes) { @@ -835,7 +853,7 @@ export interface IRemoteExplorerService { onDidChangeEditable: Event<{ tunnel: ITunnelItem, editId: TunnelEditId } | undefined>; setEditable(tunnelItem: ITunnelItem | undefined, editId: TunnelEditId, data: IEditableData | null): void; getEditableData(tunnelItem: ITunnelItem | undefined, editId?: TunnelEditId): IEditableData | undefined; - forward(remote: { host: string, port: number }, localPort?: number, name?: string, source?: string, elevateIfNeeded?: boolean, isPublic?: boolean, restore?: boolean, attributes?: Attributes | null): Promise; + forward(tunnelProperties: TunnelProperties, restore?: boolean, attributes?: Attributes | null): Promise; close(remote: { host: string, port: number }): Promise; setTunnelInformation(tunnelInformation: TunnelInformation | undefined): void; setCandidateFilter(filter: ((candidates: CandidatePort[]) => Promise) | undefined): IDisposable; @@ -894,8 +912,8 @@ class RemoteExplorerService implements IRemoteExplorerService { return this._tunnelModel; } - forward(remote: { host: string, port: number }, local?: number, name?: string, source?: string, elevateIfNeeded?: boolean, isPublic?: boolean, restore?: boolean, attributes?: Attributes | null): Promise { - return this.tunnelModel.forward(remote, local, name, source, elevateIfNeeded, isPublic, restore, attributes); + forward(tunnelProperties: TunnelProperties, restore?: boolean, attributes?: Attributes | null): Promise { + return this.tunnelModel.forward(tunnelProperties, restore, attributes); } close(remote: { host: string, port: number }): Promise {