diff --git a/lib/_inspect.js b/lib/_inspect.js index 9f0f714..5897887 100644 --- a/lib/_inspect.js +++ b/lib/_inspect.js @@ -22,6 +22,7 @@ 'use strict'; const { spawn } = require('child_process'); const { EventEmitter } = require('events'); +const net = require('net'); const util = require('util'); const runAsStandalone = typeof __dirname !== 'undefined'; @@ -90,6 +91,45 @@ function createAgentProxy(domain, client) { }); } +function portIsFree(host, port, timeout = 2000) { + const retryDelay = 150; + let didTimeOut = false; + + return new Promise((resolve, reject) => { + setTimeout(() => { + didTimeOut = true; + reject(new Error( + `Timeout (${timeout}) waiting for ${host}:${port} to be free`)); + }, timeout); + + function pingPort() { + if (didTimeOut) return; + + const socket = net.connect(port, host); + let didRetry = false; + function retry() { + if (!didRetry && !didTimeOut) { + didRetry = true; + setTimeout(pingPort, retryDelay); + } + } + + socket.on('error', (error) => { + if (error.code === 'ECONNREFUSED') { + resolve(); + } else { + retry(); + } + }); + socket.on('connect', () => { + socket.destroy(); + retry(); + }); + } + pingPort(); + }); +} + class NodeInspector { constructor(options, stdin, stdout) { this.options = options; @@ -169,7 +209,14 @@ class NodeInspector { run() { this.killChild(); - return this._runScript().then((child) => { + const { host, port } = this.options; + + const runOncePortIsFree = () => { + return portIsFree(host, port) + .then(() => this._runScript()); + }; + + return runOncePortIsFree().then((child) => { this.child = child; let connectionAttempts = 0; @@ -194,7 +241,6 @@ class NodeInspector { }); }; - const { host, port } = this.options; this.print(`connecting to ${host}:${port} ..`, true); return attemptConnect(); });