Skip to content

Bun.serve: apply TCP backpressure to a request body the handler reads slowly - #36006

Merged
Jarred-Sumner merged 15 commits into
mainfrom
farm/b04e5c23/serve-request-body-backpressure
Jul 27, 2026
Merged

Bun.serve: apply TCP backpressure to a request body the handler reads slowly#36006
Jarred-Sumner merged 15 commits into
mainfrom
farm/b04e5c23/serve-request-body-backpressure

drop stale u16 width note on the FlagsBits comment

fa95bc4
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Jul 26, 2026 in 36m 56s

Code review found 2 important issues

Found 2 candidates, confirmed 2. See review comments for details.

Details

Severity Count
🔴 Important 2
🟡 Nit 0
🟣 Pre-existing 0
Severity File:Line Issue
🔴 Important src/runtime/server/RequestContext.rs:4083-4088 Bun.write(file, req) in a Bun.serve handler hangs for request bodies > 1 MiB
🔴 Important src/runtime/server/RequestContext.rs:1189-1190 resume_request_body_socket() in end_already_responded_stream may dereference a freed uWS socket

Annotations

Check failure on line 4088 in src/runtime/server/RequestContext.rs

See this annotation in the file changed.

@claude claude / Claude Code Review

Bun.write(file, req) in a Bun.serve handler hangs for request bodies > 1 MiB

`await Bun.write(file, req)` in a `Bun.serve` handler now hangs for any body > 1 MiB. That path (Blob.rs:5232-5250, `BodyValue::Locked` → `WriteFileWaitFromLockedValueTask`) sets `locked.on_receive_value` and awaits the last chunk without calling `on_start_buffering` or creating a ByteStream, so `REQUEST_BODY_BUFFER_ALL` is never set, the pre-stream HWM check pauses the socket at 1 MiB, and nothing ever resumes it — same bug class as the `buffer_action` deadlock adf72bd fixed, on the `on_receive

Check failure on line 1190 in src/runtime/server/RequestContext.rs

See this annotation in the file changed.

@claude claude / Claude Code Review

resume_request_body_socket() in end_already_responded_stream may dereference a freed uWS socket

The fix in adf72bd added `resume_request_body_socket()` to `end_already_responded_stream()`, but this function's own doc comment (lines 1170-1178) states that `self.resp` may point to a socket uSockets has **already freed** via `us_internal_free_closed_sockets` and must be released *without dereferencing it*. When `REQUEST_BODY_PAUSED` is set, `resp.resume_()` → `us_socket_resume(s)` reads `s->fin_deferred` (socket.c:852, libuv) / `s->flags.is_paused` (:857) on freed memory, and `HttpResponse::r