From 374cc835461d8d7959e5e563910ccd5b739e45f5 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 26 Mar 2026 22:01:40 -0700 Subject: [PATCH] net: track and close connections in a pre-exit hook This is an attempt to fix the flaky failures on http(s) and tcp tests on Windows. --- lib/net.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/net.js b/lib/net.js index efd2029fb67ddd..9fb8df96b30038 100644 --- a/lib/net.js +++ b/lib/net.js @@ -35,6 +35,7 @@ const { NumberParseInt, ObjectDefineProperty, ObjectSetPrototypeOf, + SafeSet, Symbol, SymbolAsyncDispose, SymbolDispose, @@ -912,6 +913,7 @@ Socket.prototype._destroy = function(exception, cb) { if (this._server) { debug('has server'); this._server._connections--; + this._server._connectionSockets?.delete(this); if (this._server._emitCloseIfDrained) { this._server._emitCloseIfDrained(); } @@ -1867,6 +1869,7 @@ function Server(options, connectionListener) { } this._connections = 0; + this._connectionSockets = new SafeSet(); this[async_id_symbol] = -1; this._handle = null; @@ -2365,6 +2368,7 @@ function onconnection(err, clientHandle) { } self._connections++; + self._connectionSockets.add(socket); socket.server = self; socket._server = self; self.emit('connection', socket); @@ -2436,6 +2440,22 @@ Server.prototype.close = function(cb) { this._handle = null; } + // Register a beforeExit handler to destroy any remaining connections + // before the process exits. This ensures connections are torn down + // during normal JS execution rather than during environment cleanup, + // where on Windows the IOCP completion processing can race with + // handle teardown. + if (this._connectionSockets.size > 0) { + const connections = this._connectionSockets; + const onBeforeExit = () => { + process.removeListener('beforeExit', onBeforeExit); + for (const socket of connections) { + socket.destroy(); + } + }; + process.on('beforeExit', onBeforeExit); + } + if (this._usingWorkers) { let left = this._workers.length; const onWorkerClose = () => {