Skip to content

HeadersInit: honor an overridden Symbol.iterator on a Headers object - #43023

Open
robobun wants to merge 3 commits into
mainfrom
robobun/4c5a6ad6/headers-init-iterator
Open

robobun wants to merge 3 commits into
mainfrom
robobun/4c5a6ad6/headers-init-iterator

Conversation

@robobun

@robobun robobun commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • A Headers object given as a HeadersInit is copied from its header list. Bun never reads its Symbol.iterator. Node and browsers call it. A subclass with its own storage loses every header: [headers] SuperHeaders not compatible with Bun's Response constructor remix-run/remix#10872 (SuperHeaders). WPT "Create headers with existing headers with custom iterator" fails.
  • The cause is a bare type check at each shortcut: JSFetchHeaders.cpp:136, JSWebSocket.cpp:316, Response.rs:1278, fetch.rs:1135, fetch.rs:1164, FetchSession.rs:124, server_body.rs:1203.

Fix

  • Add JSFetchHeaders::toWrappedAsInit (FetchHeaders::cast_as_init in Rust). It returns the header list only when a lookup of Symbol.iterator, which runs no user code, finds the built-in Headers.prototype.entries. Each shortcut uses it and keeps its fallback, the generic conversion.
  • Correct because the generic conversion is the Web IDL algorithm. An object with the built-in method keeps the copy, so fetch() still sends names in the case the user wrote. Undici has the same rule. Cost: 10 to 25 ns per converted Headers object.
  • Verified: test/js/web/fetch/headers.test.ts (17 new tests, 15 fail before) and the vendored wpt/headers-basic.any.js (1 of 23 fails before).
  • Self-reviewed: 5 concerns raised, 4 addressed. Not done: cast_ as a predicate, because server.fetch() still needs the pointer.

Background

  • HeadersInit is the Web IDL type of every headers value: a sequence of pairs or a record. For an object, Web IDL reads Symbol.iterator. If it is a method, the value converts through it.
  • FetchHeaders is the C++ header list. JSFetchHeaders is its JS wrapper, the Headers object.
  • The copy exists for speed, and because iteration yields lowercase names.
Notes

Repro. With @mjackson/headers@0.10.0 (the class from the Remix report):

import SuperHeaders from "@mjackson/headers";
const headers = new SuperHeaders();
headers.append("X-Test", "Bun test");
console.log(new Response("", { headers }).headers.get("X-Test"));
// main: null    this branch: "Bun test"    Node v26.3.0: "Bun test"

Without a package:

class H extends Headers { *[Symbol.iterator]() { yield ["x", "y"]; } }
const show = h => JSON.stringify([...Headers.prototype.entries.call(h)]);
console.log(show(new Headers(new H([["a", "b"]]))));
console.log(show(new Response(null, { headers: new H([["a", "b"]]) }).headers));
const h = new Headers([["a", "b"]]);
h[Symbol.iterator] = function* () { yield ["own", "1"]; };
console.log(show(new Headers(h)));
line 1 line 2 line 3
Node v26.3.0 [["x","y"]] [["x","y"]] [["own","1"]]
main [["a","b"]] [["a","b"]] [["a","b"]]
this branch [["x","y"]] [["x","y"]] [["own","1"]]

Entry points. new Headers(), Response and Request init (new Response, Response.json, Response.redirect, new Request), fetch() headers and proxy.headers (also Bun.FetchSession), server.upgrade(), and the WebSocket proxy.headers option. The WebSocket headers option already used the generic conversion.

The rule. Undici takes its copy path when V[Symbol.iterator] === Headers.prototype.entries and V is not a Proxy (lib/web/fetch/headers.js, webidl.converters.HeadersInit). toWrappedAsInit identifies the built-in by its host function, so the entries of another realm also passes. A Proxy of a Headers is not a JSFetchHeaders, so it never reached the copy.

The lookup. The function walks the prototype chain with getDirect. It stops, and returns null, at an object that overrides getOwnPropertySlot or getPrototype, or that has a static property table. A Proxy in the chain is the practical case. The generic conversion then does the real [[Get]], so a get trap or an accessor runs exactly once. Two tests pin that. To getDirect an accessor is a GetterSetter cell, which is not a JSFunction, so the function returns null for it too.

