report the byte limit when response headers exceed it - #9642
Conversation
readUtf8LineStrict() surfaces an oversized response head as a raw okio
EOFException ("\n not found: limit=... content=<hex>"), giving no hint
that the 256 KiB header limit was reached. Translate that case into a
ProtocolException that names the limit, while still rethrowing the
original EOFException when the stream is genuinely truncated before the
limit so truncation is not misreported as an oversized header.
Fixes lysine-dev#9233
|
For anyone reviewing: the failing checks here are pre-existing on
|
kdelay
left a comment
There was a problem hiding this comment.
The rewrite is guarded on "the buffer actually reached the limit", but >= claims one input size that is a genuine truncation.
A header line of exactly HEADER_LIMIT bytes is legal: okio's readUtf8LineStrict(limit) accepts a line of limit bytes followed by CRLF. So a stream that ends after exactly HEADER_LIMIT bytes with no terminator is a truncated legal line, and it now gets the limit message instead of EOFException.
Measured on this branch (aac154e) with a probe test that writes one header line into a Buffer and then reads headers, JDK 17, :okhttp:jvmTest:
truncated line of 262143 bytes -> java.io.EOFException: \n not found: limit=262143 ...
truncated line of 262144 bytes -> java.net.ProtocolException: response headers exceed the 256 KiB limit
truncated line of 262145 bytes -> java.net.ProtocolException: response headers exceed the 256 KiB limit
complete line of 262144 bytes + CRLF CRLF -> OK headers.size=1
complete line of 262145 bytes + CRLF CRLF -> java.net.ProtocolException: response headers exceed the 256 KiB limit
(The limit= in okio's message is min(buffer.size, limit), not the argument.)
Rows 4 and 5 are the control: 262144 bytes parses fine, 262145 is the first size that is genuinely over the limit. That makes row 2 a truncation of a line the parser accepts, which the comment in readLine() says should stay an EOFException. The discriminator wants a strict comparison:
if (source.buffer.size > headerLimit) {With that one character changed, row 2 becomes EOFException: \n not found: limit=262144 and rows 1, 3, 4, 5 are unchanged. HeadersReaderTest still passes as written (3 tests, 0 failures).
truncatedHeadersBeforeLimitStillThrowEof feeds a 21-byte buffer, so it cannot see this boundary. A case at exactly HEADER_LIMIT bytes would pin it.
readUtf8LineStrict(headerLimit) accepts a header line of exactly headerLimit bytes followed by a terminator, so a stream that ends after headerLimit bytes with no terminator is a truncated legal line rather than an oversized header. The discriminator compared buffer.size with `>= headerLimit`, which reported that boundary truncation as the limit error; tighten it to `> headerLimit` so only a strictly larger buffer maps to the ProtocolException. Add a HeadersReaderTest case at exactly HEADER_LIMIT bytes to pin the boundary.
|
Good catch — a line of exactly |
|
Please refrain from posting LLM-generated text. It is about to be banned from the project, and future posts will be deleted and LLM-generated PRs will be closed. |
Summary
When a response head exceeds the 256 KiB limit,
HeadersReaderlet okio's rawEOFExceptionpropagate (\n not found: limit=... content=<hex>), which gives nohint that the header size limit was the cause (#9233).
This translates that case into a
ProtocolExceptionthat names the limit.readUtf8LineStrict()throwsEOFExceptionboth when the line exceeds the bytelimit and when the stream ends before a line terminator, so the exception is only
rewritten when the buffer actually reached the limit
(
source.buffer.size >= headerLimit). A genuinely truncated response stillsurfaces as an
EOFException, keeping that distinct failure mode intact, and theoriginal exception is retained as the cause.
Testing
New
HeadersReaderTest:ProtocolException("response headers exceed the 256 KiB limit")EOFException./gradlew :okhttp:jvmTest --tests "okhttp3.internal.http1.HeadersReaderTest"andthe existing
okhttp3.CallLimitsTestboth pass.Fixes #9233