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(