diff --git a/httpcore/_async/http11.py b/httpcore/_async/http11.py index 32bc5c41e..3b55ee2a3 100644 --- a/httpcore/_async/http11.py +++ b/httpcore/_async/http11.py @@ -167,6 +167,19 @@ async def _receive_event(self, timeout: TimeoutDict) -> H11Event: if event is h11.NEED_DATA: data = await self.socket.read(self.READ_NUM_BYTES, timeout) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self.h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + self.h11_state.receive_data(data) else: assert event is not h11.NEED_DATA diff --git a/httpcore/_sync/http11.py b/httpcore/_sync/http11.py index a805b8bad..3cd1c3995 100644 --- a/httpcore/_sync/http11.py +++ b/httpcore/_sync/http11.py @@ -167,6 +167,19 @@ def _receive_event(self, timeout: TimeoutDict) -> H11Event: if event is h11.NEED_DATA: data = self.socket.read(self.READ_NUM_BYTES, timeout) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self.h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + self.h11_state.receive_data(data) else: assert event is not h11.NEED_DATA