﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
33738	ASGI http.disconnect not handled on requests with body.	Carlton Gibson	Dennis Chukwunta	"Noticed whilst reviewing [https://github.com/django/django/pull/15704 PR 15704] for #33699, we're not handling the ASGI `http.disconnect` message correctly. Since it's only dealt with whilst reading the request body, `http.disconnect` is not processed on a request that includes a body. 

https://github.com/django/django/blob/241fe59b74bb6031fa644f3ad55e6ad6a9187510/django/core/handlers/asgi.py#L189

{{{
    async def read_body(self, receive):
        """"""Reads an HTTP body from an ASGI connection.""""""
        # Use the tempfile that auto rolls-over to a disk file as it fills up.
        body_file = tempfile.SpooledTemporaryFile(
            max_size=settings.FILE_UPLOAD_MAX_MEMORY_SIZE, mode=""w+b""
        )
        while True:
            message = await receive()
            if message[""type""] == ""http.disconnect"":    # This is the only place `http.disconnect` is handled. 
                body_file.close()
                # Early client disconnect.
                raise RequestAborted()
            # Add a body chunk from the message, if provided.
            if ""body"" in message:
                body_file.write(message[""body""])
            # Quit out if that's the end.
            if not message.get(""more_body"", False):
                break
        body_file.seek(0)
        return body_file
}}}

`http.disconnect` is designed for long-polling — so we imagine a client opening a request, with a request body, and then disconnecting before the response is generated. 

The protocol server (Daphne/uvicorn/...) will send the `http.diconnect` message, but it's not handled.

This test fails on main (at 9f5548952906c6ea97200c016734b4f519520a64 — 4.2 pre-alpha)

{{{
diff --git a/tests/asgi/tests.py b/tests/asgi/tests.py
index ef7b55724e..a68ca8a473 100644
--- a/tests/asgi/tests.py
+++ b/tests/asgi/tests.py
@@ -188,6 +188,18 @@ class ASGITest(SimpleTestCase):
         with self.assertRaises(asyncio.TimeoutError):
             await communicator.receive_output()
 
+    async def test_disconnect_with_body(self):
+        application = get_asgi_application()
+        scope = self.async_request_factory._base_scope(path=""/"")
+        communicator = ApplicationCommunicator(application, scope)
+        await communicator.send_input({
+            ""type"": ""http.request"",
+            ""body"": b""some body"",
+        })
+        await communicator.send_input({""type"": ""http.disconnect""})
+        with self.assertRaises(asyncio.TimeoutError):
+            await communicator.receive_output()
+
     async def test_wrong_connection_type(self):
         application = get_asgi_application()
         scope = self.async_request_factory._base_scope(path=""/"", type=""other"")
}}}


To handle this correctly it looks like we'd need something like Channel's [https://github.com/django/channels/blob/ae60a7d86f3655a1cc35cd9198e61fe5dcc5d1a1/channels/utils.py#L32 `await_many_dispatch()`] to keep receiving from the input queue whilst dispatching the request. 🤔"	New feature	closed	HTTP handling	4.0	Normal	fixed	ASGI	Noxx Andrew Godwin	Ready for checkin	1	0	0	0	0	0