server.upgrade() and an empty init. create_from_js returns None for an empty HeadersInit, and server.upgrade() treated None as an invalid value. On main server.upgrade(req, { headers: {} }) throws upgrade options.headers must be a Headers or an object. With this PR a Headers object whose iterator yields nothing reaches the same code, so an empty conversion now adds no header, for {} and [] too. A test covers the three.

Cost. One release binary with the walk switched on and off between rounds (a temporary flag, not in this PR). Best of 3 runs of 21 rounds, 1M calls per round, ns per call. The machine was busy, so the last digit is noise.

case walk off walk on delta
new Headers(h), 1 header 48.1 57.7 +9.6
new Headers(h), 8 headers 209.6 216.1 +6.5
new Headers(subclassInstance), 8 headers 215.7 240.0 +24.3
new Response(null, { headers }), empty 219.2 233.9 +14.7
new Response(null, { headers }), 1 header 284.2 304.7 +20.5
new Response(null, { headers }), 8 headers 453.6 478.7 +25.1
new Request(url, { headers }), 8 headers 663.8 684.6 +20.8

A plain object or an array does not reach the check. The walk costs more inside new Response than in the new Headers loop. It reads about ten cache lines (the Headers.prototype structure, its property table, the function, its executable), and they stay hot only in the small loop.

The first version used a PropertySlot::VMInquiry lookup. In the same kind of one-binary comparison it cost 17 to 23 ns on new Headers(h), against 10 ns for the walk, so this PR has the walk. A watchpoint on Headers.prototype[Symbol.iterator] would make the common case a pointer compare. It needs per-realm state and adaptive-watchpoint plumbing that the bindings do not use anywhere today, so it is not part of this PR.

Not changed: server.fetch(). It has the same shortcut (server_body.rs:2310). #40888 rewrites that block to fix a double free, and a change here would conflict with it. The same one-token change applies after #40888 lands.

Not changed: a Request or Response given as the init object (new Response(body, response), new Request(url, request)). That shortcut (Init::init, Request::construct_into) copies the fields of the platform object and skips every getter of the dictionary, headers included. Node reads response.headers there and converts it. It is a different shortcut with a different precondition, and it never looks at a Headers object.

WPT. headers-basic.any.js is byte-identical to upstream (blob ead1047645a1), with a driver that copies textstream-wpt.test.ts. The other files of fetch/api/headers/ are not vendored here. They fail for reasons that this PR does not touch (forbidden header names, Set-Cookie iteration order), or they need setup(), which the shim does not have.

Dead code. FetchHeaders::cast has no caller left, so this PR removes it. fetch_headers_from_js in server_body.rs loses its unused global parameter. The new FFI entry point is in JSFetchHeaders.cpp, not in bindings.cpp.

Self-review. Five concerns, four addressed:

  • the overlap with server.fetch: copy a Headers argument instead of adopting the wrapper's pointer #40888 in server.fetch() (that site is now left out)
  • server.upgrade() threw for a Headers object whose iterator yields nothing (fixed, with a test)
  • a hand-typed copy of the WPT subtest (replaced with the vendored file)
  • the demand signal (the Remix report is now cited)
  • FetchHeaders::cast_ as a boolean predicate (not done: server.fetch() still uses the pointer)

Suites run on the debug ASAN build. headers.test.ts and wpt/headers-basic-wpt.test.ts (139 pass, also under BUN_JSC_validateExceptionChecks=1), headers.undici.test.ts, headers-case.test.ts, fetch_headers.test.js, fetch-args.test.ts, fetch-session.test.ts, response.test.ts, body.test.ts, body-clone.test.ts, request.test.ts, request-subclass.test.ts, test/js/deno/fetch/{headers,request,response}.test.ts, websocket-proxy.test.ts, websocket-custom-headers.test.ts, proxy.test.ts, test/js/first_party/ws/ws.test.ts: all pass. websocket-server.test.ts and bun-server.test.ts have tests that spawn a client and time out when the whole file runs on this loaded machine. The set changes between runs, and the tests pass in smaller runs with -t.


[human-review] gate passed · iteration 0 · 12 files touched

fails on main (without fix)
ASAN without fix: 16 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/js/web/fetch/headers.test.ts test/js/web/fetch/wpt/headers-basic-wpt.test.ts
bun test v1.4.3 (c6b7fcb5b)

