Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit 2d87cbe

Browse files
bnoordhuisJan Krems
authored and
Jan Krems
committed
fix: Make --inspect-port=0 work
Selecting port zero makes the inspector bind to a random port. The inspector prints the URL it listens on to stderr. Parse the output from the child process to find the port number. This commit coincidentally also makes `node inspect --port=0 x.js` work and a regression test to that effect has been added. Note that connecting to an existing process that listens on a non-standard port with `node inspect -p <pid>` does not work because then we never see the output from the inspector. Fixes: nodejs/node#16469
1 parent 93caa0f commit 2d87cbe

File tree

4 files changed

+60
-25
lines changed

4 files changed

+60
-25
lines changed

lib/_inspect.js

+12-19
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,9 @@ const [ InspectClient, createRepl ] =
4242

4343
const debuglog = util.debuglog('inspect');
4444

45-
const DEBUG_PORT_PATTERN = /^--(?:debug|inspect)(?:-port|-brk)?=(\d{1,5})$/;
46-
function getDefaultPort() {
47-
for (const arg of process.execArgv) {
48-
const match = arg.match(DEBUG_PORT_PATTERN);
49-
if (match) {
50-
return +match[1];
51-
}
52-
}
53-
return 9229;
54-
}
55-
5645
function portIsFree(host, port, timeout = 2000) {
46+
if (port === 0) return Promise.resolve(); // Binding to a random port.
47+
5748
const retryDelay = 150;
5849
let didTimeOut = false;
5950

@@ -110,9 +101,11 @@ function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) {
110101
let output = '';
111102
function waitForListenHint(text) {
112103
output += text;
113-
if (/Debugger listening on/.test(output)) {
104+
if (/Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//.test(output)) {
105+
const host = RegExp.$1;
106+
const port = Number.parseInt(RegExp.$2);
114107
child.stderr.removeListener('data', waitForListenHint);
115-
resolve(child);
108+
resolve([child, port, host]);
116109
}
117110
}
118111

@@ -160,10 +153,11 @@ class NodeInspector {
160153
options.port,
161154
this.childPrint.bind(this));
162155
} else {
163-
this._runScript = () => Promise.resolve(null);
156+
this._runScript =
157+
() => Promise.resolve([null, options.port, options.host]);
164158
}
165159

166-
this.client = new InspectClient(options.port, options.host);
160+
this.client = new InspectClient();
167161

168162
this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'];
169163
this.domainNames.forEach((domain) => {
@@ -223,17 +217,16 @@ class NodeInspector {
223217

224218
run() {
225219
this.killChild();
226-
const { host, port } = this.options;
227220

228-
return this._runScript().then((child) => {
221+
return this._runScript().then(([child, port, host]) => {
229222
this.child = child;
230223

231224
let connectionAttempts = 0;
232225
const attemptConnect = () => {
233226
++connectionAttempts;
234227
debuglog('connection attempt #%d', connectionAttempts);
235228
this.stdout.write('.');
236-
return this.client.connect()
229+
return this.client.connect(port, host)
237230
.then(() => {
238231
debuglog('connection established');
239232
this.stdout.write(' ok');
@@ -288,7 +281,7 @@ class NodeInspector {
288281

289282
function parseArgv([target, ...args]) {
290283
let host = '127.0.0.1';
291-
let port = getDefaultPort();
284+
let port = 9229;
292285
let isRemote = false;
293286
let script = target;
294287
let scriptArgs = args;

lib/internal/inspect_client.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,12 @@ function decodeFrameHybi17(data) {
164164
}
165165

166166
class Client extends EventEmitter {
167-
constructor(port, host) {
167+
constructor() {
168168
super();
169169
this.handleChunk = this._handleChunk.bind(this);
170170

171-
this._port = port;
172-
this._host = host;
171+
this._port = undefined;
172+
this._host = undefined;
173173

174174
this.reset();
175175
}
@@ -284,7 +284,9 @@ class Client extends EventEmitter {
284284
});
285285
}
286286

287-
connect() {
287+
connect(port, host) {
288+
this._port = port;
289+
this._host = host;
288290
return this._discoverWebsocketPath()
289291
.then((urlPath) => this._connectWebsocket(urlPath));
290292
}

test/cli/launch.test.js

+40
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,46 @@ test('custom port', (t) => {
2626
});
2727
});
2828

29+
test('random port', (t) => {
30+
const script = Path.join('examples', 'three-lines.js');
31+
32+
const cli = startCLI(['--port=0', script]);
33+
34+
return cli.waitForInitialBreak()
35+
.then(() => cli.waitForPrompt())
36+
.then(() => {
37+
t.match(cli.output, 'debug>', 'prints a prompt');
38+
t.match(
39+
cli.output,
40+
/< Debugger listening on /,
41+
'forwards child output');
42+
})
43+
.then(() => cli.quit())
44+
.then((code) => {
45+
t.equal(code, 0, 'exits with success');
46+
});
47+
});
48+
49+
test('random port with --inspect-port=0', (t) => {
50+
const script = Path.join('examples', 'three-lines.js');
51+
52+
const cli = startCLI([script], ['--inspect-port=0']);
53+
54+
return cli.waitForInitialBreak()
55+
.then(() => cli.waitForPrompt())
56+
.then(() => {
57+
t.match(cli.output, 'debug>', 'prints a prompt');
58+
t.match(
59+
cli.output,
60+
/< Debugger listening on /,
61+
'forwards child output');
62+
})
63+
.then(() => cli.quit())
64+
.then((code) => {
65+
t.equal(code, 0, 'exits with success');
66+
});
67+
});
68+
2969
test('examples/three-lines.js', (t) => {
3070
const script = Path.join('examples', 'three-lines.js');
3171
const cli = startCLI([script]);

test/cli/start-cli.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const BREAK_MESSAGE = new RegExp('(?:' + [
1616
'exception', 'other', 'promiseRejection',
1717
].join('|') + ') in', 'i');
1818

19-
function startCLI(args) {
20-
const child = spawn(process.execPath, [CLI, ...args]);
19+
function startCLI(args, flags = []) {
20+
const child = spawn(process.execPath, [...flags, CLI, ...args]);
2121
let isFirstStdoutChunk = true;
2222

2323
const outputBuffer = [];

0 commit comments

Comments
 (0)