Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tunnels in shared process should be per remote #135937

Merged
merged 2 commits into from Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -88,8 +88,8 @@ import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/pl
import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter';
import { ISignService } from 'vs/platform/sign/common/sign';
import { SignService } from 'vs/platform/sign/node/signService';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { TunnelService } from 'vs/platform/remote/node/tunnelService';
import { ISharedTunnelsService } from 'vs/platform/remote/common/tunnel';
import { SharedTunnelsService } from 'vs/platform/remote/node/tunnelService';
import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService';
import { SharedProcessTunnelService } from 'vs/platform/remote/node/sharedProcessTunnelService';
import { ipcSharedProcessWorkerChannelName, ISharedProcessWorkerConfiguration, ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService';
Expand Down Expand Up @@ -326,7 +326,7 @@ class SharedProcessMain extends Disposable {
services.set(ISignService, new SyncDescriptor(SignService));

// Tunnel
services.set(ITunnelService, new SyncDescriptor(TunnelService));
services.set(ISharedTunnelsService, new SyncDescriptor(SharedTunnelsService));
services.set(ISharedProcessTunnelService, new SyncDescriptor(SharedProcessTunnelService));

return new InstantiationService(services);
Expand Down
Expand Up @@ -29,7 +29,7 @@ export interface ISharedProcessTunnelService {
* Start a previously created tunnel.
* Can only be called once per created tunnel.
*/
startTunnel(id: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort: number | undefined, elevateIfNeeded: boolean | undefined): Promise<ISharedProcessTunnel>;
startTunnel(authority: string, id: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort: number | undefined, elevateIfNeeded: boolean | undefined): Promise<ISharedProcessTunnel>;
/**
* Set the remote address info for a previously created tunnel.
* Should be called as often as the resolver resolves.
Expand Down
7 changes: 7 additions & 0 deletions src/vs/platform/remote/common/tunnel.ts
Expand Up @@ -13,6 +13,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection';

export const ITunnelService = createDecorator<ITunnelService>('tunnelService');
export const ISharedTunnelsService = createDecorator<ISharedTunnelsService>('sharedTunnelsService');

export interface RemoteTunnel {
readonly tunnelRemotePort: number;
Expand Down Expand Up @@ -110,6 +111,12 @@ export interface ITunnel {
dispose(): Promise<void> | void;
}

export interface ISharedTunnelsService {
readonly _serviceBrand: undefined;

openTunnel(authority: string, addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number, elevateIfNeeded?: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | undefined> | undefined;
}

export interface ITunnelService {
readonly _serviceBrand: undefined;

Expand Down
8 changes: 4 additions & 4 deletions src/vs/platform/remote/node/sharedProcessTunnelService.ts
Expand Up @@ -5,7 +5,7 @@

import { ILogService } from 'vs/platform/log/common/log';
import { ISharedProcessTunnel, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { ISharedTunnelsService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IAddress, IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection';
import { Disposable } from 'vs/base/common/lifecycle';
import { canceled } from 'vs/base/common/errors';
Expand Down Expand Up @@ -55,7 +55,7 @@ export class SharedProcessTunnelService extends Disposable implements ISharedPro
private readonly _disposedTunnels: Set<string> = new Set<string>();

constructor(
@ITunnelService private readonly _tunnelService: ITunnelService,
@ISharedTunnelsService private readonly _tunnelService: ISharedTunnelsService,
@ILogService private readonly _logService: ILogService,
) {
super();
Expand All @@ -71,10 +71,10 @@ export class SharedProcessTunnelService extends Disposable implements ISharedPro
return { id };
}

async startTunnel(id: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort: number | undefined, elevateIfNeeded: boolean | undefined): Promise<ISharedProcessTunnel> {
async startTunnel(authority: string, id: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort: number | undefined, elevateIfNeeded: boolean | undefined): Promise<ISharedProcessTunnel> {
const tunnelData = new TunnelData();

const tunnel = await Promise.resolve(this._tunnelService.openTunnel(tunnelData, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort, elevateIfNeeded));
const tunnel = await Promise.resolve(this._tunnelService.openTunnel(authority, tunnelData, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort, elevateIfNeeded));
if (!tunnel) {
this._logService.info(`[SharedProcessTunnelService] Could not create a tunnel to ${tunnelRemoteHost}:${tunnelRemotePort} (remote).`);
tunnelData.dispose();
Expand Down
32 changes: 31 additions & 1 deletion src/vs/platform/remote/node/tunnelService.ts
Expand Up @@ -14,7 +14,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { connectRemoteAgentTunnel, IAddressProvider, IConnectionOptions, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection';
import { AbstractTunnelService, isAllInterfaces, isLocalhost, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel';
import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as ISharedTunnelsService, isLocalhost, ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/remote/common/tunnel';
import { ISignService } from 'vs/platform/sign/common/sign';

async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
Expand Down Expand Up @@ -189,3 +189,33 @@ export class TunnelService extends BaseTunnelService {
super(nodeSocketFactory, logService, signService, productService, configurationService);
}
}

export class SharedTunnelsService extends Disposable implements ISharedTunnelsService {
declare readonly _serviceBrand: undefined;
private readonly _tunnelServices: Map<string, ITunnelService> = new Map();

public constructor(
@ILogService protected readonly logService: ILogService,
@IProductService private readonly productService: IProductService,
@ISignService private readonly signService: ISignService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
}

async openTunnel(authority: string, addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number, elevateIfNeeded?: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | undefined> {
this.logService.trace(`ForwardedPorts: (SharedTunnelService) openTunnel request for ${remoteHost}:${remotePort} on local port ${localPort}.`);
if (!this._tunnelServices.has(authority)) {
const tunnelService = new TunnelService(this.logService, this.signService, this.productService, this.configurationService);
this._register(tunnelService);
this._tunnelServices.set(authority, tunnelService);
tunnelService.onTunnelClosed(async () => {
if ((await tunnelService.tunnels).length === 0) {
tunnelService.dispose();
this._tunnelServices.delete(authority);
}
});
}
return this._tunnelServices.get(authority)!.openTunnel(addressProvider, remoteHost, remotePort, localPort, elevateIfNeeded, privacy, protocol);
}
}
Expand Up @@ -92,8 +92,8 @@ export class TunnelService extends AbstractTunnelService {
private async _createSharedProcessTunnel(addressProvider: IAddressProvider, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort: number | undefined, elevateIfNeeded: boolean | undefined): Promise<RemoteTunnel> {
const { id } = await this._sharedProcessTunnelService.createTunnel();
this._activeSharedProcessTunnels.add(id);

const result = await this._sharedProcessTunnelService.startTunnel(id, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort, elevateIfNeeded);
const authority = this._environmentService.remoteAuthority!;
const result = await this._sharedProcessTunnelService.startTunnel(authority, id, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort, elevateIfNeeded);
const tunnel = this._instantiationService.createInstance(SharedProcessTunnel, id, addressProvider, tunnelRemoteHost, tunnelRemotePort, result.tunnelLocalPort, result.localAddress, () => {
this._activeSharedProcessTunnels.delete(id);
});
Expand Down