sussudio
Version:
An unofficial VS Code Internal API
189 lines (188 loc) • 5.1 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as net from 'net';
/**
* Given a start point and a max number of retries, will find a port that
* is openable. Will return 0 in case no free port can be found.
*/
export function findFreePort(startPort, giveUpAfter, timeout, stride = 1) {
let done = false;
return new Promise(resolve => {
const timeoutHandle = setTimeout(() => {
if (!done) {
done = true;
return resolve(0);
}
}, timeout);
doFindFreePort(startPort, giveUpAfter, stride, (port) => {
if (!done) {
done = true;
clearTimeout(timeoutHandle);
return resolve(port);
}
});
});
}
function doFindFreePort(startPort, giveUpAfter, stride, clb) {
if (giveUpAfter === 0) {
return clb(0);
}
const client = new net.Socket();
// If we can connect to the port it means the port is already taken so we continue searching
client.once('connect', () => {
dispose(client);
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
});
client.once('data', () => {
// this listener is required since node.js 8.x
});
client.once('error', (err) => {
dispose(client);
// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect
if (err.code !== 'ECONNREFUSED') {
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
}
// Otherwise it means the port is free to use!
return clb(startPort);
});
client.connect(startPort, '127.0.0.1');
}
// Reference: https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc#56
export const BROWSER_RESTRICTED_PORTS = {
1: true,
7: true,
9: true,
11: true,
13: true,
15: true,
17: true,
19: true,
20: true,
21: true,
22: true,
23: true,
25: true,
37: true,
42: true,
43: true,
53: true,
69: true,
77: true,
79: true,
87: true,
95: true,
101: true,
102: true,
103: true,
104: true,
109: true,
110: true,
111: true,
113: true,
115: true,
117: true,
119: true,
123: true,
135: true,
137: true,
139: true,
143: true,
161: true,
179: true,
389: true,
427: true,
465: true,
512: true,
513: true,
514: true,
515: true,
526: true,
530: true,
531: true,
532: true,
540: true,
548: true,
554: true,
556: true,
563: true,
587: true,
601: true,
636: true,
989: true,
990: true,
993: true,
995: true,
1719: true,
1720: true,
1723: true,
2049: true,
3659: true,
4045: true,
5060: true,
5061: true,
6000: true,
6566: true,
6665: true,
6666: true,
6667: true,
6668: true,
6669: true,
6697: true,
10080: true // Amanda
};
/**
* 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.
*/
export function findFreePortFaster(startPort, giveUpAfter, timeout, hostname = '127.0.0.1') {
let resolved = false;
let timeoutHandle = undefined;
let countTried = 1;
const server = net.createServer({ pauseOnConnect: true });
function doResolve(port, resolve) {
if (!resolved) {
resolved = true;
server.removeAllListeners();
server.close();
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
resolve(port);
}
}
return new Promise(resolve => {
timeoutHandle = setTimeout(() => {
doResolve(0, resolve);
}, timeout);
server.on('listening', () => {
doResolve(startPort, resolve);
});
server.on('error', err => {
if (err && (err.code === 'EADDRINUSE' || err.code === 'EACCES') && (countTried < giveUpAfter)) {
startPort++;
countTried++;
server.listen(startPort, hostname);
}
else {
doResolve(0, resolve);
}
});
server.on('close', () => {
doResolve(0, resolve);
});
server.listen(startPort, hostname);
});
}
function dispose(socket) {
try {
socket.removeAllListeners('connect');
socket.removeAllListeners('error');
socket.end();
socket.destroy();
socket.unref();
}
catch (error) {
console.error(error); // otherwise this error would get lost in the callback chain
}
}