Raise the HTTP/1 line limit to 64 KiB and expose header limits on Transport - #1365
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1365 +/- ##
==========================================
+ Coverage 89.70% 89.72% +0.01%
==========================================
Files 31 31
Lines 12675 12680 +5
==========================================
+ Hits 11370 11377 +7
+ Misses 1305 1303 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…nsport The HTTP/1 client failed any response with a line longer than 8 KiB (`ProtocolError: HTTP/1 line exceeds configured max_line_bytes`), and nothing public could raise the limit: `_HTTP1_DEFAULT_MAX_LINE_BYTES` was hard-coded, `_read_transport_incoming_response` fell back to it at both call sites, and `Transport` had no keyword for either limit. Header lines that long occur in practice (a 9,695-byte Content-Security-Policy was reported), and curl and Python accept them. - Raise `_HTTP1_DEFAULT_MAX_LINE_BYTES` from 8 KiB to 64 KiB (Python's `http.client` `_MAXLINE`). The 1 MiB header-block limit is unchanged and still bounds memory. The constant is shared with the server-side request parser, so its per-line limit moves too. - Add `max_line_bytes` and `max_header_bytes` keywords to `Transport`, validated positive with `max_line_bytes <= max_header_bytes`; `max_line_bytes` defaults to `min(64 KiB, max_header_bytes)`. - Plumb the transport's limits into `_read_transport_incoming_response` at both call sites instead of the module constants. - Document the knobs in the `Transport` docstring, the client guide, and the changelog; add raw-TCP mock-origin tests for the 9,695-byte line, the boundary at the new default, lowering/raising via the keyword, the header-block limit, and constructor validation. Fixes #1362 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
quinnj
force-pushed
the
fix/h1-max-line-bytes
branch
from
September 14, 2026 21:42
fc2bbcd to
a1d25b6
Compare
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The HTTP/1 client failed any response containing a line longer than 8 KiB:
Header lines that long occur in practice (a 9,695-byte
Content-Security-Policywas seen in the wild), curl and Python read such responses without complaint, and there was no way to raise HTTP.jl's limit:_HTTP1_DEFAULT_MAX_LINE_BYTES = 8 * 1024was hard-coded insrc/http1.jl,_read_transport_incoming_responseinsrc/http_transport.jltookmax_line_bytes/max_header_byteswith those defaults, both call sites passed nothing, andTransporthad no keyword for either limit (HTTP.Transport(max_line_bytes = ...)was aMethodError).Change
_HTTP1_DEFAULT_MAX_LINE_BYTESis now 64 KiB (Python'shttp.client_MAXLINE). The 1 MiB header-block limit (_HTTP1_DEFAULT_MAX_HEADER_BYTES) is unchanged and still bounds memory. The constant is shared with the server-side request parser (read_request), so the server's per-line limit for request lines and request header lines moves to 64 KiB as well; itsmax_header_bytesblock limit (1 MiB default, configurable onServer) is untouched.Transportgainsmax_line_bytesandmax_header_byteskeywords (stored as fields, documented in theTransportdocstring and the client guide). Both must be positive andmax_line_bytesmay not exceedmax_header_bytes(a longer line can never be accepted past the block limit, so that combination is rejected as dead configuration).max_line_bytesdefaults tomin(64 KiB, max_header_bytes), soTransport(max_header_bytes = 16 * 1024)alone works and pulls the per-line limit down with it._read_transport_incoming_responsereads both limits directly from its transport, including when it consumes informational heads and chunked trailers.[Unreleased](### Addedfor the keywords,### Changedfor the default bump).Client(; ...)does not forward generic transport options (onlylocal_addrfolds into the default transport), so callers tune these viaHTTP.Client(transport = HTTP.Transport(max_line_bytes = 128 * 1024)).Tests
New tests in
test/http_client_transport_tests.jlusing a raw-TCP mock origin (skipped on Windows CI like the file's other raw-socket tests):Content-Security-Policyheader line succeeds with defaults; the same response fails withProtocolError(_PROTOCOL_ERROR_LINE_TOO_LONG) underTransport(max_line_bytes = 8 * 1024);ProtocolError, andTransport(max_line_bytes = 128 * 1024)accepts it;Transport(max_header_bytes = 4 * 1024)rejects a ten-line header block with_PROTOCOL_ERROR_HEADERS_TOO_LARGEwhile the default accepts it;max_header_bytes-only lowering, zero/negative values andmax_line_bytes > max_header_bytesthrowArgumentError.Also verified end to end with the issue's reproducer through
HTTP.getagainst a raw-socket origin: a 9,695-byte header line returns 200 (it failed before), a line over 64 KiB still raises the sameProtocolError, and theTransportkeyword lowers and raises the limit as expected. FullPkg.testrun locally on Julia 1.12.6.Fixes #1362
🤖 Generated with Claude Code
Review validation: added end-to-end checks for limits after 103 Early Hints and on chunked trailers. The transport and test-policy suites pass locally.
Co-authored by Codex