From 0efd3835da6dcc713f74aadf7b52779d0d1fa17d Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 13 Apr 2024 16:05:51 +0200 Subject: [PATCH] Send `content-length` header on 5xx (#2304) --- tests/protocols/test_http.py | 14 ++++++++- uvicorn/protocols/http/flow_control.py | 38 +++++++++--------------- uvicorn/protocols/http/httptools_impl.py | 28 ++++++++--------- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/tests/protocols/test_http.py b/tests/protocols/test_http.py index 76fd9336c..677e72f9f 100644 --- a/tests/protocols/test_http.py +++ b/tests/protocols/test_http.py @@ -671,7 +671,19 @@ async def test_max_concurrency(http_protocol_cls: HTTPProtocol): protocol = get_connected_protocol(app, http_protocol_cls, limit_concurrency=1) protocol.data_received(SIMPLE_GET_REQUEST) await protocol.loop.run_one() - assert b"HTTP/1.1 503 Service Unavailable" in protocol.transport.buffer + assert ( + b"\r\n".join( + [ + b"HTTP/1.1 503 Service Unavailable", + b"content-type: text/plain; charset=utf-8", + b"content-length: 19", + b"connection: close", + b"", + b"Service Unavailable", + ] + ) + == protocol.transport.buffer + ) @pytest.mark.anyio diff --git a/uvicorn/protocols/http/flow_control.py b/uvicorn/protocols/http/flow_control.py index 893a26c80..42ecade71 100644 --- a/uvicorn/protocols/http/flow_control.py +++ b/uvicorn/protocols/http/flow_control.py @@ -1,12 +1,6 @@ import asyncio -from uvicorn._types import ( - ASGIReceiveCallable, - ASGISendCallable, - HTTPResponseBodyEvent, - HTTPResponseStartEvent, - Scope, -) +from uvicorn._types import ASGIReceiveCallable, ASGISendCallable, Scope CLOSE_HEADER = (b"connection", b"close") @@ -45,20 +39,16 @@ def resume_writing(self) -> None: self._is_writable_event.set() -async def service_unavailable(scope: "Scope", receive: "ASGIReceiveCallable", send: "ASGISendCallable") -> None: - response_start: "HTTPResponseStartEvent" = { - "type": "http.response.start", - "status": 503, - "headers": [ - (b"content-type", b"text/plain; charset=utf-8"), - (b"connection", b"close"), - ], - } - await send(response_start) - - response_body: "HTTPResponseBodyEvent" = { - "type": "http.response.body", - "body": b"Service Unavailable", - "more_body": False, - } - await send(response_body) +async def service_unavailable(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: + await send( + { + "type": "http.response.start", + "status": 503, + "headers": [ + (b"content-type", b"text/plain; charset=utf-8"), + (b"content-length", b"19"), + (b"connection", b"close"), + ], + } + ) + await send({"type": "http.response.body", "body": b"Service Unavailable", "more_body": False}) diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py index 3fd5515af..60debaf8f 100644 --- a/uvicorn/protocols/http/httptools_impl.py +++ b/uvicorn/protocols/http/httptools_impl.py @@ -16,7 +16,6 @@ ASGIReceiveEvent, ASGISendEvent, HTTPRequestEvent, - HTTPResponseBodyEvent, HTTPResponseStartEvent, HTTPScope, ) @@ -435,21 +434,18 @@ async def run_asgi(self, app: ASGI3Application) -> None: self.on_response = lambda: None async def send_500_response(self) -> None: - response_start_event: HTTPResponseStartEvent = { - "type": "http.response.start", - "status": 500, - "headers": [ - (b"content-type", b"text/plain; charset=utf-8"), - (b"connection", b"close"), - ], - } - await self.send(response_start_event) - response_body_event: HTTPResponseBodyEvent = { - "type": "http.response.body", - "body": b"Internal Server Error", - "more_body": False, - } - await self.send(response_body_event) + await self.send( + { + "type": "http.response.start", + "status": 500, + "headers": [ + (b"content-type", b"text/plain; charset=utf-8"), + (b"content-length", b"21"), + (b"connection", b"close"), + ], + } + ) + await self.send({"type": "http.response.body", "body": b"Internal Server Error", "more_body": False}) # ASGI interface async def send(self, message: ASGISendEvent) -> None: