Fixed #33755 -- Moved ASGI body-file cleanup into request class.
This commit is contained in:
parent
c32858a8ce
commit
e96320c917
|
@ -99,3 +99,8 @@ class ASGIStaticFilesHandler(StaticFilesHandlerMixin, ASGIHandler):
|
|||
return await super().__call__(scope, receive, send)
|
||||
# Hand off to the main app
|
||||
return await self.application(scope, receive, send)
|
||||
|
||||
async def get_response_async(self, request):
|
||||
response = await super().get_response_async(request)
|
||||
response._resource_closers.append(request.close)
|
||||
return response
|
||||
|
|
|
@ -128,6 +128,10 @@ class ASGIRequest(HttpRequest):
|
|||
def COOKIES(self):
|
||||
return parse_cookie(self.META.get("HTTP_COOKIE", ""))
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
self._stream.close()
|
||||
|
||||
|
||||
class ASGIHandler(base.BaseHandler):
|
||||
"""Handler for ASGI requests."""
|
||||
|
@ -164,21 +168,19 @@ class ASGIHandler(base.BaseHandler):
|
|||
except RequestAborted:
|
||||
return
|
||||
# Request is complete and can be served.
|
||||
try:
|
||||
set_script_prefix(self.get_script_prefix(scope))
|
||||
await sync_to_async(signals.request_started.send, thread_sensitive=True)(
|
||||
sender=self.__class__, scope=scope
|
||||
)
|
||||
# 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:
|
||||
set_script_prefix(self.get_script_prefix(scope))
|
||||
await sync_to_async(signals.request_started.send, thread_sensitive=True)(
|
||||
sender=self.__class__, scope=scope
|
||||
)
|
||||
# Get the request and check for basic issues.
|
||||
request, error_response = self.create_request(scope, body_file)
|
||||
if request is None:
|
||||
body_file.close()
|
||||
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):
|
||||
|
|
|
@ -59,6 +59,9 @@ class LimitedStream:
|
|||
self.buffer = sio.read()
|
||||
return line
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class WSGIRequest(HttpRequest):
|
||||
def __init__(self, environ):
|
||||
|
|
|
@ -340,6 +340,8 @@ class HttpRequest:
|
|||
self._body = self.read()
|
||||
except OSError as e:
|
||||
raise UnreadablePostError(*e.args) from e
|
||||
finally:
|
||||
self._stream.close()
|
||||
self._stream = BytesIO(self._body)
|
||||
return self._body
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ class FakePayload:
|
|||
self.__content.write(content)
|
||||
self.__len += len(content)
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
def closing_iterator_wrapper(iterable, close):
|
||||
try:
|
||||
|
|
|
@ -165,7 +165,11 @@ class ASGITest(SimpleTestCase):
|
|||
|
||||
async def test_post_body(self):
|
||||
application = get_asgi_application()
|
||||
scope = self.async_request_factory._base_scope(method="POST", path="/post/")
|
||||
scope = self.async_request_factory._base_scope(
|
||||
method="POST",
|
||||
path="/post/",
|
||||
query_string="echo=1",
|
||||
)
|
||||
communicator = ApplicationCommunicator(application, scope)
|
||||
await communicator.send_input({"type": "http.request", "body": b"Echo!"})
|
||||
response_start = await communicator.receive_output()
|
||||
|
@ -175,6 +179,18 @@ class ASGITest(SimpleTestCase):
|
|||
self.assertEqual(response_body["type"], "http.response.body")
|
||||
self.assertEqual(response_body["body"], b"Echo!")
|
||||
|
||||
async def test_untouched_request_body_gets_closed(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"})
|
||||
response_start = await communicator.receive_output()
|
||||
self.assertEqual(response_start["type"], "http.response.start")
|
||||
self.assertEqual(response_start["status"], 204)
|
||||
response_body = await communicator.receive_output()
|
||||
self.assertEqual(response_body["type"], "http.response.body")
|
||||
self.assertEqual(response_body["body"], b"")
|
||||
|
||||
async def test_get_query_string(self):
|
||||
application = get_asgi_application()
|
||||
for query_string in (b"name=Andrew", "name=Andrew"):
|
||||
|
|
|
@ -26,7 +26,10 @@ def sync_waiter(request):
|
|||
|
||||
@csrf_exempt
|
||||
def post_echo(request):
|
||||
return HttpResponse(request.body)
|
||||
if request.GET.get("echo"):
|
||||
return HttpResponse(request.body)
|
||||
else:
|
||||
return HttpResponse(status=204)
|
||||
|
||||
|
||||
sync_waiter.active_threads = set()
|
||||
|
|
Loading…
Reference in New Issue