]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
asgi: Send http.disconnect message on receive() after response complete (#909)
authorJamie Hewland <jhewland@gmail.com>
Fri, 1 May 2020 09:51:35 +0000 (11:51 +0200)
committerGitHub <noreply@github.com>
Fri, 1 May 2020 09:51:35 +0000 (11:51 +0200)
Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
httpx/_dispatch/asgi.py
tests/test_asgi.py

index 6b89f46262614c574b4d0bfe34c00a280b82450c..5edca1ed8c5a5e69766eb034506168d239b68e12 100644 (file)
@@ -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:
index 2b40586288e13c7c7fbf5199c6b2fd48b55e36e7..72a003936e1aa1dbd6fd96c67d38aa2d40ceab06 100644 (file)
@@ -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