Skip to content

fix(ext/fetch): raise default HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE to 256KB - #36558

Open
bartlomieju wants to merge 1 commit into
denoland:mainfrom
bartlomieju:fix/http2-max-header-list-size
Open

fix(ext/fetch): raise default HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE to 256KB#36558
bartlomieju wants to merge 1 commit into
denoland:mainfrom
bartlomieju:fix/http2-max-header-list-size

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

Fixes #36462.

Problem

fetch()ing certain HTTP/2 responses fails with TypeError: fetch failed
(stream error detected: unspecific protocol error detected) while the same
request works with curl and Node.

Root cause

hyper defaults the HTTP/2 client's SETTINGS_MAX_HEADER_LIST_SIZE to 16KB.
When a server sends a response with a large header block (e.g. a long
content-security-policy header, or many set-cookie / x-* headers), the
decoded header list exceeds 16KB and the h2 layer resets the stream with a
PROTOCOL_ERROR before the response ever reaches JavaScript.

Confirmed with h2 tracing on the reproducer from the issue:

h2::frame::headers  - load_hpack; header list size over max
h2::proto::streams::recv - stream error REQUEST_HEADER_FIELDS_TOO_LARGE -- frame is over size
send_reset reason=PROTOCOL_ERROR, initiator=Library

Browsers and curl advertise much larger limits, so these responses work
everywhere except Deno.

Fix

Default the client SETTINGS_MAX_HEADER_LIST_SIZE to 256KB (the value
Chrome advertises) instead of hyper's 16KB, so fetch() interoperates with the
same servers browsers do. Users can still override it via
Deno.createHttpClient({ http2MaxHeaderListSize }).

Tests

  • Enlarged the existing /large_headers test-server route to ~29KB decoded
    (over the old 16KB default, under the 64KB the existing "large enough" test
    uses).
  • Added fetchHttp2LargeHeadersDefault regression test: a default client
    fetching a large-header response now succeeds.

Verified the reproducer URL now returns 200, and that the old binary rejects
the enlarged route while the patched binary accepts it.

… 256KB

hyper defaults the HTTP/2 client's SETTINGS_MAX_HEADER_LIST_SIZE to 16KB.
Responses with large header blocks (e.g. long content-security-policy or
many set-cookie headers) exceed this and get rejected by the h2 layer with
a stream PROTOCOL_ERROR before ever reaching JavaScript, surfacing as
'TypeError: fetch failed'. Browsers and curl allow much larger header
lists, so such responses work everywhere except Deno.

Default the limit to 256KB (matching the value Chrome advertises) so
fetch() interoperates with the same servers browsers do. Users can still
override it via Deno.createHttpClient({ http2MaxHeaderListSize }).

Closes denoland#36462
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.

Deno fetch() fails with certain HTTP/2 responses

1 participant