test/js/web/fetch/headers.test.ts:
(pass) Headers > constructor > can create headers from no arguments [4.09ms]
(pass) Headers > constructor > cannot create headers from null [3.70ms]
(pass) Headers > constructor > can create headers from empty object [2.30ms]
(pass) Headers > constructor > can create headers from object [1.65ms]
(pass) Headers > constructor > deleted key in header constructor is not kept [2.52ms]
(pass) Headers > constructor > constructing headers from an object interleaves Get with value conversion [4.80ms]
(pass) Headers > constructor > constructing headers from an object with a getter interleaves Get with value conversion [6.04ms]
(pass) Headers > constructor > constructing headers from an object observes a getter installed by an earlier value's toString [8.70ms]
(pass) Headers > constructor > constructing headers from an object propagates an exception from a getter installed by an earlier value's toString 
... (truncated)

release without fix: all passed
bun test v1.4.3-canary.1 (a808385d7)

test/js/web/fetch/headers.test.ts:
(pass) Headers > constructor > can create headers from no arguments [0.06ms]
(pass) Headers > constructor > cannot create headers from null [0.06ms]
(pass) Headers > constructor > can create headers from empty object [0.02ms]
(pass) Headers > constructor > can create headers from object [0.03ms]
(pass) Headers > constructor > deleted key in header constructor is not kept [0.04ms]
(pass) Headers > constructor > constructing headers from an object interleaves Get with value conversion [0.09ms]
(pass) Headers > constructor > constructing headers from an object with a getter interleaves Get with value conversion [0.08ms]
(pass) Headers > constructor > constructing headers from an object observes a getter installed by an earlier value's toString [0.14ms]
(pass) Headers > constructor > constructing headers from an object propagates an exception from a getter installed by an earlier value's toString [0.08ms]
(pass) Headers > constructor > constructing headers from an object keeps own-property semantics after setPrototypeOf mid-conversion [0.07ms]
(pass) Headers > constructor > can create headers from 
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/js/web/fetch/headers.test.ts test/js/web/fetch/wpt/headers-basic-wpt.test.ts
bun test v1.4.3 (c6b7fcb5b)

test/js/web/fetch/headers.test.ts:
(pass) Headers > constructor > can create headers from no arguments [5.29ms]
(pass) Headers > constructor > cannot create headers from null [4.11ms]
(pass) Headers > constructor > can create headers from empty object [2.40ms]
(pass) Headers > constructor > can create headers from object [1.65ms]
(pass) Headers > constructor > deleted key in header constructor is not kept [2.52ms]
(pass) Headers > constructor > constructing headers from an object interleaves Get with value conversion [4.82ms]
(pass) Headers > constructor > constructing headers from an object with a getter interleaves Get with value conversion [5.76ms]
(pass) Headers > constructor > constructing headers from an object observes a getter installed by an earlier value's toString [8.13ms]
(pass) Headers > constructor > constructing headers from an object propagates an exception from a getter installed by an earlier value's toString 
... (truncated)

