mirror of
https://github.com/django/django.git
synced 2025-10-25 22:56:12 +00:00
Fixed #33738 -- Allowed handling ASGI http.disconnect in long-lived requests.
This commit is contained in:
committed by
Mariusz Felisiak
parent
4e4eda6d6c
commit
1d1ddffc27
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -177,15 +178,49 @@ class ASGIHandler(base.BaseHandler):
|
||||
body_file.close()
|
||||
await self.send_response(error_response, send)
|
||||
return
|
||||
# Get the response, using the async mode of BaseHandler.
|
||||
# Try to catch a disconnect while getting response.
|
||||
tasks = [
|
||||
asyncio.create_task(self.run_get_response(request)),
|
||||
asyncio.create_task(self.listen_for_disconnect(receive)),
|
||||
]
|
||||
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||||
done, pending = done.pop(), pending.pop()
|
||||
# Allow views to handle cancellation.
|
||||
pending.cancel()
|
||||
try:
|
||||
await pending
|
||||
except asyncio.CancelledError:
|
||||
# Task re-raised the CancelledError as expected.
|
||||
pass
|
||||
try:
|
||||
response = done.result()
|
||||
except RequestAborted:
|
||||
body_file.close()
|
||||
return
|
||||
except AssertionError:
|
||||
body_file.close()
|
||||
raise
|
||||
# Send the response.
|
||||
await self.send_response(response, send)
|
||||
|
||||
async def listen_for_disconnect(self, receive):
|
||||
"""Listen for disconnect from the client."""
|
||||
message = await receive()
|
||||
if message["type"] == "http.disconnect":
|
||||
raise RequestAborted()
|
||||
# This should never happen.
|
||||
assert False, "Invalid ASGI message after request body: %s" % message["type"]
|
||||
|
||||
async def run_get_response(self, request):
|
||||
"""Get async response."""
|
||||
# Use the async mode of BaseHandler.
|
||||
response = await self.get_response_async(request)
|
||||
response._handler_class = self.__class__
|
||||
# Increase chunk size on file responses (ASGI servers handles low-level
|
||||
# chunking).
|
||||
if isinstance(response, FileResponse):
|
||||
response.block_size = self.chunk_size
|
||||
# Send the response.
|
||||
await self.send_response(response, send)
|
||||
return response
|
||||
|
||||
async def read_body(self, receive):
|
||||
"""Reads an HTTP body from an ASGI connection."""
|
||||
|
||||
Reference in New Issue
Block a user