Skip to content

fetch, Request, server.fetch: stop sending GET for a method outside the method table - #44718

Open
robobun wants to merge 1 commit into
mainfrom
robobun/abc564cf/fetch-method-token
Open

robobun wants to merge 1 commit into
mainfrom
robobun/abc564cf/fetch-method-token

Conversation

@robobun

@robobun robobun commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #42497

Problem

  • fetch(), new Request() and server.fetch() silently send GET for a method that is not an all-upper or all-lower entry of the method table ("PatCh", "Put", "BREW").
  • Cause: a table miss becomes GET in fetch_impl (fetch.rs:615), response::Init::init (Response.rs:1303) and on_fetch (server_body.rs:2303). The closed Method enum cannot hold another token.

Fix

  • One reader, method_jsc::request_method_from_init, replaces them. After a miss, Fetch's six normalized verbs match in any case, another token is kept as written, and a non-token, TRACK, Trace or Connect is a TypeError.
  • Request stores an OwnedMethod (verb or token bytes). The HTTP client holds a Copy MethodRef, which build_request writes. HTTP/3 rejects a method over 65,535 bytes.
  • Verified: six test files fail on main, pass here. Self-reviewed: 12 concerns raised, 11 addressed (Notes).

Background

Downsides

  • Behaviour change on a released API (Notes): false is sent as false, non-tokens throw, new Response(body, { method }) no longer gives a Request its method, and a token sent to Bun.serve fails (Custom HTTP methods via Bun.serve() #6556).
  • Request grows 104 to 112 bytes (same mimalloc block). .method costs 44 instructions (41), finalize 137 (124). .text +5,888 bytes.
Notes

For a maintainer to decide

  1. The token carry. OwnedMethod (on Request) and MethodRef (in FetchOptions, AsyncHTTP, HTTPClient) are new types in bun_http_types, bun_http_jsc, bun_http and bun_runtime. fetch: stop replacing unrecognized HTTP methods with GET #33469 and fix: don't silently convert unknown HTTP methods to GET in fetch() #27265 had the same design and closed with no review. The other way is a larger METHOD_MAP, as in Support MKADDRESSBOOK HTTP method #28955 and Add obscure HTTP methods #3553. A row for LIST fixes that one token and no mixed-case verb. Each row is also accepted by node:http, where Node answers 400.
  2. Three behaviours of main differ from Fetch and are kept:
    • An all-lower table verb is sent in upper case ("patch" as PATCH). Fetch and Node send patch. Bun replaces node-fetch, and openai@4.76.0, cloudflare@3.5.0 and @mux/mux-node@9.0.1 call fetch with method: "patch" (core.js: this.methodRequest('patch', ...)).
    • CONNECT and TRACE in the two spellings of the table are sent. Fetch forbids them (Request and Response constructors skip several validations the Fetch spec requires #42499).
    • "" and null mean no method. Fetch throws for "" and sends null. The bundled undici.request passes method: null.
  3. TRACK, and mixed-case Trace and Connect, were a silent GET and are now a TypeError, which is the Fetch result. It is Bun's first forbidden-method check. A throw can be relaxed later. A method that is sent cannot be taken back.
  4. Which release carries the behaviour change below.

Behaviour change on a released API

method main this PR Node 26.3
"PatCh", "BREW", "LIST" GET as written as written
"Put", "pOsT" GET PUT, POST PUT, POST
false, 0, 123 GET false, 0, 123 false, 0, 123
"GET POST", {}, [] GET TypeError TypeError
"TRACK", "Trace", "Connect" GET TypeError TypeError
"patch" PATCH PATCH patch
"CONNECT", "TRACE" sent sent TypeError
"", null GET GET TypeError, null

The table holds for new Request(url, { method }).method and for the request line of fetch(url, { method }) to a raw socket. More flips:

  • method: cond && "POST" sends false when cond is false. Before, it sent GET.
  • Errors are TypeError with code ERR_INVALID_ARG_VALUE: "GET POST" is not a valid HTTP method. and "TRACK" HTTP method is unsupported. fetch() and server.fetch() reject, and new Request() throws. Nothing is sent.
  • A token may carry a body. With no body it sends no Content-Length. A 301, 302, 307 or 308 keeps the token and the body, and a 303 changes it to GET. A request with a token is not sent again after a reset on a reused connection.
  • fetch("s3://..."): DELETE, GET, HEAD, POST and PUT in any case now reach S3 as that verb. "Delete" deletes the object. "Put" or "pOsT" uploads the body, and with no body writes an empty object. Before, "Delete" and "Put" with no body were a read, and "Put" with a body was a TypeError. Every other method rejects with ERR_S3_INVALID_METHOD and signs nothing. Before, it was a read.
  • A data:, file: or blob: URL rejects a non-token. Before, every method was answered.
  • Response no longer stores a method. response::Init.method existed so that Request could read a method out of Init::init, and it can hold a verb only. Request now reads the method itself. So new Response(body, init) no longer reads init.method (Node does not read it). new Request(url, new Response("x", { method: "POST" })).method was POST and is GET, also for a cloned or HTMLRewriter-transformed Response, and a fetch() of that Request now rejects with "fetch() request with GET/HEAD method cannot have body." new Request(putRequest, new Response(null, { method: "DELETE" })).method was DELETE and is GET (Node: PUT, the subject of Request: take a method from RequestInit only when it has one #41994). For a Response built without a method, main and this PR agree.
  • verbose: "curl" prints the method after -X with no quotes, as it prints header values today with no escaping. A token can hold characters such as a backtick or $. fetch, audit: write the verbose curl line and the audit fix --ignore line as shell words #41726 writes every word of that line as a shell word.
  • The bytes of a token are not counted in the size that Request reports to the GC.

Bun to Bun (Bun.serve handler that returns req.method, client fetch(url, { method }) of this PR)

leg main BREW, custom PatCh PURGE1
HTTP/1.1, plain and TLS 200, handler saw GET rejects ECONNRESET rejects ECONNRESET 400
HTTP/2 200, handler saw GET 501 501 501
HTTP/3 200, handler saw GET 200, handler saw GET 200, handler saw PATCH 200, handler saw GET
server.fetch("/", { method }) handler saw GET handler saw the token handler saw PatCh handler saw PURGE1

So the Bun.serve snippet of #42497, and #6021, still need the server side.

Not in this PR

Sequencing

Measurements (release builds of main 3f1765a and of this diff on it, linux x64. perf, valgrind and bloaty are not in the container. Taken before the review changes, which are 9 lines on an error path of server.fetch and shorter comments)

  • Instructions per call, main to PR (gdb stepi from function entry to return on bun-profile, 5 equal repeats): .method getter 41 to 44. new Request(url) 781 to 776. new Request(url, { method: "POST" }) 3719 to 3735. new Request(request, { method: "POST" }) 5542 to 5559. Table lookup for "POST" 183 to 183. HTTPClient::build_request for a GET 116 to 120. RequestClass__finalize 124 to 137.
  • One Bun.serve request (server::trampoline::on_request, production mode, lowest repeated value of 15 samples, runs interleaved): 8191 to 8175 for fetch() { return new Response("Hello") }, 8227 to 8214 when the handler reads req.method.
  • size_of::<Request>() 104 to 112 bytes (assert_ffi_layout!). mimalloc serves 97 to 112 bytes from its 112-byte block. estimateShallowMemoryUsageOf(new Request("http://a/")) 193 to 201.
  • gdb sizeof, debug build: AsyncHTTP 2208 to 2224 (the same 2560-byte mimalloc block), HTTPClient inside it 1680 to 1688, FetchOptions 936 to 944, FetchTasklet 1128 and Response 496 unchanged. MethodRef is 12 bytes and OwnedMethod is 16.
  • Release binary: .text 58,536,037 to 58,541,925 bytes (+5,888), file +4,096 bytes (size -A). Copies of the method compare tree in .text: 3 to 3.
  • mimalloc calls for 3000 table-verb operations (1000 new Request POST, 1000 clone(), 1000 fetch POST), two interleaved runs each: 46,548 and 47,959 on main, 46,459 and 46,491 with the PR. The count moves more between runs than between builds.
  • Request heads for the 72 table spellings plus undefined, "" and null: 0 of 75 differ from main. Sockets opened for fetch(url, { method: "GET POST" }): 1 to 0.
  • Wall-clock, bench/snippets/request-method-getter.mjs, 12 interleaved runs per binary on 2 pinned CPUs: request.clone().method 251 ± 23 ns to 253 ± 23 ns, new Request(url).method 309 ± 17 ns to 306 ± 27 ns. Bun.serve hello world under ab -k -c 50, 10 interleaved runs: 22,178 ± 1,514 to 22,331 ± 1,516 requests per second. Both deltas are below the noise.
  • Outside the fetch path: 0 lines in src/install, src/runtime/cli, the S3 client, src/standalone_graph and packages/.

Tests (each file fails on main's debug build and passes here)

  • test/js/web/request/request.test.ts: 66 of 87 tests fail on main.
  • test/js/web/fetch/fetch-args.test.ts: 27 of 133. A raw-socket server, six native entry points and node-fetch, an HTTP proxy, HTTP/2 with a node:http2 server, s3: with the in-process S3 server, data:, file: and blob:.
  • test/js/web/fetch/fetch-redirect.test.ts: 5 of 35.
  • test/js/web/fetch/fetch-http3-client.test.ts: 2 of 69. A node:quic origin reads :method.
  • test/js/bun/http/proxy-stress-matrix.test.ts: 8 of 343. BREW and PatCh through an HTTP and an HTTPS proxy to an HTTP and an HTTPS origin, which covers the CONNECT tunnel.
  • test/js/bun/http/bun-serve-fetch-invalid-args.test.ts: 3 of 17.
  • Unit tests in src/http_types/Method.rs under miri (cargo miri test -p bun_http_types --lib -- Method, 6 pass).
  • Also run: clippy for the touched crates, cargo fmt, test/internal/source-lints/, BUN_JSC_validateExceptionChecks=1 on three of the files, cargo check for x86_64-pc-windows-msvc, aarch64-apple-darwin and aarch64-unknown-linux-musl, and 43 more test files under test/js/web/fetch, test/js/web/request, test/js/bun/http, test/js/workerd, test/js/node/http and test/js/first_party/undici. Every failure there is a timeout that main's debug build has too.
  • Not run: macOS and Windows. WPT's fetch method tests are not in the repository.

Self-review

Two rounds. The first asked for a rework of the first cut: a plain enum in place of a bit-packed method slot, no change to the presence rule of #41994, a TypeError for TRACK, and more coverage. The second raised 12 concerns on this diff. 11 are addressed: the title, the docs line and the root-cause lines, the s3: rows, the scheme test, the HTTP/3 check cut down to the method, an origin of its own for the over-long method test, the field order of the Request literals, the server.fetch test calls, and a tracker for each item of "Not in this PR". One is not taken: an issue for the HTTP/2 UnableToEncode observation, because requests in flight complete and nothing is lost.


no test proof · iteration 3 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/web/fetch/fetch-http3-client.test.ts

@robobun

robobun commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status

Reproduced on main 3f1765a (release build, linux x64) with a raw TCP server, so that the request line is visible:

import net from "node:net";
const lines = [];
const server = net.createServer(socket => {
  socket.on("data", data => {
    lines.push(String(data).split("\r\n")[0]);
    socket.end("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n");
  });
});
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
const url = `http://127.0.0.1:${server.address().port}/`;
for (const method of ["PatCh", "Put", "BREW"]) {
  await (await fetch(url, { method })).arrayBuffer();
  console.log(JSON.stringify(method), "->", lines.at(-1), "| Request.method:", new Request(url, { method }).method);
}
server.close();

main prints GET / HTTP/1.1 and GET for all three. This branch prints PatCh / HTTP/1.1, PUT / HTTP/1.1 and BREW / HTTP/1.1, and the same methods for Request.method.

The six touched test files fail on main's debug build and pass on this branch (counts in the Notes of the description).

State: CI is green on f038c68 and every review thread is resolved. The next step is a maintainer's review. The decisions that are theirs are listed first in the Notes of the description (the two new method types, three behaviours kept from main, and the release for the behaviour change).

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 8487c9ea-435e-429d-8415-699e7b90f418
📥 Commits

Reviewing files that changed from the base of the PR and between bd599f5 and a8dc861.

📒 Files selected for processing (18)
  • docs/runtime/networking/fetch.mdx
  • src/http/AsyncHTTP.rs
  • src/http/h3_client/encode.rs
  • src/http/lib.rs
  • src/http_jsc/method_jsc.rs
  • src/http_types/Method.rs
  • src/runtime/api/html_rewriter.rs
  • src/runtime/server/server_body.rs
  • src/runtime/webcore/Request.rs
  • src/runtime/webcore/Response.rs
  • src/runtime/webcore/fetch.rs
  • src/runtime/webcore/fetch/FetchTasklet.rs
  • test/js/bun/http/bun-serve-fetch-invalid-args.test.ts
  • test/js/bun/http/proxy-stress-matrix.test.ts
  • test/js/web/fetch/fetch-args.test.ts
  • test/js/web/fetch/fetch-http3-client.test.ts
  • test/js/web/fetch/fetch-redirect.test.ts
  • test/js/web/request/request.test.ts
💤 Files with no reviewable changes (1)
  • src/runtime/api/html_rewriter.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Walkthrough

The pull request adds support for custom HTTP method tokens across Request construction and fetch transport. It classifies invalid and forbidden methods, preserves valid token spelling, updates method handling in HTTP clients, and adds tests for request construction, redirects, and HTTP protocols.

Changes

HTTP Method Token Support

Layer / File(s) Summary
Method representation and parsing
src/http_types/Method.rs, src/http_jsc/method_jsc.rs, docs/runtime/networking/fetch.mdx
Method parsing distinguishes normalized verbs, valid custom tokens, forbidden methods, and invalid values. Owned methods retain custom token bytes. The fetch documentation describes invalid-method errors and empty-string handling.
Request construction and server fetch
src/runtime/webcore/Request.rs, src/runtime/webcore/Response.rs, src/runtime/server/server_body.rs, src/runtime/api/html_rewriter.rs, test/js/web/request/request.test.ts, test/js/bun/http/bun-serve-fetch-invalid-args.test.ts
Request stores owned methods and preserves custom tokens through construction and cloning. Server fetch uses the shared method parser. Response initialization no longer stores or copies a method. Tests cover method normalization, token preservation, and errors.
Fetch and HTTP transport
src/runtime/webcore/fetch.rs, src/runtime/webcore/fetch/FetchTasklet.rs, src/http/AsyncHTTP.rs, src/http/lib.rs, src/http/h3_client/encode.rs, test/js/web/fetch/fetch-args.test.ts, test/js/web/fetch/fetch-http3-client.test.ts, test/js/web/fetch/fetch-redirect.test.ts, test/js/bun/http/proxy-stress-matrix.test.ts
Fetch passes method references with the request header buffer to HTTP clients. Body framing and S3 handling use known-method checks. HTTP/3 rejects methods longer than u16::MAX bytes. Tests cover method transmission, bodies, redirects, proxies, S3, and HTTP/3.

Suggested reviewers: jarred-sumner, dylan-conway

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to a8dc8

This change stops fetch and Request from silently sending GET for methods outside the method table, and it preserves valid custom method tokens. No merge-blocking problem was found in the reviewed changes.

Pre-merge checks: Passed 4
Passed 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 identifies the main change: preventing fetch, Request, and server.fetch from silently sending GET for methods outside the method table.
Description check Passed The description explains the problem, implementation, behavior changes, scope, testing, performance impact, and known limitations. It does not use the exact template headings, but it provides the requ…
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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.

Beyond the inline findings, I also checked the token's lifetime on the HTTP thread: MethodRef::Token is an offset/length into headers.buf, so later appends to that buffer (proxy/auth headers) do not invalidate it, the detach_lifetime in build_request borrows the same header_buf the header values already borrow, and the retry-after-reset path restores the method from the JS-side copy over the same buffer. The redirect arm keeps a token on 301/302/307/308 and rewrites to GET on 303 as described, and the HTTP/2 encoder has no 16-bit field limit, so the H3-only guard is the right scope for the method itself.

Extended reasoning...

The change replaces the closed Method enum with OwnedMethod/MethodRef across fetch, Request, server.fetch, the HTTP/1-2-3 client and S3 signing, and is a user-visible behaviour change on a released API (false/0 sent as tokens, TypeErrors for non-tokens and TRACK/Trace/Connect, Response no longer feeding a method into Request). It touches unsafe lifetime detachment on the request header buffer and the request line that goes on the wire, which I traced as offset-based and sound. With three confirmed inline findings plus two unposted verified ones, and open design questions the author explicitly leaves to a maintainer, a human review is required rather than approval.

Findings marked 🟡 are optional suggestions and need no follow-up push.

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

  • 🟡 src/runtime/webcore/Request.rs — nit: the GC is told nothing about the bytes a token method holds, so a Request built with a very long method is under-reported to the collector after merging. calculate_estimated_byte_size (src/runtime/webcore/Request.rs:463-467) and memory_cost (src/runtime/webcore/Request.rs:374-379) sum body, url and size_of::<Request>() only, while OwnedMethod::Token now owns a heap Box<[u8]> of user-chosen length. Fix: add the token's length (0 for Known) to both sums so every owning field of Request is reported, as the url already is.

    Why this was flagged

    new Request(url, { method }) with a long token string, or fetch(url, { method }) through Request::init2 (src/runtime/server/server_body.rs:2341), stores the method as OwnedMethod::Token(Box<[u8]>) (src/http_jsc/method_jsc.rs:60). calculate_estimated_byte_size at src/runtime/webcore/Request.rs:463-467 and memory_cost at src/runtime/webcore/Request.rs:374-379 never add that allocation, so JSC's reported extra memory for the wrapper omits it. On the base branch method is a 1-byte enum and nothing is owned, so no accounting is lost there. The PR text itself notes the bytes are not counted; no guard elsewhere adds them.

    Verification: request_method_miss at /home/claude/bun/src/http_jsc/method_jsc.rs:62 does OwnedMethod::Token(Box::from(utf8.slice())) for any RFC 9110 token of any length. Neither memory_cost (Request.rs:374-379) nor calculate_estimated_byte_size (Request.rs:462-467) includes it. On the base branch method: Method was an inline 1-byte enum, so the omission is new with this change.

Comment thread src/http/h3_client/encode.rs Outdated
Comment thread src/runtime/server/server_body.rs Outdated
@robobun

robobun commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 2:38 PM PT - Oct 14th, 2026

✅ @robobun, your commit f038c68f7a4138b2c0e7cf487c66d75868cc1c53 passed in Build #125572! 🎉


🧪   To try this PR locally:

bunx bun-pr 44718

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

bun-44718 --bun

Comment thread src/http/AsyncHTTP.rs Outdated
Comment thread src/http/h3_client/encode.rs Outdated
Comment thread src/http/lib.rs Outdated
Comment thread src/http/lib.rs Outdated
Comment thread src/http_jsc/method_jsc.rs Outdated
Comment thread src/http_jsc/method_jsc.rs Outdated
Comment thread src/http_jsc/method_jsc.rs Outdated
Comment thread src/http_jsc/method_jsc.rs Outdated
Comment thread src/http_jsc/method_jsc.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/http_types/Method.rs Outdated
Comment thread src/runtime/webcore/Request.rs Outdated
Comment thread src/runtime/webcore/Request.rs Outdated
Comment thread src/runtime/webcore/fetch.rs Outdated
Comment thread src/runtime/webcore/fetch.rs 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.

I re-reviewed the new push and found no new bugs; because this changes the wire behaviour of a released API and introduces two cross-crate types (OwnedMethod/MethodRef) that the description itself asks a maintainer to rule on, a human look is still worthwhile.

What was reviewed:

  • The push routes a server.fetch() method error through rejected_promise_with_caught_exception, which closes the synchronous-throw point from the earlier review; fetch()'s new data: rejection also goes through reject_on_exception, so neither entry point throws.
  • Token lifetime: the method is appended to request_headers.buf right before FetchOptions is built, nothing touches that buffer before AsyncHTTP::init takes it as headers_buf, and HTTPClient.header_buf is never reassigned, so the detach_lifetime in build_request and the is_in assert hold.
  • OwnedMethod::Token ownership on Request: the bail! paths drop the local Request normally, clone_into's ptr::write only overwrites the GET seed (no Box leak), and Box::from_raw in finalize frees it.
  • The u32::try_from(...).unwrap() in store_token mirrors Headers::append and a single JS string cannot exceed u32, so it is not a new reachable panic; the HTTP/3 length guard is unchanged from the earlier push.
Extended reasoning...

The change replaces the closed Method enum fallback-to-GET with OwnedMethod on Request and a Copy MethodRef (a StringPointer into the request header buffer) in FetchOptions/AsyncHTTP/HTTPClient, adds is_token/classify_miss validation that throws TypeError for non-tokens and forbidden methods, removes Response::Init.method, and adds tests across fetch, Request, server.fetch, redirects, proxies and HTTP/3. The security-relevant surface is request-line injection, which is_token (RFC 9110 tchar set, unit-tested over all 256 bytes) now rejects before anything is sent. The earlier run's server.fetch sync-throw finding is addressed in this push; the HTTP/3 pre-existing truncation note is unchanged. Deferring rather than approving because the diff is ~1,400 lines across the HTTP client, Request layout and S3 paths, changes behaviour on a released API, and the author explicitly requests a maintainer's ruling on the new type design.

Still open from earlier reviews (1):

  • 🔴 src/http/h3_client/encode.rs:53 — pre-existing: over HTTP/3 a header value, :path or a batch of headers whose bytes exceed 65,535 still goes out truncate…

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

@robobun

robobun commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

On the optional finding about Request::memory_cost and calculate_estimated_byte_size: not taken for now.

calculate_estimated_byte_size runs once for each Request wrapper, so once for each Bun.serve request, and such a request always holds a verb of the table. A token is the method name that the caller wrote, a few bytes in practice. The count would add a branch to every request for a number below one allocator size class. The description says that these bytes are not counted.

If a maintainer wants them counted, it is one match on self.method in both sums.

…he method table

A method string that was not an exact all-upper or all-lower entry of the
method table became GET with no error, in fetch(), new Request() and
server.fetch().

One reader now takes the method of a request from JS
(method_jsc::request_method_from_init). A table hit is that verb, as
before. After a miss, DELETE, GET, HEAD, OPTIONS, POST and PUT match in
any case, every other RFC 9110 token is kept as written, and a string
that is not a token throws a TypeError. So do TRACK, and CONNECT and
TRACE in mixed case.

Request.method is an OwnedMethod: a verb, or the bytes of a token. The
HTTP client holds a Copy MethodRef whose token is a StringPointer into
the request header buffer, and build_request writes it to the request
line for HTTP/1.1, a proxy, HTTP/2 and HTTP/3.

Response no longer stores a method. Request read its method from
response::Init, which can hold a verb only.

The HTTP/3 client rejects a method over 65,535 bytes. QPACK takes a
16-bit length, and a longer method would go out as its first bytes.

Kept as before: all-lower table verbs are upper-cased, CONNECT and TRACE
in the spellings of the table are sent, and "" and null name no method.
@robobun
robobun force-pushed the robobun/abc564cf/fetch-method-token branch from 9f54c1f to f038c68 Compare October 14, 2026 20:51
@robobun

robobun commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

The comment check flagged 23 comment groups that this PR added under src/. In f038c68 each one is a single line or is gone, and its thread is resolved. The doc comment of method_jsc::from_js is the one from main again. The check's own scan of the new diff finds no group of two or more added comment lines.

@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 completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

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.

2 participants