From f1e0fc645bb0b2c15d1510c9a8501743297dec9d Mon Sep 17 00:00:00 2001 From: Jonas Lundberg Date: Mon, 30 May 2022 22:45:48 +0200 Subject: [PATCH] Fixed #33754 -- Fixed crash with prematurely closed ASGI request body. Regression in 441103a04d1d167dc870eaaf90e3fba974f67c93. --- AUTHORS | 1 + django/core/handlers/asgi.py | 12 ++++++------ tests/asgi/tests.py | 12 ++++++++++++ tests/asgi/urls.py | 7 +++++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1f39fdb6c4b..4444ee16aff 100644 --- a/AUTHORS +++ b/AUTHORS @@ -490,6 +490,7 @@ answer newbie questions, and generally made Django that much better: Jökull Sólberg Auðunsson Jon Dufresne Jonas Haag + Jonas Lundberg Jonathan Davis Jonatas C. D. Jonathan Buchanan diff --git a/django/core/handlers/asgi.py b/django/core/handlers/asgi.py index 11c8bc209be..cfe5101ea49 100644 --- a/django/core/handlers/asgi.py +++ b/django/core/handlers/asgi.py @@ -171,14 +171,14 @@ class ASGIHandler(base.BaseHandler): ) # Get the request and check for basic issues. request, error_response = self.create_request(scope, body_file) + if request is None: + await self.send_response(error_response, send) + return + # Get the response, using the async mode of BaseHandler. + response = await self.get_response_async(request) + response._handler_class = self.__class__ finally: body_file.close() - if request is None: - await self.send_response(error_response, send) - return - # Get the response, using 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): diff --git a/tests/asgi/tests.py b/tests/asgi/tests.py index ef7b55724ea..cfee11802c1 100644 --- a/tests/asgi/tests.py +++ b/tests/asgi/tests.py @@ -163,6 +163,18 @@ class ASGITest(SimpleTestCase): self.assertEqual(response_body["type"], "http.response.body") self.assertEqual(response_body["body"], b"From Scotland,Wales") + async def test_post_body(self): + application = get_asgi_application() + scope = self.async_request_factory._base_scope(method="POST", path="/post/") + communicator = ApplicationCommunicator(application, scope) + await communicator.send_input({"type": "http.request", "body": b"Echo!"}) + response_start = await communicator.receive_output() + self.assertEqual(response_start["type"], "http.response.start") + self.assertEqual(response_start["status"], 200) + response_body = await communicator.receive_output() + self.assertEqual(response_body["type"], "http.response.body") + self.assertEqual(response_body["body"], b"Echo!") + async def test_get_query_string(self): application = get_asgi_application() for query_string in (b"name=Andrew", "name=Andrew"): diff --git a/tests/asgi/urls.py b/tests/asgi/urls.py index e6c74ab488c..bd286c9b2fb 100644 --- a/tests/asgi/urls.py +++ b/tests/asgi/urls.py @@ -2,6 +2,7 @@ import threading from django.http import FileResponse, HttpResponse from django.urls import path +from django.views.decorators.csrf import csrf_exempt def hello(request): @@ -23,6 +24,11 @@ def sync_waiter(request): return hello(request) +@csrf_exempt +def post_echo(request): + return HttpResponse(request.body) + + sync_waiter.active_threads = set() sync_waiter.lock = threading.Lock() sync_waiter.barrier = threading.Barrier(2) @@ -35,5 +41,6 @@ urlpatterns = [ path("", hello), path("file/", lambda x: FileResponse(open(test_filename, "rb"))), path("meta/", hello_meta), + path("post/", post_echo), path("wait/", sync_waiter), ]