|
2 | 2 |
|
3 | 3 | import contextvars
|
4 | 4 | from contextlib import AsyncExitStack
|
| 5 | +from pathlib import Path |
5 | 6 | from typing import (
|
6 | 7 | Any,
|
7 | 8 | AsyncGenerator,
|
|
18 | 19 | from starlette.middleware import Middleware, _MiddlewareClass
|
19 | 20 | from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
20 | 21 | from starlette.requests import ClientDisconnect, Request
|
21 |
| -from starlette.responses import PlainTextResponse, Response, StreamingResponse |
| 22 | +from starlette.responses import FileResponse, PlainTextResponse, Response, StreamingResponse |
22 | 23 | from starlette.routing import Route, WebSocketRoute
|
23 | 24 | from starlette.testclient import TestClient
|
24 | 25 | from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
@@ -1132,3 +1133,54 @@ async def send(message: Message) -> None:
|
1132 | 1133 | {"type": "http.response.body", "body": b"good!", "more_body": True},
|
1133 | 1134 | {"type": "http.response.body", "body": b"", "more_body": False},
|
1134 | 1135 | ]
|
| 1136 | + |
| 1137 | + |
| 1138 | +@pytest.mark.anyio |
| 1139 | +async def test_asgi_pathsend_events(tmpdir: Path) -> None: |
| 1140 | + path = tmpdir / "example.txt" |
| 1141 | + with path.open("w") as file: |
| 1142 | + file.write("<file content>") |
| 1143 | + |
| 1144 | + request_body_sent = False |
| 1145 | + response_complete = anyio.Event() |
| 1146 | + events: list[Message] = [] |
| 1147 | + |
| 1148 | + async def endpoint_with_pathsend(_: Request) -> FileResponse: |
| 1149 | + return FileResponse(path) |
| 1150 | + |
| 1151 | + async def passthrough( |
| 1152 | + request: Request, call_next: RequestResponseEndpoint |
| 1153 | + ) -> Response: |
| 1154 | + return await call_next(request) |
| 1155 | + |
| 1156 | + app = Starlette( |
| 1157 | + middleware=[Middleware(BaseHTTPMiddleware, dispatch=passthrough)], |
| 1158 | + routes=[Route("/", endpoint_with_pathsend)], |
| 1159 | + ) |
| 1160 | + |
| 1161 | + scope = { |
| 1162 | + "type": "http", |
| 1163 | + "version": "3", |
| 1164 | + "method": "GET", |
| 1165 | + "path": "/", |
| 1166 | + "extensions": {"http.response.pathsend": {}}, |
| 1167 | + } |
| 1168 | + |
| 1169 | + async def receive() -> Message: |
| 1170 | + nonlocal request_body_sent |
| 1171 | + if not request_body_sent: |
| 1172 | + request_body_sent = True |
| 1173 | + return {"type": "http.request", "body": b"", "more_body": False} |
| 1174 | + await response_complete.wait() |
| 1175 | + return {"type": "http.disconnect"} |
| 1176 | + |
| 1177 | + async def send(message: Message) -> None: |
| 1178 | + events.append(message) |
| 1179 | + if message["type"] == "http.response.pathsend": |
| 1180 | + response_complete.set() |
| 1181 | + |
| 1182 | + await app(scope, receive, send) |
| 1183 | + |
| 1184 | + assert len(events) == 2 |
| 1185 | + assert events[0]["type"] == "http.response.start" |
| 1186 | + assert events[1]["type"] == "http.response.pathsend" |
0 commit comments