mirror of https://github.com/django/django.git
Bug in0bd2c0c901
. Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es> Backport of52b054824e
from main
This commit is contained in:
parent
1ecbc04624
commit
610cd06c3f
|
@ -116,6 +116,16 @@ def closing_iterator_wrapper(iterable, close):
|
||||||
request_finished.connect(close_old_connections)
|
request_finished.connect(close_old_connections)
|
||||||
|
|
||||||
|
|
||||||
|
async def aclosing_iterator_wrapper(iterable, close):
|
||||||
|
try:
|
||||||
|
async for chunk in iterable:
|
||||||
|
yield chunk
|
||||||
|
finally:
|
||||||
|
request_finished.disconnect(close_old_connections)
|
||||||
|
close() # will fire request_finished
|
||||||
|
request_finished.connect(close_old_connections)
|
||||||
|
|
||||||
|
|
||||||
def conditional_content_removal(request, response):
|
def conditional_content_removal(request, response):
|
||||||
"""
|
"""
|
||||||
Simulate the behavior of most web servers by removing the content of
|
Simulate the behavior of most web servers by removing the content of
|
||||||
|
@ -174,9 +184,14 @@ class ClientHandler(BaseHandler):
|
||||||
|
|
||||||
# Emulate a WSGI server by calling the close method on completion.
|
# Emulate a WSGI server by calling the close method on completion.
|
||||||
if response.streaming:
|
if response.streaming:
|
||||||
response.streaming_content = closing_iterator_wrapper(
|
if response.is_async:
|
||||||
response.streaming_content, response.close
|
response.streaming_content = aclosing_iterator_wrapper(
|
||||||
)
|
response.streaming_content, response.close
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response.streaming_content = closing_iterator_wrapper(
|
||||||
|
response.streaming_content, response.close
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
request_finished.disconnect(close_old_connections)
|
request_finished.disconnect(close_old_connections)
|
||||||
response.close() # will fire request_finished
|
response.close() # will fire request_finished
|
||||||
|
@ -223,12 +238,14 @@ class AsyncClientHandler(BaseHandler):
|
||||||
response.asgi_request = request
|
response.asgi_request = request
|
||||||
# Emulate a server by calling the close method on completion.
|
# Emulate a server by calling the close method on completion.
|
||||||
if response.streaming:
|
if response.streaming:
|
||||||
response.streaming_content = await sync_to_async(
|
if response.is_async:
|
||||||
closing_iterator_wrapper, thread_sensitive=False
|
response.streaming_content = aclosing_iterator_wrapper(
|
||||||
)(
|
response.streaming_content, response.close
|
||||||
response.streaming_content,
|
)
|
||||||
response.close,
|
else:
|
||||||
)
|
response.streaming_content = closing_iterator_wrapper(
|
||||||
|
response.streaming_content, response.close
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
request_finished.disconnect(close_old_connections)
|
request_finished.disconnect(close_old_connections)
|
||||||
# Will fire request_finished.
|
# Will fire request_finished.
|
||||||
|
|
|
@ -253,6 +253,16 @@ class HandlerRequestTests(SimpleTestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(b"".join(list(response)), b"streaming content")
|
self.assertEqual(b"".join(list(response)), b"streaming content")
|
||||||
|
|
||||||
|
def test_async_streaming(self):
|
||||||
|
response = self.client.get("/async_streaming/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
msg = (
|
||||||
|
"StreamingHttpResponse must consume asynchronous iterators in order to "
|
||||||
|
"serve them synchronously. Use a synchronous iterator instead."
|
||||||
|
)
|
||||||
|
with self.assertWarnsMessage(Warning, msg):
|
||||||
|
self.assertEqual(b"".join(list(response)), b"streaming content")
|
||||||
|
|
||||||
|
|
||||||
class ScriptNameTests(SimpleTestCase):
|
class ScriptNameTests(SimpleTestCase):
|
||||||
def test_get_script_name(self):
|
def test_get_script_name(self):
|
||||||
|
@ -329,3 +339,10 @@ class AsyncHandlerRequestTests(SimpleTestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
b"".join([chunk async for chunk in response]), b"streaming content"
|
b"".join([chunk async for chunk in response]), b"streaming content"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def test_async_streaming(self):
|
||||||
|
response = await self.async_client.get("/async_streaming/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(
|
||||||
|
b"".join([chunk async for chunk in response]), b"streaming content"
|
||||||
|
)
|
||||||
|
|
|
@ -8,6 +8,7 @@ urlpatterns = [
|
||||||
path("no_response_fbv/", views.no_response),
|
path("no_response_fbv/", views.no_response),
|
||||||
path("no_response_cbv/", views.NoResponse()),
|
path("no_response_cbv/", views.NoResponse()),
|
||||||
path("streaming/", views.streaming),
|
path("streaming/", views.streaming),
|
||||||
|
path("async_streaming/", views.async_streaming),
|
||||||
path("in_transaction/", views.in_transaction),
|
path("in_transaction/", views.in_transaction),
|
||||||
path("not_in_transaction/", views.not_in_transaction),
|
path("not_in_transaction/", views.not_in_transaction),
|
||||||
path("not_in_transaction_using_none/", views.not_in_transaction_using_none),
|
path("not_in_transaction_using_none/", views.not_in_transaction_using_none),
|
||||||
|
|
|
@ -65,6 +65,15 @@ async def async_regular(request):
|
||||||
return HttpResponse(b"regular content")
|
return HttpResponse(b"regular content")
|
||||||
|
|
||||||
|
|
||||||
|
async def async_streaming(request):
|
||||||
|
async def async_streaming_generator():
|
||||||
|
yield b"streaming"
|
||||||
|
yield b" "
|
||||||
|
yield b"content"
|
||||||
|
|
||||||
|
return StreamingHttpResponse(async_streaming_generator())
|
||||||
|
|
||||||
|
|
||||||
class CoroutineClearingView:
|
class CoroutineClearingView:
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
"""Return an unawaited coroutine (common error for async views)."""
|
"""Return an unawaited coroutine (common error for async views)."""
|
||||||
|
|
Loading…
Reference in New Issue