From: Jamie Hewland Date: Fri, 1 May 2020 09:51:35 +0000 (+0200) Subject: asgi: Send http.disconnect message on receive() after response complete (#909) X-Git-Tag: 0.13.0.dev1~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8710079d5d589faef1d49b4e9aeec83d0987b3f2;p=thirdparty%2Fhttpx.git asgi: Send http.disconnect message on receive() after response complete (#909) Co-authored-by: Florimond Manca --- diff --git a/httpx/_dispatch/asgi.py b/httpx/_dispatch/asgi.py index 6b89f462..5edca1ed 100644 --- a/httpx/_dispatch/asgi.py +++ b/httpx/_dispatch/asgi.py @@ -85,6 +85,11 @@ class ASGIDispatch(httpcore.AsyncHTTPTransport): request_body_chunks = stream.__aiter__() async def receive() -> dict: + nonlocal response_complete + + if response_complete: + return {"type": "http.disconnect"} + try: body = await request_body_chunks.__anext__() except StopAsyncIteration: diff --git a/tests/test_asgi.py b/tests/test_asgi.py index 2b405862..72a00393 100644 --- a/tests/test_asgi.py +++ b/tests/test_asgi.py @@ -67,3 +67,36 @@ async def test_asgi_exc_after_response(): client = httpx.AsyncClient(app=raise_exc_after_response) with pytest.raises(ValueError): await client.get("http://www.example.org/") + + +@pytest.mark.asyncio +async def test_asgi_disconnect_after_response_complete(): + disconnect = False + + async def read_body(scope, receive, send): + nonlocal disconnect + + status = 200 + headers = [(b"content-type", "text/plain")] + + await send( + {"type": "http.response.start", "status": status, "headers": headers} + ) + more_body = True + while more_body: + message = await receive() + more_body = message.get("more_body", False) + + await send({"type": "http.response.body", "body": b"", "more_body": False}) + + # The ASGI spec says of the Disconnect message: + # "Sent to the application when a HTTP connection is closed or if receive is + # called after a response has been sent." + # So if receive() is called again, the disconnect message should be received + message = await receive() + disconnect = message.get("type") == "http.disconnect" + + client = httpx.AsyncClient(app=read_body) + response = await client.post("http://www.example.org/", data=b"example") + assert response.status_code == 200 + assert disconnect