Repository navigation
Conversation
…P/1 fallback path Carry over the tests of #37717 for emit('connection') and http2 allowHTTP1 connections. They cover states that had no test on this path: a request body that still arrives after the response ended, a response that has not ended, a sweep in the tick of res.end(), a sweep from the request listener, closeAllConnections() next to an upgraded socket, and Http2SecureServer#close() with a partial request head. A connection that has sent nothing is idle, as in Node 26.5.
|
Status This PR adds tests only. The five tests come from #37717, which is closed because #43557 and #44032 supersede it. How I verified them (453a99a):
CI: the diff is green. The one red test is not related to this PR.
The PR is ready for a maintainer. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: oven-sh/bun/.coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review. WalkthroughAdds tests for HTTP server connection sweeps and server close behavior. The tests cover multiple request and response states, including partial request heads, pending responses, reused connections, and upgraded sockets. They also cover HTTP/1 connections accepted by an HTTP/2 secure server. ChangesHTTP connection closing
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to This PR adds regression coverage for HTTP connection closing; no concrete user-facing or test-workflow failure is established, so it appears ready after normal checks. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Updated 4:28 AM PT - Sep 29th, 2026
❌ @robobun, your commit 453a99a has 1 failures in
🧪 To try this PR locally: bunx bun-pr 44203That installs a local version of the PR into your bun-44203 --bun |
… an idle sweep test waits on readResponseBody() also rejects on 'end': a destroy() of the server half of a duplexPair() arrives on the client half as 'end', not as 'close'. The waits for a request head, for the request listener and for the 'upgrade' listener reject when the server closes the connection first. The http2 test asserts that the server has not emitted 'close' while one connection is still open.
There was a problem hiding this comment.
LGTM — 453a99a addresses the three earlier inline notes, and this run found nothing new.
What was reviewed:
readResponseBodynow settles on 'end' as well as 'close'/'error', andbeforeClosewraps every head-arrival and listener wait in the fournode:httptests, so a wrongly destroyed duplexPair half rejects instead of hanging; listener cleanup in thefinallyavoids a late rejection escaping the race.- The http2 test's
partialHeadArrivedrejects on 'close', and theonServerClosemock is checked after both destroyed clients report 'close', so an early server 'close' can now fail the test. - All five tests assert one exact
toEqualobject per state and release every duplex/TLS socket and the server infinally;mock,connectOverHttp1andreadHttp1Responseare already defined in node-http2.test.js.
Extended reasoning...
The diff is test-only (303 insertions) in test/js/node/http/node-http-server-close-drain.test.ts and test/js/node/http2/node-http2.test.js, adding four http.Server closeIdleConnections()/closeAllConnections() tests driven through emit('connection') with duplexPair() and one Http2SecureServer#close() allowHTTP1 test; no runtime source is touched and no security-sensitive surface is involved. The latest commit resolves every point raised in the prior review (hang-on-regression paths now reject, the 'close' ordering is asserted), neither file has a CODEOWNERS entry, no other reviewer has requested changes, and the bug hunt exited on dry_streak, which decided approve.
Related to #37717
Behaviour change: none
Problem
close()andcloseIdleConnections()follow Node.js on the HTTP/1 fallback path.res.end()closeAllConnections()Http2SecureServer#close()with a partial headFix
test/js/node/http/node-http-server-close-drain.test.tsand one test totest/js/node/http2/node-http2.test.js.node:httptests also pass, unchanged, on Node.js v26.5.0 and v26.10.0.Background
src/js/internal/http1_server_fallback.ts. It serves the sockets fromserver.emit("connection", socket)and the HTTP/1.1 clients of an http2 server withallowHTTP1. A socket that a listeninghttp.Serveraccepts does not use it.closeIdleHttp1Connections) destroys a connection when no request is open on it and no response is pending. A request is open from its first byte to its end.Notes
Result for each state. "Closed" and "kept" are the
destroyedflag of the server side of the connection, read directly after the call.res.end()closeAllConnections(), connection with a partial headcloseIdleConnections()destroys it beforecloseAllConnections(), upgraded socketallowHTTP1,close(), nothing sentallowHTTP1,close(), part of a request head'close''close'allowHTTP1,close(), one exchange doneThe new tests.
closeIdleConnections() with emit('connection'): a connection in each state, one sweepcloseIdleConnections() with emit('connection'): closes a connection in the tick of its res.end()closeIdleConnections() with emit('connection'): called in the request listener, leaves the connection of that requestcloseAllConnections() with emit('connection'): destroys a connection that holds a partial head, leaves an upgraded socketHttp2SecureServer#close() leaves an allowHTTP1 connection that has sent part of a request headNode.js runs.
node-http-server-close-drain.test.tsimportsbun:testandharness. A resolve hook (module.registerHooks) maps those two imports to a small shim, and Node.js runs the unchanged file. Only the four new tests were selected.node-http2.test.jshas more imports that are specific to Bun, so for that test the same steps and assertions ran as a plain script: 30 of 30 runs pass on Node.js v26.5.0 and on v26.10.0.Node.js v26.3.0 is older than nodejs/node@417aacbc365. There, the two tests that have a connection with nothing sent fail on that row only.
Other runs on the debug build of main. The whole
node-http-server-close-drain.test.ts: 32 pass, 0 fail.node-http2.test.js -t "Http2SecureServer#close": 4 pass, 0 fail.Run time. On the debug build the four
node:httptests take 0.07 to 0.6 s each and the http2 test takes 0.26 to 0.35 s. On Node.js the fournode:httptests take 1 to 43 ms each.When the server drops a connection that a test waits on, the test fails at once with a message.
readResponseBody()rejects on'end','close'and'error'of the client half.beforeClose()rejects when the server half closes before a request head arrives or before a listener runs.The
'close'check of the http2 test is before the last request head is complete, while one connection is open. It is not after the last response: Node.js v26.5.0 and v26.10.0 emit the server's'close'before the client has that response (30 of 30 runs), and main emits it after (30 of 30 runs). The two events come from different sockets, so the test does not assert their order.Not carried over from #37717.
http2 allowHTTP1 connections leave the server's connection list when they closereads theConnectionsListthat #37717 added. Main keeps the sockets in aSet. With.sizein place of.all()the test passes on main, but it tests an internal field, so it is not included.Found outside this change. On the fallback path, main does not apply
headersTimeout,requestTimeoutorkeepAliveTimeout. The probe uses TCP sockets fromemit('connection')and timeouts of 300, 600 and 300 ms. Node.js v26.5.0 and v26.10.0 close the three connections after 0.4, 0.7 and 1.3 s, the first one with a 408. Main closes none of them in 2.5 s. #37739 and #37724 are the open PRs for that. #37739 has the branch of #37717 as its base.no test proof · iteration 1 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/node/http/node-http-server-close-drain.test.ts