diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index d53e3cf329e3a..afab749949551 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -21,7 +21,7 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape { } async $openTunnel(tunnelOptions: TunnelOptions): Promise { - const tunnel = await this.remoteExplorerService.tunnelModel.forward(tunnelOptions.remote.port, tunnelOptions.localPort, tunnelOptions.name); + const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remote.port, tunnelOptions.localPort, tunnelOptions.name); if (tunnel) { return { remote: { host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, localAddress: tunnel.localAddress }; } @@ -29,7 +29,11 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape { } async $closeTunnel(remotePort: number): Promise { - return this.remoteExplorerService.tunnelModel.close(remotePort); + return this.remoteExplorerService.close(remotePort); + } + + $addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): Promise { + return Promise.resolve(this.remoteExplorerService.addDetected(tunnels)); } dispose(): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0286e781fc935..91f9d879c625a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -780,6 +780,7 @@ export interface MainThreadWindowShape extends IDisposable { export interface MainThreadTunnelServiceShape extends IDisposable { $openTunnel(tunnelOptions: TunnelOptions): Promise; $closeTunnel(remotePort: number): Promise; + $addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): Promise; } // -- extension host diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index a3b5ed0057d99..a0c3aa4e0867d 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -32,6 +32,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; interface ITestRunner { /** Old test runner API, as exported from `vscode/lib/testrunner` */ @@ -76,6 +77,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio protected readonly _extHostWorkspace: ExtHostWorkspace; protected readonly _extHostConfiguration: ExtHostConfiguration; protected readonly _logService: ILogService; + protected readonly _extHostTunnelService: IExtHostTunnelService; protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; @@ -104,7 +106,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio @IExtHostConfiguration extHostConfiguration: IExtHostConfiguration, @ILogService logService: ILogService, @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtensionStoragePaths storagePath: IExtensionStoragePaths + @IExtensionStoragePaths storagePath: IExtensionStoragePaths, + @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService ) { this._hostUtils = hostUtils; this._extHostContext = extHostContext; @@ -113,6 +116,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio this._extHostWorkspace = extHostWorkspace; this._extHostConfiguration = extHostConfiguration; this._logService = logService; + this._extHostTunnelService = extHostTunnelService; this._disposables = new DisposableStore(); this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); @@ -652,6 +656,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio extensionHostEnv: result.extensionHostEnv }; + await this._extHostTunnelService.addDetected(result.detectedTunnels); + return { type: 'ok', value: { diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index 2f061642da4ad..5b13798e29491 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -22,13 +22,16 @@ export interface TunnelDto { } export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { + readonly _serviceBrand: undefined; makeTunnel(forward: TunnelOptions): Promise; + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise; } export const IExtHostTunnelService = createDecorator('IExtHostTunnelService'); export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { + readonly _serviceBrand: undefined; private readonly _proxy: MainThreadTunnelServiceShape; constructor( @@ -52,5 +55,12 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } return undefined; } + + async addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise { + if (tunnels) { + return this._proxy.$addDetected(tunnels); + } + } + } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index ea19a1064df5f..8077d54908213 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -51,7 +51,7 @@ class TunnelTreeVirtualDelegate implements IListVirtualDelegate { export interface ITunnelViewModel { onForwardedPortsChanged: Event; readonly forwarded: TunnelItem[]; - readonly published: TunnelItem[]; + readonly detected: TunnelItem[]; readonly candidates: TunnelItem[]; readonly groups: ITunnelGroup[]; } @@ -79,11 +79,11 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { items: this.forwarded }); } - if (this.model.published.size > 0) { + if (this.model.detected.size > 0) { groups.push({ - label: nls.localize('remote.tunnelsView.published', "Published"), - tunnelType: TunnelType.Published, - items: this.published + label: nls.localize('remote.tunnelsView.detected', "Detected"), + tunnelType: TunnelType.Detected, + items: this.detected }); } const candidates = this.candidates; @@ -107,9 +107,9 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { }); } - get published(): TunnelItem[] { - return Array.from(this.model.published.values()).map(tunnel => { - return new TunnelItem(TunnelType.Published, tunnel.remote, tunnel.localAddress, false, tunnel.name, tunnel.description); + get detected(): TunnelItem[] { + return Array.from(this.model.detected.values()).map(tunnel => { + return new TunnelItem(TunnelType.Detected, tunnel.remote, tunnel.localAddress, false, tunnel.name, tunnel.description); }); } @@ -118,7 +118,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { const values = this.model.candidates.values(); let iterator = values.next(); while (!iterator.done) { - if (!this.model.forwarded.has(iterator.value.remote) && !this.model.published.has(iterator.value.remote)) { + if (!this.model.forwarded.has(iterator.value.remote) && !this.model.detected.has(iterator.value.remote)) { candidates.push(new TunnelItem(TunnelType.Candidate, iterator.value.remote, iterator.value.localAddress, false, undefined, iterator.value.description)); } iterator = values.next(); @@ -324,7 +324,7 @@ class TunnelDataSource implements IAsyncDataSource; - readonly published: Map; + readonly detected: Map; readonly candidates: Map; private _onForwardPort: Emitter = new Emitter(); public onForwardPort: Event = this._onForwardPort.event; @@ -53,7 +53,7 @@ export class TunnelModel extends Disposable { }); }); - this.published = new Map(); + this.detected = new Map(); this.candidates = new Map(); this._register(this.tunnelService.onTunnelOpened(tunnel => { if (this.candidates.has(tunnel.tunnelRemotePort)) { @@ -99,6 +99,9 @@ export class TunnelModel extends Disposable { if (this.forwarded.has(remote)) { this.forwarded.get(remote)!.name = name; this._onPortName.fire(remote); + } else if (this.detected.has(remote)) { + this.detected.get(remote)!.name = name; + this._onPortName.fire(remote); } } @@ -107,7 +110,17 @@ export class TunnelModel extends Disposable { } address(remote: number): string | undefined { - return (this.forwarded.get(remote) || this.published.get(remote))?.localAddress; + return (this.forwarded.get(remote) || this.detected.get(remote))?.localAddress; + } + + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): void { + tunnels.forEach(tunnel => { + this.detected.set(tunnel.remote.port, { + remote: tunnel.remote.port, + localAddress: tunnel.localAddress, + closeable: false + }); + }); } } @@ -120,6 +133,9 @@ export interface IRemoteExplorerService { onDidChangeEditable: Event; setEditable(remote: number | undefined, data: IEditableData | null): void; getEditableData(remote: number | undefined): IEditableData | undefined; + forward(remote: number, local?: number, name?: string): Promise; + close(remote: number): Promise; + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): void; } export interface HelpInformation { @@ -221,6 +237,20 @@ class RemoteExplorerService implements IRemoteExplorerService { return this._tunnelModel; } + forward(remote: number, local?: number, name?: string): Promise { + return this.tunnelModel.forward(remote, local, name); + } + + close(remote: number): Promise { + return this.tunnelModel.close(remote); + } + + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): void { + if (tunnels) { + this.tunnelModel.addDetected(tunnels); + } + } + setEditable(remote: number | undefined, data: IEditableData | null): void { if (!data) { this.editable = undefined;