Skip to content

Commit

Permalink
Add hybrid automatic port detection (#178687)
Browse files Browse the repository at this point in the history
* Hybrid port detection

* Use correct source for autoforwarded

* Watch for port closure in output forwarder

* Update setting description
  • Loading branch information
alexr00 committed Mar 30, 2023
1 parent fdc9c7c commit d4d0d2a
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
4 changes: 2 additions & 2 deletions src/vs/workbench/api/browser/mainThreadTunnelService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { MainThreadTunnelServiceShape, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource, PortAttributesProviderSelector, TunnelDto } from 'vs/workbench/api/common/extHost.protocol';
import { TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel';
import { Disposable } from 'vs/base/common/lifecycle';
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
Expand Down Expand Up @@ -42,7 +42,7 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun

private processFindingEnabled(): boolean {
return (!!this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING) || this.tunnelService.hasTunnelProvider)
&& (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS);
&& (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) !== PORT_AUTO_SOURCE_SETTING_OUTPUT);
}

async $setRemoteTunnelService(processId: number): Promise<void> {
Expand Down
32 changes: 28 additions & 4 deletions src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as nls from 'vs/nls';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Extensions, IViewContainersRegistry, IViewsRegistry, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
import { Attributes, AutoTunnelSource, IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS, TUNNEL_VIEW_CONTAINER_ID, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { Attributes, AutoTunnelSource, IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_HYBRID, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS, TUNNEL_VIEW_CONTAINER_ID, TUNNEL_VIEW_ID, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction, openPreviewEnabledContext } from 'vs/workbench/contrib/remote/browser/tunnelView';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
Expand Down Expand Up @@ -202,7 +202,10 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon
} else {
const useProc = () => (configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS);
if (useProc()) {
this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService,
this._register(new ProcAutomaticPortForwarding(false, configurationService, remoteExplorerService, notificationService,
openerService, externalOpenerService, tunnelService, hostService, logService, contextKeyService));
} else if (configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_HYBRID) {
this._register(new ProcAutomaticPortForwarding(true, configurationService, remoteExplorerService, notificationService,
openerService, externalOpenerService, tunnelService, hostService, logService, contextKeyService));
}
this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService,
Expand Down Expand Up @@ -433,6 +436,10 @@ class OutputAutomaticPortForwarding extends Disposable {
this.tryStartStopUrlFinder();
}));
this.tryStartStopUrlFinder();

if (configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_HYBRID) {
this._register(this.tunnelService.onTunnelClosed(tunnel => this.notifier.hide([tunnel.port])));
}
}

private tryStartStopUrlFinder() {
Expand Down Expand Up @@ -484,6 +491,7 @@ class ProcAutomaticPortForwarding extends Disposable {
private portsFeatures: IDisposable | undefined;

constructor(
private readonly unforwardOnly: boolean,
private readonly configurationService: IConfigurationService,
readonly remoteExplorerService: IRemoteExplorerService,
readonly notificationService: INotificationService,
Expand Down Expand Up @@ -613,12 +621,24 @@ class ProcAutomaticPortForwarding extends Disposable {

private async handleCandidateUpdate(removed: Map<string, { host: string; port: number }>) {
const removedPorts: number[] = [];
let autoForwarded: Set<string>;
if (this.unforwardOnly) {
autoForwarded = new Set();
for (const entry of this.remoteExplorerService.tunnelModel.forwarded.entries()) {
if (entry[1].source.source === TunnelSource.Auto) {
autoForwarded.add(entry[0]);
}
}
} else {
autoForwarded = this.autoForwarded;
}

for (const removedPort of removed) {
const key = removedPort[0];
const value = removedPort[1];
if (this.autoForwarded.has(key)) {
if (autoForwarded.has(key)) {
await this.remoteExplorerService.close(value);
this.autoForwarded.delete(key);
autoForwarded.delete(key);
removedPorts.push(value.port);
} else if (this.notifiedOnly.has(key)) {
this.notifiedOnly.delete(key);
Expand All @@ -628,6 +648,10 @@ class ProcAutomaticPortForwarding extends Disposable {
}
}

if (this.unforwardOnly) {
return;
}

if (removedPorts.length > 0) {
await this.notifier.hide(removedPorts);
}
Expand Down
7 changes: 4 additions & 3 deletions src/vs/workbench/contrib/remote/common/remote.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,12 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
},
'remote.autoForwardPortsSource': {
type: 'string',
markdownDescription: localize('remote.autoForwardPortsSource', "Sets the source from which ports are automatically forwarded when {0} is true. On Windows and Mac remotes, the `process` option has no effect and `output` will be used. Requires a reload to take effect.", '`#remote.autoForwardPorts#`'),
enum: ['process', 'output'],
markdownDescription: localize('remote.autoForwardPortsSource', "Sets the source from which ports are automatically forwarded when {0} is true. On Windows and Mac remotes, the `process` and `hybrid` options have no effect and `output` will be used. Requires a reload to take effect.", '`#remote.autoForwardPorts#`'),
enum: ['process', 'output', 'hybrid'],
enumDescriptions: [
localize('remote.autoForwardPortsSource.process', "Ports will be automatically forwarded when discovered by watching for processes that are started and include a port."),
localize('remote.autoForwardPortsSource.output', "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports forwarded based on output will not be \"un-forwarded\" until reload or until the port is closed by the user in the Ports view.")
localize('remote.autoForwardPortsSource.output', "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports forwarded based on output will not be \"un-forwarded\" until reload or until the port is closed by the user in the Ports view."),
localize('remote.autoForwardPortsSource.hybrid', "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports will be \"un-forwarded\" by watching for processes that listen on that port to be terminated.")
],
default: 'process'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts';
export const PORT_AUTO_SOURCE_SETTING = 'remote.autoForwardPortsSource';
export const PORT_AUTO_SOURCE_SETTING_PROCESS = 'process';
export const PORT_AUTO_SOURCE_SETTING_OUTPUT = 'output';
export const PORT_AUTO_SOURCE_SETTING_HYBRID = 'hybrid';

export enum TunnelType {
Candidate = 'Candidate',
Expand Down

0 comments on commit d4d0d2a

Please sign in to comment.