Path: blob/main/extensions/copilot/src/util/vs/base/node/ports.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import * as net from 'net';89/**10* Given a start point and a max number of retries, will find a port that11* is openable. Will return 0 in case no free port can be found.12*/13export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise<number> {14let done = false;1516return new Promise(resolve => {17const timeoutHandle = setTimeout(() => {18if (!done) {19done = true;20return resolve(0);21}22}, timeout);2324doFindFreePort(startPort, giveUpAfter, stride, (port) => {25if (!done) {26done = true;27clearTimeout(timeoutHandle);28return resolve(port);29}30});31});32}3334function doFindFreePort(startPort: number, giveUpAfter: number, stride: number, clb: (port: number) => void): void {35if (giveUpAfter === 0) {36return clb(0);37}3839const client = new net.Socket();4041// If we can connect to the port it means the port is already taken so we continue searching42client.once('connect', () => {43dispose(client);4445return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);46});4748client.once('data', () => {49// this listener is required since node.js 8.x50});5152client.once('error', (err: Error & { code?: string }) => {53dispose(client);5455// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect56if (err.code !== 'ECONNREFUSED') {57return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);58}5960// Otherwise it means the port is free to use!61return clb(startPort);62});6364client.connect(startPort, '127.0.0.1');65}6667// Reference: https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc#5668export const BROWSER_RESTRICTED_PORTS: Record<number, boolean> = {691: true, // tcpmux707: true, // echo719: true, // discard7211: true, // systat7313: true, // daytime7415: true, // netstat7517: true, // qotd7619: true, // chargen7720: true, // ftp data7821: true, // ftp access7922: true, // ssh8023: true, // telnet8125: true, // smtp8237: true, // time8342: true, // name8443: true, // nicname8553: true, // domain8669: true, // tftp8777: true, // priv-rjs8879: true, // finger8987: true, // ttylink9095: true, // supdup91101: true, // hostriame92102: true, // iso-tsap93103: true, // gppitnp94104: true, // acr-nema95109: true, // pop296110: true, // pop397111: true, // sunrpc98113: true, // auth99115: true, // sftp100117: true, // uucp-path101119: true, // nntp102123: true, // NTP103135: true, // loc-srv /epmap104137: true, // netbios105139: true, // netbios106143: true, // imap2107161: true, // snmp108179: true, // BGP109389: true, // ldap110427: true, // SLP (Also used by Apple Filing Protocol)111465: true, // smtp+ssl112512: true, // print / exec113513: true, // login114514: true, // shell115515: true, // printer116526: true, // tempo117530: true, // courier118531: true, // chat119532: true, // netnews120540: true, // uucp121548: true, // AFP (Apple Filing Protocol)122554: true, // rtsp123556: true, // remotefs124563: true, // nntp+ssl125587: true, // smtp (rfc6409)126601: true, // syslog-conn (rfc3195)127636: true, // ldap+ssl128989: true, // ftps-data129990: true, // ftps130993: true, // ldap+ssl131995: true, // pop3+ssl1321719: true, // h323gatestat1331720: true, // h323hostcall1341723: true, // pptp1352049: true, // nfs1363659: true, // apple-sasl / PasswordServer1374045: true, // lockd1385060: true, // sip1395061: true, // sips1406000: true, // X111416566: true, // sane-port1426665: true, // Alternate IRC [Apple addition]1436666: true, // Alternate IRC [Apple addition]1446667: true, // Standard IRC [Apple addition]1456668: true, // Alternate IRC [Apple addition]1466669: true, // Alternate IRC [Apple addition]1476697: true, // IRC + TLS14810080: true // Amanda149};150151export function isPortFree(port: number, timeout: number): Promise<boolean> {152return findFreePortFaster(port, 0, timeout).then(port => port !== 0);153}154155interface ServerError {156code?: string;157}158159/**160* 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.161*/162export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number, hostname: string = '127.0.0.1'): Promise<number> {163let resolved: boolean = false;164let timeoutHandle: Timeout | undefined = undefined;165let countTried: number = 1;166const server = net.createServer({ pauseOnConnect: true });167function doResolve(port: number, resolve: (port: number) => void) {168if (!resolved) {169resolved = true;170server.removeAllListeners();171server.close();172if (timeoutHandle) {173clearTimeout(timeoutHandle);174}175resolve(port);176}177}178return new Promise<number>(resolve => {179timeoutHandle = setTimeout(() => {180doResolve(0, resolve);181}, timeout);182183server.on('listening', () => {184doResolve(startPort, resolve);185});186server.on('error', (err: ServerError) => {187if (err && (err.code === 'EADDRINUSE' || err.code === 'EACCES') && (countTried < giveUpAfter)) {188startPort++;189countTried++;190server.listen(startPort, hostname);191} else {192doResolve(0, resolve);193}194});195server.on('close', () => {196doResolve(0, resolve);197});198server.listen(startPort, hostname);199});200}201202function dispose(socket: net.Socket): void {203try {204socket.removeAllListeners('connect');205socket.removeAllListeners('error');206socket.end();207socket.destroy();208socket.unref();209} catch (error) {210console.error(error); // otherwise this error would get lost in the callback chain211}212}213214215