Skip to content

Commit 73cc9cb

Browse files
committed
Improve finding of free port
Fixes microsoft#88941, microsoft#88846, microsoft#85834
1 parent c1793c0 commit 73cc9cb

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

src/vs/base/node/ports.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,49 @@ function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: numb
7272
client.connect(startPort, '127.0.0.1');
7373
}
7474

75+
/**
76+
* Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener.
77+
*/
78+
export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise<number> {
79+
let resolved: boolean = false;
80+
let timeoutHandle: NodeJS.Timeout | undefined = undefined;
81+
let countTried: number = 1;
82+
const server = net.createServer({ pauseOnConnect: true });
83+
function doResolve(port: number, resolve: (port: number) => void) {
84+
if (!resolved) {
85+
resolved = true;
86+
server.removeAllListeners();
87+
server.close();
88+
if (timeoutHandle) {
89+
clearTimeout(timeoutHandle);
90+
}
91+
resolve(port);
92+
}
93+
}
94+
return new Promise<number>(resolve => {
95+
timeoutHandle = setTimeout(() => {
96+
doResolve(0, resolve);
97+
}, timeout);
98+
99+
server.on('listening', () => {
100+
doResolve(startPort, resolve);
101+
});
102+
server.on('error', err => {
103+
if (err && ((<any>err).code === 'EADDRINUSE' || (<any>err).code === 'EACCES') && (countTried < giveUpAfter)) {
104+
startPort++;
105+
countTried++;
106+
server.listen(startPort, '127.0.0.1');
107+
} else {
108+
doResolve(0, resolve);
109+
}
110+
});
111+
server.on('close', () => {
112+
doResolve(0, resolve);
113+
});
114+
server.listen(startPort, '127.0.0.1');
115+
});
116+
}
117+
75118
function dispose(socket: net.Socket): void {
76119
try {
77120
socket.removeAllListeners('connect');

src/vs/workbench/services/remote/node/tunnelService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory';
1616
import { ISignService } from 'vs/platform/sign/common/sign';
1717
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1818
import { ILogService } from 'vs/platform/log/common/log';
19-
import { findFreePort } from 'vs/base/node/ports';
19+
import { findFreePortFaster } from 'vs/base/node/ports';
2020
import { AbstractTunnelService } from 'vs/workbench/services/remote/common/tunnelService';
2121

22-
export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
22+
async function createRemoteTunnel(options: IConnectionOptions, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
2323
const tunnel = new NodeRemoteTunnel(options, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort);
2424
return tunnel.waitForReady();
2525
}
@@ -64,7 +64,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
6464
public async waitForReady(): Promise<this> {
6565

6666
// try to get the same port number as the remote port number...
67-
const localPort = await findFreePort(this.suggestedLocalPort ?? this.tunnelRemotePort, 1, 1000);
67+
const localPort = await findFreePortFaster(this.suggestedLocalPort ?? this.tunnelRemotePort, 2, 1000);
6868

6969
// if that fails, the method above returns 0, which works out fine below...
7070
const address = (<net.AddressInfo>this._server.listen(localPort).address());

0 commit comments

Comments
 (0)