From ee9d9c2b9bddfcbf3fd3482e30dee3911e59bf8e Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Thu, 30 May 2024 10:40:19 +0200 Subject: [PATCH] net: add new net.server.listen tracing channel PR-URL: https://github.com/nodejs/node/pull/53136 Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Mohammed Keyvanzadeh Reviewed-By: Santiago Gimeno --- doc/api/diagnostics_channel.md | 21 +++++ lib/net.js | 19 ++++ test/parallel/test-diagnostics-channel-net.js | 92 ++++++++++++++++--- 3 files changed, 119 insertions(+), 13 deletions(-) diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index e27c55eedf3a61..ce2aae7705f98a 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -1131,6 +1131,26 @@ Emitted when a new TCP or pipe client socket is created. Emitted when a new TCP or pipe connection is received. +`tracing:net.server.listen:asyncStart` + +* `server` {net.Server} +* `options` {Object} + +Emitted when [`net.Server.listen()`][] is invoked, before the port or pipe is actually setup. + +`tracing:net.server.listen:asyncEnd` + +* `server` {net.Server} + +Emitted when [`net.Server.listen()`][] has completed and thus the server is ready to accept connection. + +`tracing:net.server.listen:error` + +* `server` {net.Server} +* `error` {Error} + +Emitted when [`net.Server.listen()`][] is returning an error. + #### UDP `udp.socket` @@ -1179,5 +1199,6 @@ Emitted when a new thread is created. [`diagnostics_channel.unsubscribe(name, onMessage)`]: #diagnostics_channelunsubscribename-onmessage [`end` event]: #endevent [`error` event]: #errorevent +[`net.Server.listen()`]: net.md#serverlisten [`start` event]: #startevent [context loss]: async_context.md#troubleshooting-context-loss diff --git a/lib/net.js b/lib/net.js index 1e48e79cce435e..6c33bb4dcd6520 100644 --- a/lib/net.js +++ b/lib/net.js @@ -151,6 +151,7 @@ const kPerfHooksNetConnectContext = Symbol('kPerfHooksNetConnectContext'); const dc = require('diagnostics_channel'); const netClientSocketChannel = dc.channel('net.client.socket'); const netServerSocketChannel = dc.channel('net.server.socket'); +const netServerListen = dc.tracingChannel('net.server.listen'); const { hasObserver, @@ -1879,6 +1880,11 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { if (typeof rval === 'number') { const error = new UVExceptionWithHostPort(rval, 'listen', address, port); + + if (netServerListen.hasSubscribers) { + netServerListen.error.publish({ server: this, error }); + } + process.nextTick(emitErrorNT, this, error); return; } @@ -1898,6 +1904,11 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { const ex = new UVExceptionWithHostPort(err, 'listen', address, port); this._handle.close(); this._handle = null; + + if (netServerListen.hasSubscribers) { + netServerListen.error.publish({ server: this, error: ex }); + } + defaultTriggerAsyncIdScope(this[async_id_symbol], process.nextTick, emitErrorNT, @@ -1906,6 +1917,10 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { return; } + if (netServerListen.hasSubscribers) { + netServerListen.asyncEnd.publish({ server: this }); + } + // Generate connection key, this should be unique to the connection this._connectionKey = addressType + ':' + address + ':' + port; @@ -1993,6 +2008,10 @@ Server.prototype.listen = function(...args) { throw new ERR_SERVER_ALREADY_LISTEN(); } + if (netServerListen.hasSubscribers) { + netServerListen.asyncStart.publish({ server: this, options }); + } + if (cb !== null) { this.once('listening', cb); } diff --git a/test/parallel/test-diagnostics-channel-net.js b/test/parallel/test-diagnostics-channel-net.js index 4004cbf8df7a00..57dfc9a6fc2beb 100644 --- a/test/parallel/test-diagnostics-channel-net.js +++ b/test/parallel/test-diagnostics-channel-net.js @@ -5,21 +5,87 @@ const net = require('net'); const dc = require('diagnostics_channel'); const isNetSocket = (socket) => socket instanceof net.Socket; +const isNetServer = (server) => server instanceof net.Server; -dc.subscribe('net.client.socket', common.mustCall(({ socket }) => { - assert.strictEqual(isNetSocket(socket), true); -})); +function testDiagnosticChannel(subscribers, test, after) { + dc.tracingChannel('net.server.listen').subscribe(subscribers); -dc.subscribe('net.server.socket', common.mustCall(({ socket }) => { - assert.strictEqual(isNetSocket(socket), true); -})); + test(common.mustCall(() => { + dc.tracingChannel('net.server.listen').unsubscribe(subscribers); + after?.(); + })); +} -const server = net.createServer(common.mustCall((socket) => { - socket.destroy(); - server.close(); -})); +const testSuccessfullListen = common.mustCall(() => { + let cb; + const server = net.createServer(common.mustCall((socket) => { + socket.destroy(); + server.close(); + cb(); + })); -server.listen(() => { - const { port } = server.address(); - net.connect(port); + dc.subscribe('net.client.socket', common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); + })); + + dc.subscribe('net.server.socket', common.mustCall(({ socket }) => { + assert.strictEqual(isNetSocket(socket), true); + })); + + testDiagnosticChannel( + { + asyncStart: common.mustCall(({ server: currentServer, options }) => { + assert.strictEqual(isNetServer(server), true); + assert.strictEqual(currentServer, server); + assert.strictEqual(options.customOption, true); + }), + asyncEnd: common.mustCall(({ server: currentServer }) => { + assert.strictEqual(isNetServer(server), true); + assert.strictEqual(currentServer, server); + }), + error: common.mustNotCall() + }, + common.mustCall((callback) => { + cb = callback; + server.listen({ port: 0, customOption: true }, () => { + const { port } = server.address(); + net.connect(port); + }); + }), + testFailingListen + ); }); + +const testFailingListen = common.mustCall(() => { + const originalServer = net.createServer(common.mustNotCall()); + + originalServer.listen(() => { + const server = net.createServer(common.mustNotCall()); + + testDiagnosticChannel( + { + asyncStart: common.mustCall(({ server: currentServer, options }) => { + assert.strictEqual(isNetServer(server), true); + assert.strictEqual(currentServer, server); + assert.strictEqual(options.customOption, true); + }), + asyncEnd: common.mustNotCall(), + error: common.mustCall(({ server: currentServer }) => { + assert.strictEqual(isNetServer(server), true); + assert.strictEqual(currentServer, server); + }), + }, + common.mustCall((callback) => { + server.on('error', () => {}); + server.listen({ port: originalServer.address().port, customOption: true }); + callback(); + }), + common.mustCall(() => { + originalServer.close(); + server.close(); + }) + ); + }); +}); + +testSuccessfullListen();