From 05972a8b34faeb91c22b9a6283da84e45e7f0781 Mon Sep 17 00:00:00 2001 From: Pitchfork-and-Torch Date: Fri, 18 Sep 2026 02:26:32 +0000 Subject: [PATCH] Fix max_incomplete_event_size for complete oversized headers Previously the size limit was only checked when next_event() returned NEED_DATA, so oversized request/response headers that arrived in a single chunk (or otherwise completed without an incomplete overflow) were accepted while the same bytes split across packets were rejected. Enforce the limit using bytes consumed by header events as well (python-hyper/h11#182). --- h11/_connection.py | 14 ++++++++++++++ h11/tests/test_connection.py | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/h11/_connection.py b/h11/_connection.py index e37d82a..49eab15 100644 --- a/h11/_connection.py +++ b/h11/_connection.py @@ -478,8 +478,22 @@ def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: if self.their_state is ERROR: raise RemoteProtocolError("Can't receive data when peer state is ERROR") try: + # Snapshot length so we can measure how many bytes a completed + # header event consumed (request/response line + headers). + before = len(self._receive_buffer) event = self._extract_next_receive_event() if event not in [NEED_DATA, PAUSED]: + # Enforce max_incomplete_event_size for completed header events + # too (python-hyper/h11#182). Oversized headers that arrive in + # one chunk previously parsed successfully, while the same + # bytes split across packets hit the NEED_DATA size check — + # making enforcement depend on TCP framing. + if isinstance(event, (Request, InformationalResponse, Response)): + consumed = before - len(self._receive_buffer) + if consumed > self._max_incomplete_event_size: + raise RemoteProtocolError( + "Receive buffer too long", error_status_hint=431 + ) self._process_event(self.their_role, cast(Event, event)) if event is NEED_DATA: if len(self._receive_buffer) > self._max_incomplete_event_size: diff --git a/h11/tests/test_connection.py b/h11/tests/test_connection.py index 01260dc..4d496b0 100644 --- a/h11/tests/test_connection.py +++ b/h11/tests/test_connection.py @@ -486,6 +486,16 @@ def test_max_incomplete_event_size_countermeasure() -> None: c.next_event() + # Complete oversized headers in a single chunk must also be rejected + # (previously only incomplete buffers above the limit were checked). + oversized = b"GET / HTTP/1.0\r\nBig: " + b"a" * 200 + b"\r\n\r\n" + assert len(oversized) > 100 + c = Connection(SERVER, max_incomplete_event_size=100) + c.receive_data(oversized) + with pytest.raises(RemoteProtocolError): + c.next_event() + + def test_reuse_simple() -> None: p = ConnectionPair() p.send(