Skip to content

Add browser-compatible Headers and streamed Response bodies - #210

Open
matthargett wants to merge 8 commits into
BabylonJS:mainfrom
rebeckerspecialties:fetch-response-headers-polyfill
Open

Add browser-compatible Headers and streamed Response bodies#210
matthargett wants to merge 8 commits into
BabylonJS:mainfrom
rebeckerspecialties:fetch-response-headers-polyfill

Conversation

@matthargett

@matthargettmatthargett commented Jul 22, 2026

Copy link
Copy Markdown

Depends on #208 and #209. The commits remain separate so this can be rebased to the Fetch-only change after those prerequisites land.

Replaces the ad hoc reusable buffered response object with standard-shaped Headers and Response classes. Headers normalize and validate fields, preserve current Set-Cookie behavior, and support live sorted iteration. Response bodies are ReadableStreams with single-consumption semantics, cloning, and browser-shaped metadata and body readers. Empty BufferSource bodies retain a non-null stream without enqueueing an invalid zero-length byte chunk.

UrlLib still buffers the transport response, but completion now copies it once into JavaScript-owned memory. The Response wrapper does not make another snapshot; multi-chunk consumers retain chunk references and allocate one exact contiguous result only when needed. Header iteration is mutation-cached, and delete/set compact storage in place.

Focused coverage is adapted from WPT fetch/api/headers and fetch/api/response, WebKit body-init and empty-chunk regressions, Firefox open-stream teardown, and a bounded Chromium disturbed-stream regression.

Validated on JavaScriptCore ASan/UBSan, JavaScriptCore TSan, and QuickJS Release: 254 JavaScript cases plus 8 native tests in each configuration.

Vendor the ES5 web-streams-polyfill 4.3.0 ponyfill and expose an idempotent Streams initializer that preserves constructors supplied by the selected JavaScript engine.
Cover readable, writable, transform, BYOB, error, tee, subclassing, and host-constructor behavior with focused ports from WPT plus Firefox and Chromium regression tests. Validate the implementation on JavaScriptCore under ASan/UBSan and QuickJS Release.
Implement iterable BlobPart conversion, MIME and endings normalization, zero-copy Blob composition and slicing, and lazy 64 KiB byte streams. Delegate File streaming and slicing to its backing Blob while preserving browser class identity.\n\nUse immutable shared segments so nested Blob and slice construction do not duplicate payload bytes. Add focused WPT, WebKit, and Firefox regression coverage for constructor ordering, iterator closure, UTF-8 decoding, BYOB reads, cancellation, lifetime safety, and large nested streams.
Replace the ad hoc buffered fetch response object with standard-shaped Headers and Response classes backed by ReadableStream bodies. Preserve host constructors, normalize and validate header values, enforce single-use bodies, and expose native responses with one transport-to-JavaScript copy.
Reuse normalized header views between mutations, compact header storage in place, retain stream chunks only until consumption, and allocate a contiguous body result at most once when required.
Add focused WPT, WebKit, Firefox, and Chromium regression coverage plus JavaScriptCore/QuickJS and initialization tests.
CopilotAI review requested due to automatic review settings July 22, 2026 05:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the runtime’s Fetch surface to use browser-shaped Headers and Response objects (with streamed bodies), adds an optional WHATWG Streams ponyfill to support streaming semantics, and extends the native Blob/File polyfills to support slicing and streaming in a way that matches modern web platform behavior.

Changes:

  • Added a Streams polyfill module that installs missing WHATWG Streams constructors from a vendored web-streams-polyfill ES5 ponyfill.
  • Reworked Fetch to use a JS Headers/Response polyfill layer and to expose response bodies as ReadableStreams with single-consumption + cloning behavior.
  • Extended Blob/File polyfills with slice() and stream() support and expanded unit tests with focused WPT/WebKit/Firefox/Chromium-derived coverage.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
