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:
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