@@ -35,6 +35,7 @@ const {
3535 NumberParseInt,
3636 ObjectDefineProperty,
3737 ObjectSetPrototypeOf,
38+ SafeSet,
3839 Symbol,
3940 SymbolAsyncDispose,
4041 SymbolDispose,
@@ -912,6 +913,7 @@ Socket.prototype._destroy = function(exception, cb) {
912913 if ( this . _server ) {
913914 debug ( 'has server' ) ;
914915 this . _server . _connections -- ;
916+ this . _server . _connectionSockets ?. delete ( this ) ;
915917 if ( this . _server . _emitCloseIfDrained ) {
916918 this . _server . _emitCloseIfDrained ( ) ;
917919 }
@@ -1867,6 +1869,7 @@ function Server(options, connectionListener) {
18671869 }
18681870
18691871 this . _connections = 0 ;
1872+ this . _connectionSockets = new SafeSet ( ) ;
18701873
18711874 this [ async_id_symbol ] = - 1 ;
18721875 this . _handle = null ;
@@ -2365,6 +2368,7 @@ function onconnection(err, clientHandle) {
23652368 }
23662369
23672370 self . _connections ++ ;
2371+ self . _connectionSockets . add ( socket ) ;
23682372 socket . server = self ;
23692373 socket . _server = self ;
23702374 self . emit ( 'connection' , socket ) ;
@@ -2436,6 +2440,22 @@ Server.prototype.close = function(cb) {
24362440 this . _handle = null ;
24372441 }
24382442
2443+ // Register a beforeExit handler to destroy any remaining connections
2444+ // before the process exits. This ensures connections are torn down
2445+ // during normal JS execution rather than during environment cleanup,
2446+ // where on Windows the IOCP completion processing can race with
2447+ // handle teardown.
2448+ if ( this . _connectionSockets . size > 0 ) {
2449+ const connections = this . _connectionSockets ;
2450+ const onBeforeExit = ( ) => {
2451+ process . removeListener ( 'beforeExit' , onBeforeExit ) ;
2452+ for ( const socket of connections ) {
2453+ socket . destroy ( ) ;
2454+ }
2455+ } ;
2456+ process . on ( 'beforeExit' , onBeforeExit ) ;
2457+ }
2458+
24392459 if ( this . _usingWorkers ) {
24402460 let left = this . _workers . length ;
24412461 const onWorkerClose = ( ) => {
0 commit comments