Tests/UnitTests/Shared/Shared.cppInitializes Streams before Fetch and adds idempotence/host-preservation unit tests for Streams and Fetch.
Tests/UnitTests/Scripts/tests.tsAdds conformance-focused tests for Headers, Response, Streams behavior, and expands Blob/File streaming+slicing coverage.
Tests/UnitTests/CMakeLists.txtLinks UnitTests against the new Streams library.
Polyfills/Streams/ThirdParty/web-streams-polyfill/ponyfill.es5.jsVendors the ES5 ponyfill bundle used to provide missing Streams constructors.
Polyfills/Streams/ThirdParty/web-streams-polyfill/LICENSEAdds the upstream MIT license for the vendored ponyfill.
Polyfills/Streams/Source/StreamsScripts.h.inGenerates an embedded JS script by joining split ponyfill source parts at build time.
Polyfills/Streams/Source/Streams.cppImplements Streams::Initialize to fill only missing Streams globals via embedded ponyfill.
Polyfills/Streams/README.mdDocuments behavior and provenance of the Streams polyfill.
Polyfills/Streams/Include/Babylon/Polyfills/Streams.hDeclares the Streams polyfill initialization entry point.
Polyfills/Streams/CMakeLists.txtBuilds the Streams library and generates embedded ponyfill header.
Polyfills/File/Source/File.hAdds slice() and stream() method declarations to File.
Polyfills/File/Source/File.cppDelegates File slice/stream to Blob and sets Symbol.toStringTag for File instances.
Polyfills/Fetch/Source/FetchScripts.h.inAdds a generated header for embedding the Fetch JS polyfill.
Polyfills/Fetch/Source/FetchPolyfill.jsIntroduces JS implementations of Headers, Response, and createFetchResponse using ReadableStreams for bodies.
Polyfills/Fetch/Source/Fetch.cppReplaces the ad-hoc buffered response object with JS polyfill classes and a streamed body pipeline.
Polyfills/Fetch/Readme.mdUpdates Fetch documentation to describe streamed response bodies and browser-shaped APIs.
Polyfills/Fetch/CMakeLists.txtEmbeds Fetch polyfill JS and updates include paths for generated headers.
Polyfills/CMakeLists.txtAdds optional Streams polyfill subdirectory controlled by a CMake option.
Polyfills/Blob/Source/Blob.hRefactors Blob storage to shared segmented representation and adds slice/stream APIs.
Polyfills/Blob/Source/Blob.cppImplements zero-copy segmented Blob composition/slicing and lazy streaming via ReadableStream.
Polyfills/Blob/README.mdDocuments Blob streaming/slicing behavior and Streams dependency.
Polyfills/Blob/CMakeLists.txtIncludes Blob README in build sources.
CMakeLists.txtAdds JSRUNTIMEHOST_POLYFILL_STREAMS option to enable Streams polyfill.
Files not reviewed (1)
  • Polyfills/Streams/ThirdParty/web-streams-polyfill/ponyfill.es5.js: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadPolyfills/Fetch/Source/FetchPolyfill.js
Comment threadPolyfills/Fetch/Source/FetchPolyfill.js
Comment threadPolyfills/Fetch/Source/Fetch.cpp Outdated
Close byte streams for zero-length BufferSource bodies without enqueueing an invalid empty chunk. Preserve the non-null body and bodyUsed semantics, and add focused regression coverage alongside the WPT empty-response cases.
Replace a partial or null host Streams surface as a complete constructor suite so stream products retain compatible instanceof relationships. Preserve a complete suite on repeated initialization.\n\nHarden the native initialization test so N-API failures complete the test promise instead of hanging, and cover partial host replacement plus null handling and cross-constructor compatibility.
Observe standard ReadableStream consumption paths with weak per-stream state instead of depending on web-streams-polyfill's private _disturbed field. Instrument stream readers and piping once during Fetch initialization, preserve stream identity, and cache the initialized Fetch implementation so repeated initialization does not stack wrappers or replace its internal state.
Treat Headers and Response as an implementation pair when either host global is missing or null. Harden native initialization tests against unhandled N-API failures and add WPT-derived coverage for reads, cancellation, piping, and streams disturbed before Response construction.
Use ReadableByteStreamController.byobRequest views directly and respond with the produced byte count instead of allocating and enqueueing a 64 KiB intermediate chunk. Keep the existing bounded allocation path for default readers.\n\nAdd WPT-derived coverage for small offset BYOB views that cross immutable Blob segment boundaries.
Sign up for freeto 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.

2 participants

@matthargett