Skip to content

Commit 5ac90d3

Browse files
committed
keep the request method across a trailers block
send_headers stored extract_method_header() on every header block, including trailers, which carry no :method, so the remembered b"HEAD" was replaced by None and _initialize_content_length stopped applying the HEAD exemption. A response to a HEAD request that was closed with trailers then raised InvalidBodyLengthError on a bodyless message that RFC 9113 8.1.1 permits. Store the method only when the block carries one.
1 parent bc239af commit 5ac90d3

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎src/h2/stream.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,9 @@ def send_headers(self,
906906
self._authority = authority_from_headers(bytes_headers)
907907

908908
# store request method for _initialize_content_length
909-
self.request_method = extract_method_header(bytes_headers)
909+
method = extract_method_header(bytes_headers)
910+
if method is not None:
911+
self.request_method = method
910912

911913
return frames
912914

‎tests/test_head_request.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,35 @@ def test_non_zero_content_and_no_body(self, frame_factory, headers) -> None:
4545
assert event.stream_id == 1
4646
assert event.headers == self.example_response_headers
4747

48+
@pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES])
49+
def test_non_zero_content_and_no_body_after_trailers(self, frame_factory, headers) -> None:
50+
c = h2.connection.H2Connection()
51+
c.initiate_connection()
52+
c.send_headers(1, headers)
53+
c.send_headers(1, [(b"x-checksum", b"abc123")], end_stream=True)
54+
55+
f = frame_factory.build_headers_frame(
56+
[
57+
(b":status", b"200"),
58+
(b"server", b"fake-serv/0.1.0"),
59+
(b"content-length", b"1234"),
60+
],
61+
)
62+
events = c.receive_data(f.serialize())
63+
64+
assert len(events) == 1
65+
event = events[0]
66+
67+
assert isinstance(event, h2.events.ResponseReceived)
68+
assert event.stream_id == 1
69+
70+
data = frame_factory.build_data_frame(b"", flags=["END_STREAM"])
71+
events = c.receive_data(data.serialize())
72+
73+
assert len(events) == 2
74+
assert isinstance(events[0], h2.events.DataReceived)
75+
assert isinstance(events[1], h2.events.StreamEnded)
76+
4877
@pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES])
4978
def test_reject_non_zero_content_and_body(self, frame_factory, headers) -> None:
5079
c = h2.connection.H2Connection()

0 commit comments

Comments
 (0)