From 0de77399f2525cb1fadb547d483486f51e536e3d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 25 Apr 2016 17:04:25 +0200 Subject: [PATCH] find free ports: make it work, add timeout and tests --- src/vs/base/node/ports.ts | 57 +++++++++++++++---- src/vs/base/test/node/port.test.ts | 29 ++++++++-- .../thread/electron-browser/threadService.ts | 2 +- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index d3d5e1d960b..b3039481f1b 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -11,23 +11,60 @@ import net = require('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: number, giveUpAfter:number, clb: (port: number) => void): void { +export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, clb: (port: number) => void): void { + let done = false; + + const timeoutHandle = setTimeout(() =>  { + if (!done) { + done = true; + + return clb(0); + } + }, timeout); + + doFindFreePort(startPort, giveUpAfter, (port) => { + if (!done) { + done = true; + window.clearInterval(timeoutHandle); + + return clb(port); + } + }); +} + +function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: number) => void): void { if (giveUpAfter === 0) { return clb(0); } - let tryPort = startPort; + const client = new net.Socket(); - let server = net.createServer(); - server.listen(tryPort, (err) => { - server.once('close', () => { - return clb(tryPort); - }); + // If we can connect to the port it means the port is already taken so we continue searching + client.once('connect', () => { + dispose(client); - server.close(); + return doFindFreePort(startPort + 1, giveUpAfter - 1, clb); }); - server.on('error', (err) => { - findFreePort(startPort + 1, giveUpAfter - 1, clb); + 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 + 1, giveUpAfter - 1, clb); + } + + // Otherwise it means the port is free to use! + return clb(startPort); }); + + client.connect(startPort); +} + +function dispose(socket: net.Socket): void { + socket.removeAllListeners('connect'); + socket.removeAllListeners('error'); + socket.end(); + socket.destroy(); + socket.unref(); } \ No newline at end of file diff --git a/src/vs/base/test/node/port.test.ts b/src/vs/base/test/node/port.test.ts index 3259f6d1584..52a34a292d1 100644 --- a/src/vs/base/test/node/port.test.ts +++ b/src/vs/base/test/node/port.test.ts @@ -10,18 +10,18 @@ import * as net from 'net'; import ports = require('vs/base/node/ports'); suite('Ports', () => { - test('Finds a free port', function (done: () => void) { + test('Finds a free port (no timeout)', function (done: () => void) { // get an initial freeport >= 7000 - ports.findFreePort(7000, 100, (initialPort) => { + ports.findFreePort(7000, 100, 300000, (initialPort) => { assert.ok(initialPort >= 7000); // create a server to block this port const server = net.createServer(); - server.listen(initialPort, null, null, () => { + server.listen(initialPort, null, null, () =>  { // once listening, find another free port and assert that the port is different from the opened one - ports.findFreePort(7000, 50, (freePort) => { + ports.findFreePort(7000, 50, 300000, (freePort) => { assert.ok(freePort >= 7000 && freePort !== initialPort); server.close(); @@ -30,4 +30,25 @@ suite('Ports', () => { }); }); }); + + test('Finds a free port (with timeout)', function (done: () => void) { + + // get an initial freeport >= 7000 + ports.findFreePort(7000, 100, 300000, (initialPort) => { + assert.ok(initialPort >= 7000); + + // create a server to block this port + const server = net.createServer(); + server.listen(initialPort, null, null, () =>  { + + // once listening, find another free port and assert that the port is different from the opened one + ports.findFreePort(7000, 50, 0, (freePort) => { + assert.equal(freePort, 0); + server.close(); + + done(); + }); + }); + }); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/services/thread/electron-browser/threadService.ts b/src/vs/workbench/services/thread/electron-browser/threadService.ts index 80174a2c513..459142ec324 100644 --- a/src/vs/workbench/services/thread/electron-browser/threadService.ts +++ b/src/vs/workbench/services/thread/electron-browser/threadService.ts @@ -275,7 +275,7 @@ class ExtensionHostProcessManager { // Check for a free debugging port if (typeof config.env.debugExtensionHostPort === 'number') { - return findFreePort(config.env.debugExtensionHostPort, 10 /* try 10 ports */, (port) => { + return findFreePort(config.env.debugExtensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => { if (!port) { console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color: black'); -- GitLab