提交 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:
};
}
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 {
declare readonly _serviceBrand: undefined;
......@@ -105,7 +113,7 @@ export abstract class AbstractTunnelService implements ITunnelService {
return undefined;
}
if (!remoteHost || (remoteHost === '127.0.0.1')) {
if (!remoteHost) {
remoteHost = 'localhost';
}
......@@ -172,13 +180,29 @@ export abstract class AbstractTunnelService implements ITunnelService {
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;
}
export class TunnelService extends AbstractTunnelService {
protected retainOrCreateTunnel(_addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise<RemoteTunnel> | undefined {
const portMap = this._tunnels.get(remoteHost);
const existing = portMap ? portMap.get(remotePort) : undefined;
const existing = this.getTunnelFromMap(remoteHost, remotePort);
if (existing) {
++existing.refcount;
return existing.value;
......
......@@ -86,7 +86,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
this.tunnelLocalPort = address.port;
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;
}
......@@ -132,8 +132,7 @@ export class TunnelService extends AbstractTunnelService {
}
protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined {
const portMap = this._tunnels.get(remoteHost);
const existing = portMap ? portMap.get(remotePort) : undefined;
const existing = this.getTunnelFromMap(remoteHost, remotePort);
if (existing) {
++existing.refcount;
return existing.value;
......
......@@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
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 { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
......@@ -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[] {
const candidates: TunnelItem[] = [];
this._candidates.forEach(value => {
let key = MakeAddress(value.host, value.port);
if (!this.model.forwarded.has(key) && !this.model.detected.has(key)) {
if (!this.mapHasTunnel(this.model.forwarded, value.host, value.port) &&
!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,
// 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.
const otherHost = value.host === '0.0.0.0' ? 'localhost' : (value.host === 'localhost' ? '0.0.0.0' : undefined);
if (otherHost) {
key = MakeAddress(otherHost, value.port);
if (this.model.forwarded.has(key) || this.model.detected.has(key)) {
if (this.mapHasTunnel(this.model.forwarded, otherHost, value.port) ||
this.mapHasTunnel(this.model.detected, otherHost, value.port)) {
return;
}
}
......@@ -411,11 +427,11 @@ class TunnelItem implements ITunnelItem {
get label(): string {
if (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);
} else if (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);
} else {
return nls.localize('remote.tunnelsView.forwardedPortLabel5', "{0}", this.remotePort);
......
......@@ -60,7 +60,7 @@ export class UrlFinder extends Disposable {
if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) {
// normalize the host name
let host = serverUrl.hostname;
if (host !== '0.0.0.0') {
if (host !== '0.0.0.0' && host !== '127.0.0.1') {
host = 'localhost';
}
this._onDidMatchLocalUrl.fire({ port, host });
......
......@@ -451,10 +451,10 @@ export class NativeWindow extends Disposable {
return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority;
}
} : undefined;
const tunnel = await this.tunnelService.openTunnel(addressProvider, undefined, portMappingRequest.port);
const tunnel = await this.tunnelService.openTunnel(addressProvider, portMappingRequest.address, portMappingRequest.port);
if (tunnel) {
return {
resolved: uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` }),
resolved: uri.with({ authority: tunnel.localAddress }),
dispose: () => tunnel.dispose(),
};
}
......
......@@ -48,15 +48,8 @@ export interface Tunnel {
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 {
return ToLocalHost(host) + ':' + port;
return host + ':' + port;
}
export class TunnelModel extends Disposable {
......@@ -218,7 +211,7 @@ export class TunnelModel extends Disposable {
const nullIndex = value.detail.indexOf('\0');
const detail = value.detail.substr(0, nullIndex > 0 ? nullIndex : value.detail.length).trim();
return {
host: ToLocalHost(value.host),
host: value.host,
port: value.port,
detail
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册