Repository navigation
Conversation
|
Status Reproduced on main, and compared with Node.js v26.3.0: const http = require("node:http");
const net = require("node:net");
const payload = "GET /a HTTP/1.1\r\nHost: x\r\n\r\nGET /b HTTP/1.1\r\nHost: x\r\nno colon here\r\n\r\n";
const server = http.createServer((req, res) => res.end("ok"));
server.on("clientError", (err, socket) => {
console.log(JSON.stringify({ code: err.code, rawPacket: err.rawPacket.toString("latin1") }));
socket.destroy();
server.close();
});
server.listen(0, "127.0.0.1", () => {
const c = net.connect(server.address().port, "127.0.0.1", () => c.write(payload));
c.on("error", () => {});
});Node.js prints a The stack: #44510, #45396, #45397, #45398, #45399 (draft), #45400 (draft). CI: the one red test in the runs of this stack is |
c2b76a4 to
de273e4
Compare
9cd6431 to
6a5010d
Compare
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline findings, I also checked whether dropping the in-place lowercasing changes HTTP/1 routing or node:http's req.method: HttpRouter::route (packages/bun-uws/src/HttpRouter.h:255) matches method nodes by their uppercase names ("GET") with the "*" node as the last fallback, so the as-sent method routes the same as before; and node:http's strict request-line validation (isStrictMethodChar plus the methodIdFromWire check in packages/bun-uws/src/HttpParser.h:877-914) rejects any lowercase byte before dispatch, so a node:http handler still only ever sees an all-uppercase method. The mixed-case concern therefore only applies where strict validation is off and the 501 guard is skipped (a direct Bun.serve({ onNodeHTTPRequest }) context), which is not a documented user path.
Extended reasoning...
The change removes the |= 32 lowercasing in uWS HttpRequest::getMethod(), deletes getCaseSensitiveMethod(), rewires its two HttpContext.h callers, and adds one node:http pipelining test; it touches the HTTP/1 request parser (untrusted network bytes) but adds no new parsing, only removes a write into the read buffer. Two confirmed inline findings exist (test failure wiring and the H3 casing divergence), so the inline comments already signal that a human look is needed; this note only records what else was checked and ruled out.
6a5010d to
c7df286
Compare
de273e4 to
8b4be0f
Compare
|
Updated 2:49 AM PT - Oct 17th, 2026
⏳ @robobun, your commit 84f2a1d is still building in |
c7df286 to
fbde686
Compare
8b4be0f to
989d5db
Compare
fbde686 to
47a7b48
Compare
989d5db to
84f2a1d
Compare
84f2a1d to
c5d8886
Compare
47a7b48 to
142d9eb
Compare
…Error' rawPacket HttpRequest::getMethod() lowercased the method in the receive buffer when a handler read it. rawPacket is that buffer, so an earlier request of the same read showed as "get /a". getMethod() now returns the method as the client sent it, and getCaseSensitiveMethod(), which did the same, goes.
c5d8886 to
5c34108
Compare
142d9eb to
eaf2275
Compare
Problem
rawPacketof a'clientError'shows an earlier request of the same read with a lowercase method,get /a HTTP/1.1. Node.js shows the bytes as the client sent them.HttpRequest::getMethod()(packages/bun-uws/src/HttpParser.h:539) lowercases the method in the receive buffer when a handler reads it.rawPacketis that buffer.Fix
getMethod()returns the method as the client sent it.getCaseSensitiveMethod(), which did the same, goes.METHOD_MAP, which has the uppercase names. Since Bun.serve: answer 501 to an unknown method and 404 to a router miss, not a closed socket #45396 a handler runs only for one of those names.test/js/bun/http/node-http-pipeline-callbacks.test.ts(1 new test, fails without the change). Suites: Notes.Background
'clientError'gets the read that held the bad request. An earlier request of that read ran its handler before the error.Downsides
rawPacketsees a difference.Notes
GET /a HTTP/1.1\r\nHost: x\r\n\r\nGET /b HTTP/1.1\r\nHost: x\r\nno colon here\r\n\r\n. The handler of/aruns, then the parser fails on/b. Node.js v26.3.0 reports the read as sent. Bun reportedget /a ...for the first request.getMethod()has two readers.uws_req_get_methodgives the name to Rust, where each reader looks it up withMethod::whichorMethod::find.METHOD_MAPhas an uppercase and a lowercase entry for each method. The second reader is a fallback inNodeHTTP.cppthat a node:http server does not reach, because its parser accepts only the 36 uppercase names.Postthat reached a handler was read asPOSTthrough this lowercase pass. With Bun.serve: answer 501 to an unknown method and 404 to a router miss, not a closed socket #45396 that request gets 501 before any handler. So this PR changes onlyrawPacket.Http3Request::getMethod()keeps its lowercase copy, because their routers register lowercase names.fetch: 2423, 2395. POST tofetch: 2732, 2697. PUT tofetch: 2629, 2601. PROPFIND tofetch: 2693, 2630. UNSUBSCRIBE tofetch: 2776, 2692. node:http, GET: 2581, 2553. A standalone driver runs the realHttpParser.handHttpRouter.h(clang -O3) underqemu-x86_64 -one-insn-per-tb -d exec,nochain. Repeat runs differ by at most 4 instructions in 2.6 million.perfis not available in the build container (perf_event_paranoidis 4), andvalgrindis not installed.llvm-size): text 80,700,499 -> 80,701,779 bytes (Bun.serve: register the default handler on "/*" for every method #45397, then this PR, on base f4d755a). data and bss do not change. The C shim behindAnyRequest::method()is now small enough that link-time optimization inlines the function into its callers: 584 bytes less for it, and 422 to 589 bytes more in each of 6 route callbacks.node-http-pipeline-callbacks,bun-serve-routes,node-http, and 35test-http-*Node tests. On the PRs after it, which have this change:node-http-displaced-response,serve-http2,serve-http3, threetest-http-*-client-errorNode tests.