Skip to content

Accept 204/304 responses with content-length and no body - #1341

Closed
feiiiiii5 wants to merge 1 commit into
python-hyper:masterfrom
feiiiiii5:fix/bodyless-status-content-length
Closed

feiiiiii5 wants to merge 1 commit into
python-hyper:masterfrom
feiiiiii5:fix/bodyless-status-content-length

Conversation

@feiiiiii5

Copy link
Copy Markdown

What is wrong

RFC 9113 § 8.1.1 defines 204, 304 and the response to HEAD as messages that have no content, and says such a message may carry a non-zero content-length. H2Stream._initialize_content_length() only exempts self.request_method == b"HEAD", so the other two statuses are judged by framing alone:

304 + content-length: 1234 + END_STREAM on HEADERS  -> accepted
304 + content-length: 1234 + empty DATA END_STREAM  -> InvalidBodyLengthError

The same applies to 204. This rejects a legitimate response depending only on which frame carried END_STREAM.

What changes

receive_headers() now concretizes the header block once, then _initialize_content_length() treats a response whose :status is 204 or 304 like the HEAD exemption: expected content length is zero, so a bodyless message is accepted and any actual DATA is still rejected. Concretizing the block also avoids re-iterating a generator across informational-status, content-length and header-validation passes.

Tests

New tests/test_bodyless_responses.py:

  • 204 and 304 with content-length: 1234 and an empty DATA END_STREAM are accepted;
  • the same headers with END_STREAM on HEADERS are accepted;
  • a 200 with the same header and empty DATA still raises InvalidBodyLengthError;
  • a 304 carrying actual DATA still raises.

On the parent revision the two empty-DATA bodyless cases fail with InvalidBodyLengthError; with the change all 6 pass. Full suite: 1668 passed. ruff check src/ and mypy --strict src/h2/stream.py are clean.

RFC 9113 section 8.1.1 lists 204, 304 and the response to HEAD as messages defined to have no content, and allows a non-zero content-length. Only HEAD was exempt, so a 304 or 204 with content-length was accepted when END_STREAM was on HEADERS but rejected with an empty DATA frame. Concretize the header block once and apply the same exemption to 204/304.
@Kriechi

Kriechi commented Oct 31, 2026

Copy link
Copy Markdown
Member

Is this a duplicate of the same issue from #1332?

@feiiiiii5

Copy link
Copy Markdown
Author

Yes — same issue as #1332, and it was opened first (6 October), so I am closing this in its favour.

How I checked, rather than assuming

Our route treats 204/304 as bodyless inside _initialize_content_length (HEAD already was); #1332 reaches the same end by setting the expected length to 0 for those two statuses while keeping the syntax and conflict checks. I compared the test sets: #1332's test_no_content_response_with_content_length is parametrized over status and end_stream_on_headers, so it covers both END_STREAM placements, and test_no_content_response_rejects_content covers a bodyless response that does send data — the same ground as this branch's four cases.

The one case here that #1332 does not have is the control that a 200 declaring content-length: 5 and sending no body at all is still rejected — and that is exactly what #1342 is about, so nothing is lost by this closing.

Thanks for asking rather than just closing it.

@feiiiiii5

Copy link
Copy Markdown
Author

Closing in favour of #1332, which was opened first and fixes the same thing: a 204/304 carrying a content-length must not be policed against DATA framing it will never have. I compared the two test sets before deciding — the only case here that #1332 lacks is the control that a 200 declaring a length and sending nothing is still rejected, and that is what #1342 covers.

Thanks for asking instead of just closing it, and sorry for the duplicate review load.

@feiiiiii5 feiiiiii5 closed this Oct 31, 2026
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.

2 participants