提交 1171409f 编写于 作者: A Alex Ross

Better preserve host name in port forwarding

Fixes https://github.com/microsoft/vscode-remote-release/issues/2711
上级 15122ccb
...@@ -56,6 +56,14 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: ...@@ -56,6 +56,14 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address:
}; };
} }
export function isLocalhost(host: string): boolean {
return host === 'localhost' || host === '127.0.0.1';
}
function getOtherLocalhost(host: string): string | undefined {
return (host === 'localhost') ? '127.0.0.1' : ((host === '127.0.0.1') ? 'localhost' : undefined);
}
export abstract class AbstractTunnelService implements ITunnelService { export abstract class AbstractTunnelService implements ITunnelService {
declare readonly _serviceBrand: undefined; declare readonly _serviceBrand: undefined;
...@@ -105,7 +113,7 @@ export abstract class AbstractTunnelService implements ITunnelService { ...@@ -105,7 +113,7 @@ export abstract class AbstractTunnelService implements ITunnelService {
return undefined; return undefined;
} }
if (!remoteHost || (remoteHost === '127.0.0.1')) { if (!remoteHost) {
remoteHost = 'localhost'; remoteHost = 'localhost';
} }
...@@ -172,13 +180,29 @@ export abstract class AbstractTunnelService implements ITunnelService { ...@@ -172,13 +180,29 @@ export abstract class AbstractTunnelService implements ITunnelService {
this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel }); this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel });
} }
protected getTunnelFromMap(remoteHost: string, remotePort: number): { refcount: number, readonly value: Promise<RemoteTunnel> } | undefined {
const otherLocalhost = getOtherLocalhost(remoteHost);
let portMap: Map<number, { refcount: number, readonly value: Promise<RemoteTunnel> }> | undefined;
if (otherLocalhost) {
const firstMap = this._tunnels.get(remoteHost);
const secondMap = this._tunnels.get(otherLocalhost);
if (firstMap && secondMap) {
portMap = new Map([...Array.from(firstMap.entries()), ...Array.from(secondMap.entries())]);
} else {
portMap = firstMap ?? secondMap;
}
} else {
portMap = this._tunnels.get(remoteHost);
}
return portMap ? portMap.get(remotePort) : undefined;
}
protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined; protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined;
} }
export class TunnelService extends AbstractTunnelService { export class TunnelService extends AbstractTunnelService {
protected retainOrCreateTunnel(_addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise<RemoteTunnel> | undefined { protected retainOrCreateTunnel(_addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise<RemoteTunnel> | undefined {
const portMap = this._tunnels.get(remoteHost); const existing = this.getTunnelFromMap(remoteHost, remotePort);
const existing = portMap ? portMap.get(remotePort) : undefined;
if (existing) { if (existing) {
++existing.refcount; ++existing.refcount;
return existing.value; return existing.value;
......
...@@ -86,7 +86,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { ...@@ -86,7 +86,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
this.tunnelLocalPort = address.port; this.tunnelLocalPort = address.port;
await this._barrier.wait(); await this._barrier.wait();
this.localAddress = 'localhost:' + address.port; this.localAddress = `${this.tunnelRemoteHost === '127.0.0.1' ? '127.0.0.1' : 'localhost'}:${address.port}`;
return this; return this;
} }
...@@ -132,8 +132,7 @@ export class TunnelService extends AbstractTunnelService { ...@@ -132,8 +132,7 @@ export class TunnelService extends AbstractTunnelService {
} }
protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined { protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined {
const portMap = this._tunnels.get(remoteHost); const existing = this.getTunnelFromMap(remoteHost, remotePort);
const existing = portMap ? portMap.get(remotePort) : undefined;
if (existing) { if (existing) {
++existing.refcount; ++existing.refcount;
return existing.value; return existing.value;
......
...@@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; ...@@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { isLocalhost, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
...@@ -163,18 +163,34 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { ...@@ -163,18 +163,34 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
}); });
} }
private mapHasTunnel(map: Map<string, Tunnel>, host: string, port: number): boolean {
if (!isLocalhost(host)) {
return map.has(MakeAddress(host, port));
}
const stringAddress = MakeAddress('localhost', port);
if (map.has(stringAddress)) {
return true;
}
const numberAddress = MakeAddress('127.0.0.1', port);
if (map.has(numberAddress)) {
return true;
}
return false;
}
get candidates(): TunnelItem[] { get candidates(): TunnelItem[] {
const candidates: TunnelItem[] = []; const candidates: TunnelItem[] = [];
this._candidates.forEach(value => { this._candidates.forEach(value => {
let key = MakeAddress(value.host, value.port); if (!this.mapHasTunnel(this.model.forwarded, value.host, value.port) &&
if (!this.model.forwarded.has(key) && !this.model.detected.has(key)) { !this.mapHasTunnel(this.model.detected, value.host, value.port)) {
// The host:port hasn't been forwarded or detected. However, if the candidate is 0.0.0.0, // The host:port hasn't been forwarded or detected. However, if the candidate is 0.0.0.0,
// also check that the port hasn't already been forwarded with localhost, and vice versa. // also check that the port hasn't already been forwarded with localhost, and vice versa.
// For example: no need to show 0.0.0.0:3000 as a candidate if localhost:3000 is already forwarded. // For example: no need to show 0.0.0.0:3000 as a candidate if localhost:3000 is already forwarded.
const otherHost = value.host === '0.0.0.0' ? 'localhost' : (value.host === 'localhost' ? '0.0.0.0' : undefined); const otherHost = value.host === '0.0.0.0' ? 'localhost' : (value.host === 'localhost' ? '0.0.0.0' : undefined);
if (otherHost) { if (otherHost) {
key = MakeAddress(otherHost, value.port); if (this.mapHasTunnel(this.model.forwarded, otherHost, value.port) ||
if (this.model.forwarded.has(key) || this.model.detected.has(key)) { this.mapHasTunnel(this.model.detected, otherHost, value.port)) {
return; return;
} }
} }
...@@ -411,11 +427,11 @@ class TunnelItem implements ITunnelItem { ...@@ -411,11 +427,11 @@ class TunnelItem implements ITunnelItem {
get label(): string { get label(): string {
if (this.name) { if (this.name) {
return nls.localize('remote.tunnelsView.forwardedPortLabel0', "{0}", this.name); return nls.localize('remote.tunnelsView.forwardedPortLabel0', "{0}", this.name);
} else if (this.localAddress && (this.remoteHost !== 'localhost')) { } else if (this.localAddress && !isLocalhost(this.remoteHost)) {
return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} \u2192 {2}", this.remoteHost, this.remotePort, this.localAddress); return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} \u2192 {2}", this.remoteHost, this.remotePort, this.localAddress);
} else if (this.localAddress) { } else if (this.localAddress) {
return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} \u2192 {1}", this.remotePort, this.localAddress); return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} \u2192 {1}", this.remotePort, this.localAddress);
} else if (this.remoteHost !== 'localhost') { } else if (!isLocalhost(this.remoteHost)) {
return nls.localize('remote.tunnelsView.forwardedPortLabel4', "{0}:{1}", this.remoteHost, this.remotePort); return nls.localize('remote.tunnelsView.forwardedPortLabel4', "{0}:{1}", this.remoteHost, this.remotePort);
} else { } else {
return nls.localize('remote.tunnelsView.forwardedPortLabel5', "{0}", this.remotePort); return nls.localize('remote.tunnelsView.forwardedPortLabel5', "{0}", this.remotePort);
......
...@@ -60,7 +60,7 @@ export class UrlFinder extends Disposable { ...@@ -60,7 +60,7 @@ export class UrlFinder extends Disposable {
if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) {
// normalize the host name // normalize the host name
let host = serverUrl.hostname; let host = serverUrl.hostname;
if (host !== '0.0.0.0') { if (host !== '0.0.0.0' && host !== '127.0.0.1') {
host = 'localhost'; host = 'localhost';
} }
this._onDidMatchLocalUrl.fire({ port, host }); this._onDidMatchLocalUrl.fire({ port, host });
......
...@@ -451,10 +451,10 @@ export class NativeWindow extends Disposable { ...@@ -451,10 +451,10 @@ export class NativeWindow extends Disposable {
return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority; return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority;
} }
} : undefined; } : undefined;
const tunnel = await this.tunnelService.openTunnel(addressProvider, undefined, portMappingRequest.port); const tunnel = await this.tunnelService.openTunnel(addressProvider, portMappingRequest.address, portMappingRequest.port);
if (tunnel) { if (tunnel) {
return { return {
resolved: uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` }), resolved: uri.with({ authority: tunnel.localAddress }),
dispose: () => tunnel.dispose(), dispose: () => tunnel.dispose(),
}; };
} }
......
...@@ -48,15 +48,8 @@ export interface Tunnel { ...@@ -48,15 +48,8 @@ export interface Tunnel {
closeable?: boolean; closeable?: boolean;
} }
function ToLocalHost(host: string): string {
if (host === '127.0.0.1') {
host = 'localhost';
}
return host;
}
export function MakeAddress(host: string, port: number): string { export function MakeAddress(host: string, port: number): string {
return ToLocalHost(host) + ':' + port; return host + ':' + port;
} }
export class TunnelModel extends Disposable { export class TunnelModel extends Disposable {
...@@ -218,7 +211,7 @@ export class TunnelModel extends Disposable { ...@@ -218,7 +211,7 @@ export class TunnelModel extends Disposable {
const nullIndex = value.detail.indexOf('\0'); const nullIndex = value.detail.indexOf('\0');
const detail = value.detail.substr(0, nullIndex > 0 ? nullIndex : value.detail.length).trim(); const detail = value.detail.substr(0, nullIndex > 0 ? nullIndex : value.detail.length).trim();
return { return {
host: ToLocalHost(value.host), host: value.host,
port: value.port, port: value.port,
detail detail
}; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册