|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 |
| -from collections.abc import AsyncGenerator, Awaitable, Mapping |
4 |
| -from typing import Any, Callable, TypeVar |
| 3 | +from collections.abc import AsyncGenerator, AsyncIterable, Awaitable, Mapping, MutableMapping |
| 4 | +from typing import Any, Callable, TypeVar, Union |
5 | 5 |
|
6 | 6 | import anyio
|
7 | 7 |
|
8 | 8 | from starlette._utils import collapse_excgroups
|
9 | 9 | from starlette.requests import ClientDisconnect, Request
|
10 |
| -from starlette.responses import AsyncContentStream, Response |
| 10 | +from starlette.responses import Response |
11 | 11 | from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
12 | 12 |
|
13 | 13 | RequestResponseEndpoint = Callable[[Request], Awaitable[Response]]
|
14 | 14 | DispatchFunction = Callable[[Request, RequestResponseEndpoint], Awaitable[Response]]
|
| 15 | +BodyStreamGenerator = AsyncGenerator[Union[bytes, MutableMapping[str, Any]], None] |
| 16 | +AsyncContentStream = AsyncIterable[Union[str, bytes, memoryview, MutableMapping[str, Any]]] |
15 | 17 | T = TypeVar("T")
|
16 | 18 |
|
17 | 19 |
|
@@ -159,9 +161,12 @@ async def coro() -> None:
|
159 | 161 |
|
160 | 162 | assert message["type"] == "http.response.start"
|
161 | 163 |
|
162 |
| - async def body_stream() -> AsyncGenerator[bytes, None]: |
| 164 | + async def body_stream() -> BodyStreamGenerator: |
163 | 165 | async for message in recv_stream:
|
164 |
| - assert message["type"] == "http.response.body" |
| 166 | + if message["type"] == "http.response.pathsend": |
| 167 | + yield message |
| 168 | + break |
| 169 | + assert message["type"] == "http.response.body", f"Unexpected message: {message}" |
165 | 170 | body = message.get("body", b"")
|
166 | 171 | if body:
|
167 | 172 | yield body
|
@@ -214,10 +219,17 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
214 | 219 | }
|
215 | 220 | )
|
216 | 221 |
|
| 222 | + should_close_body = True |
217 | 223 | async for chunk in self.body_iterator:
|
| 224 | + if isinstance(chunk, dict): |
| 225 | + # We got an ASGI message which is not response body (eg: pathsend) |
| 226 | + should_close_body = False |
| 227 | + await send(chunk) |
| 228 | + continue |
218 | 229 | await send({"type": "http.response.body", "body": chunk, "more_body": True})
|
219 | 230 |
|
220 |
| - await send({"type": "http.response.body", "body": b"", "more_body": False}) |
| 231 | + if should_close_body: |
| 232 | + await send({"type": "http.response.body", "body": b"", "more_body": False}) |
221 | 233 |
|
222 | 234 | if self.background:
|
223 | 235 | await self.background()
|
0 commit comments