src/vs/platform/tunnel/common/tunnel.ts

519 LOC · 253 covered · 266 uncovered · 36 ranges · 48 concepts · 2 introducers · 40 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

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 related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the fileextHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 3 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 4 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×2 · 4 introduced LOCextHostMcp.ts ×2extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×4 · 6 introduced LOCextHostMcp.ts ×4extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×4 · 13 introduced LOCextHostMcp.ts ×4extHostMcpNode.ts ×8 · 45 introduced LOCextHostMcpNode.ts ×8extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×5 · 25 introduced LOCextHostMcp.ts ×5extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×3 · 23 introduced LOCextHostMcp.ts ×3extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×8 · 60 introduced LOCextHostMcp.ts ×8extHostMcp.ts ×43 · 874 introduced LOCextHostMcp.ts ×43extensionHostMain.ts ×1 · 2 introduced LOCextensionHostMain.ts ×1reset prepareStackTrace-callback|occurrence=1, extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · 0 introduced LOCreset prepareStackTrace-…extensionHostMain.ts ×5 · 26 introduced LOCextensionHostMain.ts ×5f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · 0 introduced LOCf0f2e182082072efdaf0f8e1…extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×11 · 164 introduced LOCextensionHostMain.ts ×11extHostExtensionService.ts ×64 · 1246 introduced LOCextHostExtensionService.…extHostTunnelService.ts ×3 · 22 introduced LOCextHostTunnelService.ts …extHostTunnelService.ts ×2 · 21 introduced LOCextHostTunnelService.ts …extHostTunnelService.ts ×4 · 38 introduced LOCextHostTunnelService.ts …extHostTunnelService.ts ×2 · 23 introduced LOCextHostTunnelService.ts …extHostTunnelService.ts ×1 · 9 introduced LOCextHostTunnelService.ts …extHostTunnelService.ts ×1 · 17 introduced LOCextHostTunnelService.ts …remoteAgentConnection.ts ×53 · 823 introduced LOCremoteAgentConnection.ts…extHostTunnelService.ts ×17 · 94 introduced LOCextHostTunnelService.ts …tunnel.ts ×3 · 23 introduced LOCtunnel.ts ×3tunnel.ts ×33 · 230 introduced LOCtunnel.ts ×33tunnel.test|title=Tunnel portMapping|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/common/tunnel.test|title=Tunnel portMapping|occurrence=1tunnel.test|title=Tunnel…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1extHostMcp.test|title=Ex…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1extensionHostMain.test|t…f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1f0f2e182082072efdaf0f8e1…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1extensionHostMain.test|t…reset prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 set/reset prepareStackTrace-callback|occurrence=1reset prepareStackTrace-…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1extensionHostMain.test|t…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1extHostMcpNode.test|titl…extHostTunnelService.test|title=ExtHostTunnelService findPorts|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService findPorts|occurrence=1extHostTunnelService.tes…extHostTunnelService.test|title=ExtHostTunnelService getSockets|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService getSockets|occurrence=1extHostTunnelService.tes…extHostTunnelService.test|title=ExtHostTunnelService loadConnectionTable|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService loadConnectionTable|occurrence=1extHostTunnelService.tes…extHostTunnelService.test|title=ExtHostTunnelService loadListeningPorts|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService loadListeningPorts|occurrence=1extHostTunnelService.tes…extHostTunnelService.test|title=ExtHostTunnelService parseIpAddress|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService parseIpAddress|occurrence=1extHostTunnelService.tes…extHostTunnelService.test|title=ExtHostTunnelService tryFindRootPorts|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostTunnelService.test|title=ExtHostTunnelService tryFindRootPorts|occurrence=1extHostTunnelService.tes…Focused file · src/vs/platform/tunnel/common/tunnel.ts · 519 LOCcommon/tunnel.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- tunnel.ts ×33
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 }
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 }
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') { tunnel.ts ×3
151 > return undefined;
152 > }
153 > const localhostMatch = /^(localhost|127\.0\.0\.1|0\.0\.0\.0):(\d+)$/.exec(uri.authority);
154 > if (!localhostMatch) {
155 > return undefined;
156 > }
157 > return {
158 > address: localhostMatch[1],
159 > port: +localhostMatch[2],
160 > };
161 > }
163 > export function extractQueryLocalHostUriMetaDataForPortMapping(uri: URI): { address: string; port: number } | undefined {
164 > if (uri.scheme !== 'http' && uri.scheme !== 'https' || !uri.query) { tunnel.ts ×3
165 return undefined;
166 }
167 > const keyvalues = uri.query.split('&'); tunnel.ts ×3
168 > for (const keyvalue of keyvalues) {
169 > const value = keyvalue.split('=')[1];
170 > if (/^https?:/.exec(value)) {
171 > const result = extractLocalHostUriMetaDataForPortMapping(URI.parse(value));
172 > if (result) {
173 > return result;
174 > }
175 > }
176 > }
177 return undefined;
178 }
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 }
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 }
190 > export function isPortPrivileged(port: number, host: string, os: OperatingSystem, osRelease: string): boolean {
191 if (os === OperatingSystem.Windows) {
192 return false;
193 }
194 if (os === OperatingSystem.Macintosh) {
195 if (isAllInterfaces(host)) {
196 const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(osRelease);
197 if (osVersion?.length === 4) {
198 const major = parseInt(osVersion[1]);
199 if (major >= 18 /* since macOS Mojave, darwin version 18.0.0 */) {
200 return false;
201 }
202 }
203 }
204 }
205 return port < 1024;
206 }
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>) { }
217 > dispose(): Promise<void> {
218 this._onDispose.fire();
219 this._onDispose.dispose();
220 return this._dispose();
221 }
222 > } tunnel.ts ×33
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(); }
245 > get hasTunnelProvider(): boolean {
246 return !!this._tunnelProvider;
247 }
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 }
254 > setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable {
255 this._tunnelProvider = provider;
256 if (!provider) {
257 // clear features
258 this._canElevate = false;
259 this._privacyOptions = [];
260 this._onAddedTunnelProvider.fire();
261 return {
262 dispose: () => { }
263 };
264 }
265
266 this._onAddedTunnelProvider.fire();
267 return {
268 dispose: () => {
269 this._tunnelProvider = undefined;
270 this._canElevate = false;
271 this._privacyOptions = [];
272 }
273 };
274 }
276 > setTunnelFeatures(features: TunnelProviderFeatures): void {
277 this._canElevate = features.elevation;
278 this._privacyOptions = features.privacyOptions;
279 this._canChangeProtocol = features.protocol;
280 }
282 > public get canChangeProtocol(): boolean {
283 return this._canChangeProtocol;
284 }
286 > public get canElevate(): boolean {
287 return this._canElevate;
288 }
290 > public get canChangePrivacy() {
291 return this._privacyOptions.length > 0;
292 }
294 > public get privacyOptions() {
295 return this._privacyOptions;
296 }
298 > public get tunnels(): Promise<readonly RemoteTunnel[]> {
299 return this.getTunnels();
300 }
302 > private async getTunnels(): Promise<readonly RemoteTunnel[]> {
303 const tunnels: RemoteTunnel[] = [];
304 const tunnelArray = Array.from(this._tunnels.values());
305 for (const portMap of tunnelArray) {
306 const portArray = Array.from(portMap.values());
307 for (const x of portArray) {
308 const tunnelValue = await x.value;
309 if (tunnelValue && (typeof tunnelValue !== 'string')) {
310 tunnels.push(tunnelValue);
311 }
312 }
313 }
314 return tunnels;
315 }
317 > override async dispose(): Promise<void> {
318 super.dispose();
319 for (const portMap of this._tunnels.values()) {
320 for (const { value } of portMap.values()) {
321 await value.then(tunnel => typeof tunnel !== 'string' ? tunnel?.dispose() : undefined);
322 }
323 portMap.clear();
324 }
325 this._tunnels.clear();
326 }
328 > setEnvironmentTunnel(remoteHost: string, remotePort: number, localAddress: string, privacy: string, protocol: string): void {
329 this.addTunnelToMap(remoteHost, remotePort, Promise.resolve({
330 tunnelRemoteHost: remoteHost,
331 tunnelRemotePort: remotePort,
332 localAddress,
333 privacy,
334 protocol,
335 dispose: () => Promise.resolve()
336 }));
337 }
339 > async getExistingTunnel(remoteHost: string, remotePort: number): Promise<RemoteTunnel | string | undefined> {
340 if (isAllInterfaces(remoteHost) || isLocalhost(remoteHost)) {
341 remoteHost = LOCALHOST_ADDRESSES[0];
342 }
343
344 const existing = this.getTunnelFromMap(remoteHost, remotePort);
345 if (existing) {
346 ++existing.refcount;
347 return existing.value;
348 }
349 return undefined;
350 }
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;
355 if (!addressOrTunnelProvider) {
356 return undefined;
357 }
358
359 if (!remoteHost) {
360 remoteHost = 'localhost';
361 }
362 if (!localHost) {
363 localHost = this.defaultTunnelHost;
364 }
365
366 // Prevent tunnel factories from calling openTunnel from within the factory
367 if (this._tunnelProvider && this._factoryInProgress.has(remotePort)) {
368 this.logService.debug(`ForwardedPorts: (TunnelService) Another call to create a tunnel with the same address has occurred before the last one completed. This call will be ignored.`);
369 return;
370 }
371
372 const resolvedTunnel = this.retainOrCreateTunnel(addressOrTunnelProvider, remoteHost, remotePort, localHost, localPort, elevateIfNeeded, privacy, protocol);
373 if (!resolvedTunnel) {
374 this.logService.trace(`ForwardedPorts: (TunnelService) Tunnel was not created.`);
375 return resolvedTunnel;
376 }
377
378 return resolvedTunnel.then(tunnel => {
379 if (!tunnel) {
380 this.logService.trace('ForwardedPorts: (TunnelService) New tunnel is undefined.');
381 this.removeEmptyOrErrorTunnelFromMap(remoteHost, remotePort);
382 return undefined;
383 } else if (typeof tunnel === 'string') {
384 this.logService.trace('ForwardedPorts: (TunnelService) The tunnel provider returned an error when creating the tunnel.');
385 this.removeEmptyOrErrorTunnelFromMap(remoteHost, remotePort);
386 return tunnel;
387 }
388 this.logService.trace('ForwardedPorts: (TunnelService) New tunnel established.');
389 const newTunnel = this.makeTunnel(tunnel);
390 if (tunnel.tunnelRemoteHost !== remoteHost || tunnel.tunnelRemotePort !== remotePort) {
391 this.logService.warn('ForwardedPorts: (TunnelService) Created tunnel does not match requirements of requested tunnel. Host or port mismatch.');
392 }
393 if (privacy && tunnel.privacy !== privacy) {
394 this.logService.warn('ForwardedPorts: (TunnelService) Created tunnel does not match requirements of requested tunnel. Privacy mismatch.');
395 }
396 this._onTunnelOpened.fire(newTunnel);
397 return newTunnel;
398 });
399 }
401 > private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel {
402 return {
403 tunnelRemotePort: tunnel.tunnelRemotePort,
404 tunnelRemoteHost: tunnel.tunnelRemoteHost,
405 tunnelLocalPort: tunnel.tunnelLocalPort,
406 localAddress: tunnel.localAddress,
407 privacy: tunnel.privacy,
408 protocol: tunnel.protocol,
409 dispose: async () => {
410 this.logService.trace(`ForwardedPorts: (TunnelService) dispose request for ${tunnel.tunnelRemoteHost}:${tunnel.tunnelRemotePort} `);
411 const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost);
412 if (existingHost) {
413 const existing = existingHost.get(tunnel.tunnelRemotePort);
414 if (existing) {
415 existing.refcount--;
416 await this.tryDisposeTunnel(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort, existing);
417 }
418 }
419 }
420 };
421 }
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}.`);
426 const disposePromise: Promise<void> = tunnel.value.then(async (tunnel) => {
427 if (tunnel && (typeof tunnel !== 'string')) {
428 await tunnel.dispose(true);
429 this._onTunnelClosed.fire({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort });
430 }
431 });
432 if (this._tunnels.has(remoteHost)) {
433 this._tunnels.get(remoteHost)!.delete(remotePort);
434 }
435 return disposePromise;
436 }
437 }
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);
442 if (portMap && portMap.has(remotePort)) {
443 const value = portMap.get(remotePort)!;
444 value.refcount = 0;
445 await this.tryDisposeTunnel(remoteHost, remotePort, value);
446 }
447 }
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());
452 }
453 this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel });
454 }
456 > private async removeEmptyOrErrorTunnelFromMap(remoteHost: string, remotePort: number) {
457 const hostMap = this._tunnels.get(remoteHost);
458 if (hostMap) {
459 const tunnel = hostMap.get(remotePort);
460 const tunnelResult = tunnel ? await tunnel.value : undefined;
461 if (!tunnelResult || (typeof tunnelResult === 'string')) {
462 hostMap.delete(remotePort);
463 }
464 if (hostMap.size === 0) {
465 this._tunnels.delete(remoteHost);
466 }
467 }
468 }
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.
473 if (isLocalhost(remoteHost)) {
474 hosts.push(...LOCALHOST_ADDRESSES);
475 // For localhost, we add the all interfaces hosts because if the tunnel is already available at all interfaces,
476 // then of course it is available at localhost.
477 hosts.push(...ALL_INTERFACES_ADDRESSES);
478 } else if (isAllInterfaces(remoteHost)) {
479 hosts.push(...ALL_INTERFACES_ADDRESSES);
480 }
481
482 const existingPortMaps = hosts.map(host => this._tunnels.get(host));
483 for (const map of existingPortMaps) {
484 const existingTunnel = map?.get(remotePort);
485 if (existingTunnel) {
486 return existingTunnel;
487 }
488 }
489 return undefined;
490 }
492 > canTunnel(uri: URI): boolean {
493 return !!extractLocalHostUriMetaDataForPortMapping(uri);
494 }
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;
503 this._factoryInProgress.add(key);
504 const preferredLocalPort = localPort === undefined ? remotePort : localPort;
505 const creationInfo = { elevationRequired: elevateIfNeeded ? this.isPortPrivileged(preferredLocalPort) : false };
506 const tunnelOptions: TunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort, privacy, public: privacy ? (privacy !== TunnelPrivacyId.Private) : undefined, protocol };
507 const tunnel = tunnelProvider.forwardPort(tunnelOptions, creationInfo);
508 if (tunnel) {
509 this.addTunnelToMap(remoteHost, remotePort, tunnel);
510 tunnel.finally(() => {
511 this.logService.trace('ForwardedPorts: (TunnelService) Tunnel created by provider.');
512 this._factoryInProgress.delete(key);
513 });
514 } else {
515 this._factoryInProgress.delete(key);
516 }
517 return tunnel;
518 }
519 > } tunnel.ts ×33