Skip to content

Commit

Permalink
Merge pull request #1966 from ashleysommer/asgs_chunk_1912
Browse files Browse the repository at this point in the history
Backport #1965 to 19.12LTS
  • Loading branch information
ahopkins authored Nov 5, 2020
2 parents c5070bd + be1ca93 commit 468f4ac
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 7 deletions.
2 changes: 1 addition & 1 deletion sanic/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "19.12.3"
__version__ = "19.12.4"
2 changes: 2 additions & 0 deletions sanic/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1474,3 +1474,5 @@ async def __call__(self, scope, receive, send):
self.asgi = True
asgi_app = await ASGIApp.create(self, scope, receive, send)
await asgi_app()

_asgi_single_callable = True # We conform to ASGI 3.0 single-callable
24 changes: 19 additions & 5 deletions sanic/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,17 @@ async def __call__(self) -> None:
callback = None if self.ws else self.stream_callback
await handler(self.request, None, callback)

_asgi_single_callable = True # We conform to ASGI 3.0 single-callable

async def stream_callback(self, response: HTTPResponse) -> None:
"""
Write the response.
"""
headers: List[Tuple[bytes, bytes]] = []
cookies: Dict[str, str] = {}
content_length: List[str] = []
try:
content_length = response.headers.popall("content-length", [])
cookies = {
v.key: v
for _, v in list(
Expand Down Expand Up @@ -348,12 +352,22 @@ async def stream_callback(self, response: HTTPResponse) -> None:
]

response.asgi = True

if "content-length" not in response.headers and not isinstance(
response, StreamingHTTPResponse
):
is_streaming = isinstance(response, StreamingHTTPResponse)
if is_streaming and getattr(response, "chunked", False):
# 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 += [
(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:
Expand Down
2 changes: 2 additions & 0 deletions sanic/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ async def write(self, data):
if type(data) != bytes:
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:
await self.protocol.push_data(b"%x\r\n%b\r\n" % (len(data), data))
else:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def test_chunked_streaming_returns_correct_content(streaming_app):
@pytest.mark.asyncio
async def test_chunked_streaming_returns_correct_content_asgi(streaming_app):
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):
Expand Down

0 comments on commit 468f4ac

Please sign in to comment.