release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 1737ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/25] cxx obj/unified/UnifiedSource-src_jsc_bindings-2.cpp.o
[2/25] cxx obj/unified/UnifiedSource-src_jsc_bindings_webcore-3.cpp.o
[3/25] cxx obj/unified/UnifiedSource-src_jsc_modules-0.cpp.o
[4/25] cxx obj/unified/UnifiedSource-src_jsc_bindings-4.cpp.o
[5/25] cxx obj/unified/UnifiedSource-src_jsc_bindings-0.cpp.o
[6/25] cxx obj/unified/UnifiedSource-src_jsc_bindings_webcore-2.cpp.o
[7/25] cxx obj/unified/UnifiedSource-src_jsc_bindings_node-0.cpp.o
[8/25] cxx obj/src/jsc/bindings/bindings.cpp.o
[9/25] gen generated_host_exports.rs
generated_host_exports.rs: 121 exports (host=5, lazy=10, generic=106, rust=0); 242 extern-C blocks audited
[10/25] cxx obj/src/jsc/bindings/BunObject.cpp.o
[11/25] cxx obj/unified/UnifiedSource-src_jsc_bindings-1.cpp.o
[12/25] cxx obj/src/jsc/bindings/BunProcess.cpp.o
[13/25] cxx obj/src/jsc/bindings/napi.cpp.o
[14/25] cxx obj/unified/UnifiedSource-src_jsc_bindings-3.cpp.o
[15/25] cxx obj/unified/UnifiedSource-src_jsc_bindings_webcore-1.cpp.o
[16/25] cxx obj/codegen/JSSink.cpp.
... (truncated)
diff hotspot
src/jsc/FetchHeaders.rs                         |  10 +-
 src/jsc/bindings/headers.h                      |   1 +
 src/jsc/bindings/webcore/JSFetchHeaders.cpp     |  35 ++-
 src/jsc/bindings/webcore/JSFetchHeaders.h       |   2 +
 src/jsc/bindings/webcore/JSWebSocket.cpp        |   4 +-
 src/runtime/server/server_body.rs               |  33 +--
 src/runtime/webcore/Response.rs                 |   5 +-
 src/runtime/webcore/fetch.rs                    |   4 +-
 src/runtime/webcore/fetch/FetchSession.rs       |   2 +-
 test/js/web/fetch/headers.test.ts               | 260 ++++++++++++++++++++++
 test/js/web/fetch/wpt/headers-basic-wpt.test.ts |  31 +++
 test/js/web/fetch/wpt/headers-basic.any.js      | 275 ++++++++++++++++++++++++
 12 files changed, 630 insertions(+), 32 deletions(-)

gate history · 2 passed · 0 rejected · iteration 0

evidence per changed file
file                                             reads  edits  tests
src/jsc/FetchHeaders.rs                              3      5     25
src/jsc/bindings/headers.h                           1      1     25
src/jsc/bindings/webcore/JSFetchHeaders.cpp          6      5     25
src/jsc/bindings/webcore/JSFetchHeaders.h            2      2     25
src/jsc/bindings/webcore/JSWebSocket.cpp             1      1     25
src/runtime/server/server_body.rs                    6      4     25
src/runtime/webcore/Response.rs                      1      1     25
src/runtime/webcore/fetch.rs                         3      0     25
src/runtime/webcore/fetch/FetchSession.rs            1      1     25
test/js/web/fetch/headers.test.ts                    6      7     25
test/js/web/fetch/wpt/headers-basic-wpt.test.ts      0      1      7
test/js/web/fetch/wpt/headers-basic.any.js           0      0     25

