tunnel.ts ×33

Frontier kind: Code frontier

unlabeled · c_11fe9f0dd473

40 tests · 8330 LOC · 39 files · introduces 0 tests · 230 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
33 ranges230 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1305 ranges8330 lines · 39 files · Browse complete extent
All tests (intent)
40 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 230 introduced LOC across 33 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/tunnel/common/tunnel.ts 230 introduced LOC · 33 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- tunnel.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../base/common/cancellation.js';
7 > import { Emitter, Event } from '../../../base/common/event.js';
8 > import { IDisposable, Disposable } from '../../../base/common/lifecycle.js';
9 > import { OperatingSystem } from '../../../base/common/platform.js';
10 > import { URI } from '../../../base/common/uri.js';
11 > import { IConfigurationService } from '../../configuration/common/configuration.js';
12 > import { createDecorator } from '../../instantiation/common/instantiation.js';
13 > import { ILogService } from '../../log/common/log.js';
14 > import { IAddressProvider } from '../../remote/common/remoteAgentConnection.js';
15 > import { TunnelPrivacy } from '../../remote/common/remoteAuthorityResolver.js';
16 >
17 > export const ITunnelService = createDecorator<ITunnelService>('tunnelService');
18 > export const ISharedTunnelsService = createDecorator<ISharedTunnelsService>('sharedTunnelsService');
19 >
20 > export interface RemoteTunnel {
21 > readonly tunnelRemotePort: number;
22 > readonly tunnelRemoteHost: string;
23 > readonly tunnelLocalPort?: number;
24 > readonly localAddress: string;
25 > readonly privacy: string;
26 > readonly protocol?: string;
27 > dispose(silent?: boolean): Promise<void>;
28 > }
29 >
30 > export function isRemoteTunnel(something: unknown): something is RemoteTunnel {
31 const asTunnel: Partial<RemoteTunnel> = something as Partial<RemoteTunnel>;
32 return !!(asTunnel.tunnelRemotePort && asTunnel.tunnelRemoteHost && asTunnel.localAddress && asTunnel.privacy && asTunnel.dispose);
33 }
34 > tunnel.ts
35 > export interface TunnelOptions {
36 > remoteAddress: { port: number; host: string };
37 > localAddressPort?: number;
38 > label?: string;
39 > public?: boolean;
40 > privacy?: string;
41 > protocol?: string;
42 > }
43 >
44 > export enum TunnelProtocol {
45 > Http = 'http',
46 > Https = 'https'
47 > }
48 >
49 > export enum TunnelPrivacyId {
50 > ConstantPrivate = 'constantPrivate', // private, and changing is unsupported
51 > Private = 'private',
52 > Public = 'public'
53 > }
54 >
55 > export interface TunnelCreationOptions {
56 > elevationRequired?: boolean;
57 > }
58 >
59 > export interface TunnelProviderFeatures {
60 > elevation: boolean;
61 > /**
62 > * @deprecated
63 > */
64 > public?: boolean;
65 > privacyOptions: TunnelPrivacy[];
66 > protocol: boolean;
67 > }
68 >
69 > export interface ITunnelProvider {
70 > forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<RemoteTunnel | string | undefined> | undefined;
71 > }
72 >
73 > export function isTunnelProvider(addressOrTunnelProvider: IAddressProvider | ITunnelProvider): addressOrTunnelProvider is ITunnelProvider {
74 return !!(addressOrTunnelProvider as ITunnelProvider).forwardPort;
75 }
76 > tunnel.ts
77 > export enum ProvidedOnAutoForward {
78 > Notify = 1,
79 > OpenBrowser = 2,
80 > OpenPreview = 3,
81 > Silent = 4,
82 > Ignore = 5,
83 > OpenBrowserOnce = 6
84 > }
85 >
86 > export interface ProvidedPortAttributes {
87 > port: number;
88 > autoForwardAction: ProvidedOnAutoForward;
89 > }
90 >
91 > export interface PortAttributesProvider {
92 > providePortAttributes(ports: number[], pid: number | undefined, commandLine: string | undefined, token: CancellationToken): Promise<ProvidedPortAttributes[]>;
93 > }
94 >
95 > export interface ITunnel {
96 > remoteAddress: { port: number; host: string };
97 >
98 > /**
99 > * The complete local address(ex. localhost:1234)
100 > */
101 > localAddress: string;
102 >
103 > /**
104 > * @deprecated Use privacy instead
105 > */
106 > public?: boolean;
107 >
108 > privacy?: string;
109 >
110 > protocol?: string;
111 >
112 > /**
113 > * Implementers of Tunnel should fire onDidDispose when dispose is called.
114 > */
115 > readonly onDidDispose: Event<void>;
116 >
117 > dispose(): Promise<void> | void;
118 > }
119 >
120 > export interface ISharedTunnelsService {
121 > readonly _serviceBrand: undefined;
122 >
123 > openTunnel(authority: string, addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localHost: string, localPort?: number, elevateIfNeeded?: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | string | undefined> | undefined;
124 > }
125 >
126 > export interface ITunnelService {
127 > readonly _serviceBrand: undefined;
128 >
129 > readonly tunnels: Promise<readonly RemoteTunnel[]>;
130 > readonly canChangePrivacy: boolean;
131 > readonly privacyOptions: TunnelPrivacy[];
132 > readonly onTunnelOpened: Event<RemoteTunnel>;
133 > readonly onTunnelClosed: Event<{ host: string; port: number }>;
134 > readonly canElevate: boolean;
135 > readonly canChangeProtocol: boolean;
136 > readonly hasTunnelProvider: boolean;
137 > readonly onAddedTunnelProvider: Event<void>;
138 >
139 > canTunnel(uri: URI): boolean;
140 > openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localHost?: string, localPort?: number, elevateIfNeeded?: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | string | undefined> | undefined;
141 > getExistingTunnel(remoteHost: string, remotePort: number): Promise<RemoteTunnel | string | undefined>;
142 > setEnvironmentTunnel(remoteHost: string, remotePort: number, localAddress: string, privacy: string, protocol: string): void;
143 > closeTunnel(remoteHost: string, remotePort: number): Promise<void>;
144 > setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable;
145 > setTunnelFeatures(features: TunnelProviderFeatures): void;
146 > isPortPrivileged(port: number): boolean;
147 > }
148 >
149 > export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: string; port: number } | undefined {
150 if (uri.scheme !== 'http' && uri.scheme !== 'https') {
151 return undefined;
160 };
161 }
162 > tunnel.ts
163 > export function extractQueryLocalHostUriMetaDataForPortMapping(uri: URI): { address: string; port: number } | undefined {
164 if (uri.scheme !== 'http' && uri.scheme !== 'https' || !uri.query) {
165 return undefined;
177 return undefined;
178 }
179 > tunnel.ts
180 > export const LOCALHOST_ADDRESSES = ['localhost', '127.0.0.1', '0:0:0:0:0:0:0:1', '::1'];
181 > export function isLocalhost(host: string): boolean {
182 return LOCALHOST_ADDRESSES.indexOf(host) >= 0;
183 }
184 > tunnel.ts
185 > export const ALL_INTERFACES_ADDRESSES = ['0.0.0.0', '0:0:0:0:0:0:0:0', '::'];
186 > export function isAllInterfaces(host: string): boolean {
187 return ALL_INTERFACES_ADDRESSES.indexOf(host) >= 0;
188 }
189 > tunnel.ts
190 > export function isPortPrivileged(port: number, host: string, os: OperatingSystem, osRelease: string): boolean {
191 if (os === OperatingSystem.Windows) {
192 return false;
205 return port < 1024;
206 }
207 > tunnel.ts
208 > export class DisposableTunnel {
209 > private _onDispose: Emitter<void> = new Emitter();
210 > readonly onDidDispose: Event<void> = this._onDispose.event;
211 >
212 > constructor(
213 public readonly remoteAddress: { port: number; host: string },
214 public readonly localAddress: { port: number; host: string } | string,
215 private readonly _dispose: () => Promise<void>) { }
216 > tunnel.ts
217 > dispose(): Promise<void> {
218 this._onDispose.fire();
219 this._onDispose.dispose();
220 return this._dispose();
221 }
222 > } tunnel.ts
223 >
224 > export abstract class AbstractTunnelService extends Disposable implements ITunnelService {
225 > declare readonly _serviceBrand: undefined;
226 >
227 > private _onTunnelOpened = this._register(new Emitter<RemoteTunnel>());
228 > public onTunnelOpened: Event<RemoteTunnel> = this._onTunnelOpened.event;
229 > private _onTunnelClosed = this._register(new Emitter<{ host: string; port: number }>());
230 > public onTunnelClosed: Event<{ host: string; port: number }> = this._onTunnelClosed.event;
231 > private _onAddedTunnelProvider = this._register(new Emitter<void>());
232 > public onAddedTunnelProvider: Event<void> = this._onAddedTunnelProvider.event;
233 > protected readonly _tunnels = new Map</*host*/ string, Map</* port */ number, { refcount: number; readonly value: Promise<RemoteTunnel | string | undefined> }>>();
234 > protected _tunnelProvider: ITunnelProvider | undefined;
235 > protected _canElevate: boolean = false;
236 > private _canChangeProtocol: boolean = true;
237 > private _privacyOptions: TunnelPrivacy[] = [];
238 > private _factoryInProgress: Set<number/*port*/> = new Set();
239 >
240 > public constructor(
241 @ILogService protected readonly logService: ILogService,
242 @IConfigurationService protected readonly configurationService: IConfigurationService
243 ) { super(); }
244 > tunnel.ts
245 > get hasTunnelProvider(): boolean {
246 return !!this._tunnelProvider;
247 }
248 > tunnel.ts
249 > protected get defaultTunnelHost(): string {
250 const settingValue = this.configurationService.getValue('remote.localPortHost');
251 return (!settingValue || settingValue === 'localhost') ? '127.0.0.1' : '0.0.0.0';
252 }
253 > tunnel.ts
254 > setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable {
255 this._tunnelProvider = provider;
256 if (!provider) {
273 };
274 }
275 > tunnel.ts
276 > setTunnelFeatures(features: TunnelProviderFeatures): void {
277 this._canElevate = features.elevation;
278 this._privacyOptions = features.privacyOptions;
279 this._canChangeProtocol = features.protocol;
280 }
281 > tunnel.ts
282 > public get canChangeProtocol(): boolean {
283 return this._canChangeProtocol;
284 }
285 > tunnel.ts
286 > public get canElevate(): boolean {
287 return this._canElevate;
288 }
289 > tunnel.ts
290 > public get canChangePrivacy() {
291 return this._privacyOptions.length > 0;
292 }
293 > tunnel.ts
294 > public get privacyOptions() {
295 return this._privacyOptions;
296 }
297 > tunnel.ts
298 > public get tunnels(): Promise<readonly RemoteTunnel[]> {
299 return this.getTunnels();
300 }
301 > tunnel.ts
302 > private async getTunnels(): Promise<readonly RemoteTunnel[]> {
303 const tunnels: RemoteTunnel[] = [];
304 const tunnelArray = Array.from(this._tunnels.values());
314 return tunnels;
315 }
316 > tunnel.ts
317 > override async dispose(): Promise<void> {
318 super.dispose();
319 for (const portMap of this._tunnels.values()) {
325 this._tunnels.clear();
326 }
327 > tunnel.ts
328 > setEnvironmentTunnel(remoteHost: string, remotePort: number, localAddress: string, privacy: string, protocol: string): void {
329 this.addTunnelToMap(remoteHost, remotePort, Promise.resolve({
330 tunnelRemoteHost: remoteHost,
336 }));
337 }
338 > tunnel.ts
339 > async getExistingTunnel(remoteHost: string, remotePort: number): Promise<RemoteTunnel | string | undefined> {
340 if (isAllInterfaces(remoteHost) || isLocalhost(remoteHost)) {
341 remoteHost = LOCALHOST_ADDRESSES[0];
349 return undefined;
350 }
351 > tunnel.ts
352 > openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localHost?: string, localPort?: number, elevateIfNeeded: boolean = false, privacy?: string, protocol?: string): Promise<RemoteTunnel | string | undefined> | undefined {
353 this.logService.trace(`ForwardedPorts: (TunnelService) openTunnel request for ${remoteHost}:${remotePort} on local port ${localPort}.`);
354 const addressOrTunnelProvider = this._tunnelProvider ?? addressProvider;
398 });
399 }
400 > tunnel.ts
401 > private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel {
402 return {
403 tunnelRemotePort: tunnel.tunnelRemotePort,
420 };
421 }
422 > tunnel.ts
423 > private async tryDisposeTunnel(remoteHost: string, remotePort: number, tunnel: { refcount: number; readonly value: Promise<RemoteTunnel | string | undefined> }): Promise<void> {
424 if (tunnel.refcount <= 0) {
425 this.logService.trace(`ForwardedPorts: (TunnelService) Tunnel is being disposed ${remoteHost}:${remotePort}.`);
436 }
437 }
438 > tunnel.ts
439 > async closeTunnel(remoteHost: string, remotePort: number): Promise<void> {
440 this.logService.trace(`ForwardedPorts: (TunnelService) close request for ${remoteHost}:${remotePort} `);
441 const portMap = this._tunnels.get(remoteHost);
446 }
447 }
448 > tunnel.ts
449 > protected addTunnelToMap(remoteHost: string, remotePort: number, tunnel: Promise<RemoteTunnel | string | undefined>) {
450 if (!this._tunnels.has(remoteHost)) {
451 this._tunnels.set(remoteHost, new Map());
453 this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel });
454 }
455 > tunnel.ts
456 > private async removeEmptyOrErrorTunnelFromMap(remoteHost: string, remotePort: number) {
457 const hostMap = this._tunnels.get(remoteHost);
458 if (hostMap) {
467 }
468 }
469 > tunnel.ts
470 > protected getTunnelFromMap(remoteHost: string, remotePort: number): { refcount: number; readonly value: Promise<RemoteTunnel | string | undefined> } | undefined {
471 const hosts = [remoteHost];
472 // Order matters. We want the original host to be first.
489 return undefined;
490 }
491 > tunnel.ts
492 > canTunnel(uri: URI): boolean {
493 return !!extractLocalHostUriMetaDataForPortMapping(uri);
494 }
495 > tunnel.ts
496 > public abstract isPortPrivileged(port: number): boolean;
497 >
498 > protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider | ITunnelProvider, remoteHost: string, remotePort: number, localHost: string, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | string | undefined> | undefined;
499 >
500 > protected createWithProvider(tunnelProvider: ITunnelProvider, remoteHost: string, remotePort: number, localPort: number | undefined, elevateIfNeeded: boolean, privacy?: string, protocol?: string): Promise<RemoteTunnel | string | undefined> | undefined {
501 this.logService.trace(`ForwardedPorts: (TunnelService) Creating tunnel with provider ${remoteHost}:${remotePort} on local port ${localPort}.`);
502 const key = remotePort;