-
Notifications
You must be signed in to change notification settings - Fork 27.9k
/
sharedProcessTunnelService.ts
123 lines (102 loc) · 4.03 KB
/
sharedProcessTunnelService.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService } from 'vs/platform/log/common/log';
import { ISharedProcessTunnel, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService';
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';
import { DeferredPromise } from 'vs/base/common/async';
class TunnelData extends Disposable implements IAddressProvider {
private _address: IAddress | null;
private _addressPromise: DeferredPromise<IAddress> | null;
constructor() {
super();
this._address = null;
this._addressPromise = null;
}
async getAddress(): Promise<IAddress> {
if (this._address) {
// address is resolved
return this._address;
}
if (!this._addressPromise) {
this._addressPromise = new DeferredPromise<IAddress>();
}
return this._addressPromise.p;
}
setAddress(address: IAddress): void {
this._address = address;
if (this._addressPromise) {
this._addressPromise.complete(address);
this._addressPromise = null;
}
}
setTunnel(tunnel: RemoteTunnel): void {
this._register(tunnel);
}
}
export class SharedProcessTunnelService extends Disposable implements ISharedProcessTunnelService {
_serviceBrand: undefined;
private static _lastId = 0;
private readonly _tunnels: Map<string, TunnelData> = new Map<string, TunnelData>();
private readonly _disposedTunnels: Set<string> = new Set<string>();
constructor(
@ISharedTunnelsService private readonly _tunnelService: ISharedTunnelsService,
@ILogService private readonly _logService: ILogService,
) {
super();
}
public override dispose(): void {
super.dispose();
this._tunnels.forEach((tunnel) => tunnel.dispose());
}
async createTunnel(): Promise<{ id: string }> {
const id = String(++SharedProcessTunnelService._lastId);
return { id };
}
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(authority, tunnelData, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort, elevateIfNeeded));
if (!tunnel) {
this._logService.info(`[SharedProcessTunnelService] Could not create a tunnel to ${tunnelRemoteHost}:${tunnelRemotePort} (remote).`);
tunnelData.dispose();
throw new Error(`Could not create tunnel`);
}
if (this._disposedTunnels.has(id)) {
// This tunnel was disposed in the meantime
this._disposedTunnels.delete(id);
tunnelData.dispose();
await tunnel.dispose();
throw canceled();
}
tunnelData.setTunnel(tunnel);
this._tunnels.set(id, tunnelData);
this._logService.info(`[SharedProcessTunnelService] Created tunnel ${id}: ${tunnel.localAddress} (local) to ${tunnelRemoteHost}:${tunnelRemotePort} (remote).`);
const result: ISharedProcessTunnel = {
tunnelLocalPort: tunnel.tunnelLocalPort,
localAddress: tunnel.localAddress
};
return result;
}
async setAddress(id: string, address: IAddress): Promise<void> {
const tunnel = this._tunnels.get(id);
if (!tunnel) {
return;
}
tunnel.setAddress(address);
}
async destroyTunnel(id: string): Promise<void> {
const tunnel = this._tunnels.get(id);
if (tunnel) {
this._logService.info(`[SharedProcessTunnelService] Disposing tunnel ${id}.`);
this._tunnels.delete(id);
await tunnel.dispose();
return;
}
// Looks like this tunnel is still starting, mark the id as disposed
this._disposedTunnels.add(id);
}
}