Merge pull request #1965 from ashleysommer/asgs_chunk_length
Fix Chunked Transport-Encoding in ASGI streaming response
This commit is contained in:
commit
33da0771d1
|
@ -1454,6 +1454,8 @@ class Sanic:
|
||||||
asgi_app = await ASGIApp.create(self, scope, receive, send)
|
asgi_app = await ASGIApp.create(self, scope, receive, send)
|
||||||
await asgi_app()
|
await asgi_app()
|
||||||
|
|
||||||
|
_asgi_single_callable = True # We conform to ASGI 3.0 single-callable
|
||||||
|
|
||||||
# -------------------------------------------------------------------- #
|
# -------------------------------------------------------------------- #
|
||||||
# Configuration
|
# Configuration
|
||||||
# -------------------------------------------------------------------- #
|
# -------------------------------------------------------------------- #
|
||||||
|
|
|
@ -312,13 +312,19 @@ class ASGIApp:
|
||||||
callback = None if self.ws else self.stream_callback
|
callback = None if self.ws else self.stream_callback
|
||||||
await handler(self.request, None, callback)
|
await handler(self.request, None, callback)
|
||||||
|
|
||||||
async def stream_callback(self, response: HTTPResponse) -> None:
|
_asgi_single_callable = True # We conform to ASGI 3.0 single-callable
|
||||||
|
|
||||||
|
async def stream_callback(
|
||||||
|
self, response: Union[HTTPResponse, StreamingHTTPResponse]
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write the response.
|
Write the response.
|
||||||
"""
|
"""
|
||||||
headers: List[Tuple[bytes, bytes]] = []
|
headers: List[Tuple[bytes, bytes]] = []
|
||||||
cookies: Dict[str, str] = {}
|
cookies: Dict[str, str] = {}
|
||||||
|
content_length: List[str] = []
|
||||||
try:
|
try:
|
||||||
|
content_length = response.headers.popall("content-length", [])
|
||||||
cookies = {
|
cookies = {
|
||||||
v.key: v
|
v.key: v
|
||||||
for _, v in list(
|
for _, v in list(
|
||||||
|
@ -351,12 +357,22 @@ class ASGIApp:
|
||||||
]
|
]
|
||||||
|
|
||||||
response.asgi = True
|
response.asgi = True
|
||||||
|
is_streaming = isinstance(response, StreamingHTTPResponse)
|
||||||
if "content-length" not in response.headers and not isinstance(
|
if is_streaming and getattr(response, "chunked", False):
|
||||||
response, StreamingHTTPResponse
|
# disable sanic chunking, this is done at the ASGI-server level
|
||||||
):
|
setattr(response, "chunked", False)
|
||||||
|
# content-length header is removed to signal to the ASGI-server
|
||||||
|
# to use automatic-chunking if it supports it
|
||||||
|
elif len(content_length) > 0:
|
||||||
headers += [
|
headers += [
|
||||||
(b"content-length", str(len(response.body)).encode("latin-1"))
|
(b"content-length", str(content_length[0]).encode("latin-1"))
|
||||||
|
]
|
||||||
|
elif not is_streaming:
|
||||||
|
headers += [
|
||||||
|
(
|
||||||
|
b"content-length",
|
||||||
|
str(len(getattr(response, "body", b""))).encode("latin-1"),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
if "content-type" not in response.headers:
|
if "content-type" not in response.headers:
|
||||||
|
|
|
@ -100,6 +100,8 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
||||||
"""
|
"""
|
||||||
data = self._encode_body(data)
|
data = self._encode_body(data)
|
||||||
|
|
||||||
|
# `chunked` will always be False in ASGI-mode, even if the underlying
|
||||||
|
# ASGI Transport implements Chunked transport. That does it itself.
|
||||||
if self.chunked:
|
if self.chunked:
|
||||||
await self.protocol.push_data(b"%x\r\n%b\r\n" % (len(data), data))
|
await self.protocol.push_data(b"%x\r\n%b\r\n" % (len(data), data))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -238,7 +238,7 @@ def test_chunked_streaming_returns_correct_content(streaming_app):
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_chunked_streaming_returns_correct_content_asgi(streaming_app):
|
async def test_chunked_streaming_returns_correct_content_asgi(streaming_app):
|
||||||
request, response = await streaming_app.asgi_client.get("/")
|
request, response = await streaming_app.asgi_client.get("/")
|
||||||
assert response.text == "4\r\nfoo,\r\n3\r\nbar\r\n0\r\n\r\n"
|
assert response.text == "foo,bar"
|
||||||
|
|
||||||
|
|
||||||
def test_non_chunked_streaming_adds_correct_headers(non_chunked_streaming_app):
|
def test_non_chunked_streaming_adds_correct_headers(non_chunked_streaming_app):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user