diff --git a/docs/source/api.rst b/docs/source/api.rst index 4b6798e..c264c72 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -431,9 +431,10 @@ The Connection object True if the client sent a request with the ``Expect: 100-continue`` header, and is still waiting for a response - (i.e., the server has not sent a 100 Continue or any other kind - of response, and the client has not gone ahead and started - sending the body anyway). + (i.e., the server has not sent a 100 Continue, switched protocols, + or sent a final response, and the client has not gone ahead and + started sending the body anyway). Other informational responses, + such as 103 Early Hints, do not clear this flag. See `RFC 7231 section 5.1.1 `_ for details. @@ -695,12 +696,13 @@ have to be prepared to handle :class:`Request`\s that have an ``Expect: 100-continue`` header. You can `read the spec `_ for the full details, but basically what this header means is that after sending -the :class:`Request`, the client plans to pause and wait until they -see some response from the server before they send that request's -:class:`Data`. The server's response would normally be an -:class:`InformationalResponse` with status ``100 Continue``, but it -could be anything really (e.g. a full :class:`Response` with a 4xx -status code). The crucial thing as a server, though, is that you +the :class:`Request`, the client plans to pause and wait for a +``100 Continue`` or a final response before they send that request's +:class:`Data`. The server can send an :class:`InformationalResponse` +with status ``100 Continue`` to request the body, or a final +:class:`Response` (e.g. with a 4xx status code). Other informational +responses, such as ``103 Early Hints``, leave the client waiting for +``100 Continue``. The crucial thing as a server, though, is that you should never block trying to read a request body if the client is blocked waiting for you to tell them to send the request body. diff --git a/h11/_connection.py b/h11/_connection.py index e37d82a..3c54e96 100644 --- a/h11/_connection.py +++ b/h11/_connection.py @@ -310,7 +310,11 @@ def _process_event(self, role: Type[Sentinel], event: Event) -> None: # 100-continue if type(event) is Request and has_expect_100_continue(event): self.client_is_waiting_for_100_continue = True - if type(event) in (InformationalResponse, Response): + # Other informational responses, such as 103 Early Hints, do not + # tell the client to send its request body. + if type(event) is Response or ( + type(event) is InformationalResponse and event.status_code in (100, 101) + ): self.client_is_waiting_for_100_continue = False if role is CLIENT and type(event) in (Data, EndOfMessage): self.client_is_waiting_for_100_continue = False diff --git a/h11/tests/test_connection.py b/h11/tests/test_connection.py index 01260dc..c3b8a7b 100644 --- a/h11/tests/test_connection.py +++ b/h11/tests/test_connection.py @@ -365,7 +365,8 @@ def test_automagic_connection_close_handling() -> None: assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} -def test_100_continue() -> None: +@pytest.mark.parametrize("informational_status", [None, 102, 103]) +def test_100_continue(informational_status: Optional[int]) -> None: def setup() -> ConnectionPair: p = ConnectionPair() p.send( @@ -382,6 +383,14 @@ def setup() -> ConnectionPair: ) for conn in p.conns: assert conn.client_is_waiting_for_100_continue + if informational_status is not None: + for _ in range(2): + p.send( + SERVER, + InformationalResponse(status_code=informational_status, headers=[]), + ) + for conn in p.conns: + assert conn.client_is_waiting_for_100_continue assert not p.conn[CLIENT].they_are_waiting_for_100_continue assert p.conn[SERVER].they_are_waiting_for_100_continue return p diff --git a/newsfragments/211.bugfix.rst b/newsfragments/211.bugfix.rst new file mode 100644 index 0000000..837c369 --- /dev/null +++ b/newsfragments/211.bugfix.rst @@ -0,0 +1,3 @@ +Preserve the waiting-for-100-continue state after informational responses +such as 103 Early Hints, so servers still send 100 Continue before waiting +for the request body.