Skip to content

fix(download): pipe the body instead of sendStream - #282

Merged
LouisHaftmann merged 1 commit into
devfrom
fix/download-backpressure
Oct 23, 2026
Merged

LouisHaftmann merged 1 commit into
devfrom
fix/download-backpressure

Conversation

@LouisHaftmann

Copy link
Copy Markdown
Collaborator

Split out of #270 so it can land independently of Range support.

h3's sendStream web-stream path calls res.write() without waiting for drain and doesn't notice the client going away:

stream.pipeline(stream, res) paces the read to the socket and destroys the source on close, which releases the lease through the existing close handler. ERR_STREAM_PREMATURE_CLOSE is logged at debug.

The new case in tests/proxy-backpressure.test.ts (from @chenrui333) mounts the real route in-process and pauses the client after the first chunk. Against sendStream: ~533 MB queued in the response. With pipeline: under 1 MiB.

Closes #274

Co-authored-by: @peter-svensson @chenrui333

🤖 Generated with Claude Code

h3's sendStream web-stream path calls res.write() without waiting for
drain and never notices the client hanging up. A slow client made the
server buffer the whole entry in socket write buffers (OOM with a few
concurrent restores, #274), and an aborted download kept draining the
backend read and held its reader lease until expiry.

stream.pipeline paces the read to the socket and destroys the source on
close, which releases the lease.

Co-authored-by: Peter Svensson <peter@sparetimecoders.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unbounded memory growth: upload and download routes ignore stream backpressure (v9.7.0, v9.8.0)

1 participant