Skip to content

fix(net): buffer closed ReadableStream bodies for fetch POST#3056

Merged
karlseguin merged 4 commits into
lightpanda-io:mainfrom
arimu1:fix/3052-fetch-readablestream-body
Jul 26, 2026
Merged

fix(net): buffer closed ReadableStream bodies for fetch POST#3056
karlseguin merged 4 commits into
lightpanda-io:mainfrom
arimu1:fix/3052-fetch-readablestream-body

Conversation

@arimu1

@arimu1 arimu1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes net: fetch with a ReadableStream request body sends an empty body and resolves successfully #3052 where fetch() with a ReadableStream request body silently sent Content-Length: 0 and an empty payload.
  • Adds ReadableStream.collectBodyBytes to drain a closed stream's internal queue synchronously; open, locked, or errored streams reject with TypeError instead of sending nothing.
  • JS controller.enqueue stores chunks as .js_value via enqueueValue; collectBodyBytes now converts TypedArray/ArrayBuffer/string .js_value entries (matching Response.StreamConsumer._onReadFulfilled) before buffering.
  • BodyInit.extract .stream arm uses that helper instead of returning an empty byte slice.
  • HTML fixtures: fetch_readablestream_body (multi-chunk success), fetch_readablestream_body_reject (open + locked streams → TypeError), plus /xhr/echo_post_body test-server endpoint.

Approach

Implements option 2 (buffer) from the issue for the common case (stream closed synchronously in start() with enqueued chunks), and option 1 (reject) for streams that cannot be drained synchronously. This matches the issue's expected behavior diagram and avoids silent data loss.

Test plan

  • zig fmt --check on changed .zig files — clean
  • TEST_FILTER="fetch_readablestream_body" make testnot run locally: V8 gclient sync failed (_bad_scm conflicts; also No space left on device during llvm artifact extract). Maintainer CI should exercise the HTML fixtures.
  • Full make test — blocked by same V8 cache/disk issue locally

CLA

I will sign the CLA via cla-assistant-lite when prompted on this PR (first contribution to this repo).

Fixes #3052

BodyInit.extract returned an empty body for ReadableStream request
bodies, so fetch POST with duplex:half silently sent Content-Length: 0.
Drain closed streams synchronously via collectBodyBytes; reject open,
locked, or errored streams with TypeError instead of sending nothing.

Fixes lightpanda-io#3052
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

arimu1 added 2 commits July 25, 2026 09:19
JS controller.enqueue stores chunks as .js_value via enqueueValue, so
collectBodyBytes must extract TypedArray/ArrayBuffer/string bytes with
local scope and arena.dupe before buffering. Add multi-chunk success and
open/locked reject fixtures in fetch.html.
Drop nested localScope; fetch already runs under an active Caller local.
Fixes compile error where &ls (Scope) was passed where *js.Local is required.
@arimu1

arimu1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@arimu1

arimu1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

recheck

Use existing toStringSmart to convert value to a string
@karlseguin
karlseguin merged commit f697c20 into lightpanda-io:main Jul 26, 2026
25 checks passed
@karlseguin

Copy link
Copy Markdown
Collaborator

Thanks.

@github-actions github-actions Bot locked and limited conversation to collaborators Jul 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

net: fetch with a ReadableStream request body sends an empty body and resolves successfully

2 participants