fix(ext/node): fix closeIdleConnections destroying active connections#33596
Conversation
closeIdleConnections() only checked `!socket._httpMessage` to determine if a connection was idle. This incorrectly treated connections that had never completed a request (e.g. still receiving headers) as idle, destroying them alongside truly idle keep-alive connections. Track whether a socket has ever completed a request-response cycle via `_httpMessageDetached` and only close sockets that have no active response AND have previously served one.
lunadogbot
left a comment
There was a problem hiding this comment.
LGTM as a targeted fix -- the _httpMessageDetached flag is set unconditionally in detachSocket() (line 317) which runs when a ServerResponse finishes, so it cleanly distinguishes 'fresh connection still receiving first headers' (flag false, the bug case) from 'idle keep-alive after at least one completed response' (flag true). The check !socket._httpMessage && socket._httpMessageDetached only destroys sockets that have served at least one request and have no response in flight. Verified against the 3 enrolled tests by name -- test-http-server-close-idle-wait-response still passing implies the in-flight-response branch (socket._httpMessage non-null) still protects an active write.
One residual race to think about: between a keep-alive cycle's response completing (detachSocket sets flag=true) and the next request reaching the point where assignSocket re-binds _httpMessage, the socket has !_httpMessage && _httpMessageDetached -- both conditions for 'idle' -- but it's actively parsing the next request's headers. A closeIdleConnections() call in that window would destroy an in-progress connection. Node guards this by also checking parser.incoming.complete (see lib/_http_server.js's check), which excludes sockets that are mid-message. Worth either matching that extra condition, or confirming via the test suite that the window is too short to hit in practice (the existing tests pass, so the answer is probably the latter). Not a blocker; flagging so it doesn't surprise you when a future user reports 'closeIdleConnections killed my keep-alive connection during request 2'.
Summary
closeIdleConnections()only checked!socket._httpMessagetodetermine if a connection was idle. This incorrectly treated
connections that had never completed a request (e.g. still receiving
headers) as idle, destroying them alongside truly idle keep-alive
connections.
_httpMessageDetachedflag set indetachSocket(), and only closesockets that have no active response AND have previously served one.
test-https-server-close-idle.jsandtest-http-server-close-idle.jsnode compat tests.Test plan
./x test-compat test-https-server-close-idle.jspasses (was failing)./x test-compat test-http-server-close-idle.jspasses (newly enrolled)./x test-compat test-https-server-close-all.jsstill passes./x test-compat test-http-server-close-idle-wait-response.jsstill passes