Web IDL converts a HeadersInit through the Symbol.iterator method of the
value. Bun copied the header list of any Headers object and never read
that method, so an override on a subclass, on the instance or on
Headers.prototype had no effect. A Headers subclass that keeps its
entries in its own storage (SuperHeaders in @mjackson/headers) lost every
header in new Response(body, { headers }). WPT covers the conversion in
fetch/api/headers/headers-basic.any.js ("Create headers with existing
headers with custom iterator").

JSFetchHeaders::toWrappedAsInit (FetchHeaders::cast_as_init in Rust)
returns the wrapped headers only when a side-effect-free lookup of
Symbol.iterator finds the built-in Headers.prototype.entries. The places
that took a Headers object as a HeadersInit now use it and fall back to
the generic conversion: new Headers(), Response and Request init,
fetch() headers and proxy.headers, server.upgrade() and the WebSocket
proxy.headers option.

server.upgrade() treated an empty conversion as an invalid value, so
{ headers: {} } threw. An empty HeadersInit now adds no header.

FetchHeaders::cast has no caller left and is removed. The WPT file is
vendored byte-identical with a driver next to textstream-wpt.test.ts.
@robobun

robobun commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status

Reproduced on main (1.4.3-canary, c6b7fcb5b) and compared with Node v26.3.0:

test/js/web/fetch/headers.test.ts (15 of the 17 new tests) and test/js/web/fetch/wpt/headers-basic-wpt.test.ts (1 of 23 subtests) fail on main and pass on this branch.

CI (#116928, b276ab81dd): the diff is green. 180 of 181 jobs pass, and the new tests pass on every Linux, macOS and Windows lane. The one red job is debian 13 x64-asan, where test/js/bun/spawn/spawn.test.ts ("an idle reader stopped at the highwater mark does not keep the process alive") fails. That test fails the same way on the same lane in other PR builds (#116882, #116897, #116918, #116920, #116924), and this PR does not touch Bun.spawn or the pipe reader. The other entries in the build are tests that passed on a retry.

Review: the threads are resolved. Two findings from the review are on main already and have their own trackers (#41875, #43027).

@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 876631e4-680d-4215-9d53-32f3f69d00d6

📥 Commits

Reviewing files that changed from the base of the PR and between 441311a and b276ab8.

📒 Files selected for processing (3)
  • src/jsc/FetchHeaders.rs
  • src/jsc/bindings/webcore/JSFetchHeaders.cpp
  • src/jsc/bindings/webcore/JSFetchHeaders.h

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.


Walkthrough

Changes

The PR adds guarded HeadersInit conversion for wrapped Headers values. Runtime fetch, response, WebSocket, proxy, and server-upgrade paths use the conversion. Tests cover custom iterators, fallback behavior, propagation, and Web Platform Test cases.

HeadersInit conversion and consumers

Layer / File(s) Summary
HeadersInit conversion contract
src/jsc/FetchHeaders.rs, src/jsc/bindings/headers.h, src/jsc/bindings/webcore/JSFetchHeaders.*
Adds the castAsInit FFI path and validates wrapped Headers objects before direct conversion.
Runtime and WebSocket integration
src/jsc/bindings/webcore/JSWebSocket.cpp, src/runtime/webcore/..., src/runtime/server/server_body.rs
Uses guarded conversion across headers consumers. Empty object-derived headers no longer produce upgrade header errors.
Headers behavior tests
test/js/web/fetch/headers.test.ts
Tests custom and built-in iterators across constructors, fetch, proxy requests, WebSocket connections, and server upgrades.
Web Platform Test coverage
test/js/web/fetch/wpt/*
Adds a WPT driver and tests for construction, mutation, normalization, iteration, and mutation during iteration.

Suggested reviewers: jarred-sumner

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to b276a

The changed HeadersInit paths have guarded fallback and error handling with no established merge-blocking regression.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: honoring overridden Symbol.iterator methods on Headers objects used as HeadersInit.
Description check ✅ Passed The description provides a detailed problem statement, implementation summary, scope, verification results, test coverage, and known limitations. It does not use the template headings exactly, but it …

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot 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.

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: the toWrappedAsInit walk really runs no user code (it bails on overridesGetOwnPropertySlot, static property tables, and overridesGetPrototype before every getDirect/getPrototypeDirect, and an accessor surfaces as a GetterSetter cell that fails the JSFunction cast); the new FFI entry matches parameter-for-parameter across headers.h, JSFetchHeaders.cpp, and FetchHeaders.rs; and the remaining cast_/dynamicDowncast<JSFetchHeaders> users (server.fetch() at server_body.rs:2313, the console formatter, BunInjectedScriptHost, deepEquals) are either not HeadersInit positions or explicitly left out in the description.

Extended reasoning...

Inline findings are being posted on the server.upgrade() sites in src/runtime/server/server_body.rs, so a human look is already signaled. This note only records what else was examined and ruled out from reading the diff: the prototype-chain walk in src/jsc/bindings/webcore/JSFetchHeaders.cpp (toWrappedAsInit) is gated so getDirect and getPrototypeDirect never reach an object that can answer from outside its Structure; the safe fn extern declaration in src/jsc/FetchHeaders.rs matches the C++ definition and the headers.h prototype; and the sibling-site grep shows the only HeadersInit-position shortcut not converted is server.fetch(), which the description explicitly defers to another PR. None of this is a guarantee of correctness of the change as a whole.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟣 src/runtime/server/server_body.rs — Callers of server.upgrade(req, { headers }) who pass their own Headers object with Sec-WebSocket-Protocol set have that header silently deleted from their object after the call. The copy path at server_body.rs:1727 hands the caller's live FetchHeaders to fast_remove at server_body.rs:1754 and :1763 (and :1963, :1967 at the second site), so a Headers reused across upgrades sends the protocol only on the first one. Fix: never mutate a user-owned FetchHeaders; clone the list (clone_this) when cast_as_init returns a borrowed pointer, or read the two values and skip them in to_uws_response instead of removing them.

    Extended reasoning...

    The base branch does this too on the same lines (verified from the diff: the fast_remove lines are unchanged context), which is why it was dismissed. But this PR is the change that rewires which objects reach those lines and the base already carries the comment 'Copied out because fast_remove frees the entry' without addressing that the entry belongs to the user. Step 1: const h = new Headers({ 'Sec-WebSocket-Protocol': 'chat' }) at module scope, reused per connection. Step 2: server.upgrade(req, { headers: h }) — cast_as_init at :1727 returns the borrowed list. Step 3: fast_get then fast_remove at :1750-1754 removes Sec-WebSocket-Protocol from h. Step 4: the first client gets the protocol; every later client upgraded with the same h gets no Sec-WebSocket-Protocol header, and a browser client that requested a subprotocol fails the handshake. Population: every Bun.serve and node:http WebSocket server that reuses a Headers object; rate is once per connection. Remedy: clone_this before mutation, or filter the two names during to_uws_response without mutating.

    Verification: pre-existing. Trigger: a caller passes a plain Headers object (built-in Symbol.iterator) containing Sec-WebSocket-Protocol / Sec-WebSocket-Extensions to server.upgrade(req, { headers }) and reuses that object afterwards. Mechanism verified: JSFetchHeaders::toWrappedAsInit (src/jsc/bindings/webcore/JSFetchHeaders.cpp, new code) returns &wrapper->wrapped() — the JS object's own live…

Comment thread src/runtime/server/server_body.rs
@robobun

robobun commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

On the two findings of the review, both of which are on main already:

No change to this PR for either one.

Comment thread src/jsc/FetchHeaders.rs Outdated
Comment thread src/jsc/bindings/webcore/JSFetchHeaders.cpp Outdated
Comment thread src/jsc/bindings/webcore/JSFetchHeaders.h Outdated

@claude claude Bot 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.

Code review found no issues

No high-confidence issues detected in this change.

@robobun

robobun commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 2:02 AM PT - Sep 17th, 2026

❌ @robobun, your commit b276ab8 has 1 failures in Build #116928 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 43023

That installs a local version of the PR into your bun-43023 executable, so you can run:

bun-43023 --bun

steipete pushed a commit to steipete/bun that referenced this pull request Sep 21, 2026
Backport the complete HeadersInit guard and caller cutover from
oven-sh#43023 at b276ab8 onto
the fb7c integration line. The older source keeps FetchSession proxy parsing
inline in fetch.rs, so map that caller there.

Apply the same guarded conversion to server.fetch and clone its native header
list for Request ownership. This preserves the caller's Headers snapshot and
incorporates the matching ownership correction from oven-sh#40888 without
adopting the JS wrapper's borrowed reference.
steipete added a commit to steipete/bun that referenced this pull request Sep 21, 2026
Add Unreleased notes for the five-commit integration range from
fb7c3a5 through
3ff0efc.

Runtime code, tests and build inputs remain unchanged from 3ff0efc.
Its Linux optimized and Debug/ASAN selections each passed 560 cases and
four programs, with four existing skips and 184 filter exclusions.
The canonical Rust cross-target check passed all 12 targets with no skips.

Retain implementation credit for robobun's Headers iterator work in
oven-sh#43023 and Jarred Sumner's server.fetch header-copy correction
in oven-sh#40888, both incorporated by f5234e3.
steipete pushed a commit to openclaw/bun that referenced this pull request Sep 30, 2026
Backport the complete HeadersInit guard and caller cutover from
oven-sh#43023 at b276ab8 onto
the fb7c integration line. The older source keeps FetchSession proxy parsing
inline in fetch.rs, so map that caller there.

Apply the same guarded conversion to server.fetch and clone its native header
list for Request ownership. This preserves the caller's Headers snapshot and
incorporates the matching ownership correction from oven-sh#40888 without
adopting the JS wrapper's borrowed reference.

(cherry picked from commit f5234e3)
steipete pushed a commit to openclaw/bun that referenced this pull request Sep 30, 2026
Backport the complete HeadersInit guard and caller cutover from
oven-sh#43023 at b276ab8 onto
the fb7c integration line. The older source keeps FetchSession proxy parsing
inline in fetch.rs, so map that caller there.

Apply the same guarded conversion to server.fetch and clone its native header
list for Request ownership. This preserves the caller's Headers snapshot and
incorporates the matching ownership correction from oven-sh#40888 without
adopting the JS wrapper's borrowed reference.

(cherry picked from commit f5234e3)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant