Skip to content

Commit 56b7395

Browse files
authored
fix: logging middleware with multi-body response (#3478)
* fix: logging middleware with multi-body response Prevent logging middleware from failing with a KeyError when a response sends multiple "http.response.body" messages. Closes #3477 * fix: ensure asgi messages aren't kept in log context
1 parent 9fd80e2 commit 56b7395

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

litestar/middleware/logging.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ def extract_response_data(self, scope: Scope) -> dict[str, Any]:
191191
connection_state = ScopeState.from_scope(scope)
192192
extracted_data = self.response_extractor(
193193
messages=(
194-
connection_state.log_context.pop(HTTP_RESPONSE_START),
194+
# NOTE: we don't pop the start message from the logging context in case
195+
# there are multiple body messages to be logged
196+
connection_state.log_context[HTTP_RESPONSE_START],
195197
connection_state.log_context.pop(HTTP_RESPONSE_BODY),
196198
),
197199
)
@@ -224,6 +226,10 @@ async def send_wrapper(message: Message) -> None:
224226
elif message["type"] == HTTP_RESPONSE_BODY:
225227
connection_state.log_context[HTTP_RESPONSE_BODY] = message
226228
self.log_response(scope=scope)
229+
230+
if not message["more_body"]:
231+
connection_state.log_context.clear()
232+
227233
await send(message)
228234

229235
return send_wrapper
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from litestar import asgi
2+
from litestar.middleware.logging import LoggingMiddlewareConfig
3+
from litestar.testing import create_async_test_client
4+
from litestar.types.asgi_types import Receive, Scope, Send
5+
6+
7+
@asgi("/")
8+
async def asgi_app(scope: Scope, receive: Receive, send: Send) -> None:
9+
await send(
10+
{
11+
"type": "http.response.start",
12+
"status": 200,
13+
"headers": [
14+
(b"content-type", b"text/event-stream"),
15+
(b"cache-control", b"no-cache"),
16+
(b"connection", b"keep-alive"),
17+
],
18+
}
19+
)
20+
21+
# send two bodies
22+
await send({"type": "http.response.body", "body": b"data: 1\n", "more_body": True})
23+
await send({"type": "http.response.body", "body": b"data: 2\n", "more_body": False})
24+
25+
26+
async def test_app() -> None:
27+
async with create_async_test_client(asgi_app, middleware=[LoggingMiddlewareConfig().middleware]) as client:
28+
response = await client.get("/")
29+
assert response.status_code == 200
30+
assert response.text == "data: 1\ndata: 2\n"

0 commit comments

Comments
 (0)