]> git.ipfire.org Git - thirdparty/starlette.git/commitdiff
Add support for ASGI `pathsend` extension (#2435)
authorGiovanni Barillari <g@baro.dev>
Mon, 22 Jan 2024 09:44:40 +0000 (10:44 +0100)
committerGitHub <noreply@github.com>
Mon, 22 Jan 2024 09:44:40 +0000 (02:44 -0700)
* add support for ASGI `pathsend` extension

* add test for ASGI `pathsend`

starlette/responses.py
tests/test_responses.py

index 419816b7b1502a272fb38aa3858f5f8486e19eaa..9d259a075d4fb928f1da4fcff8234fed37e30c67 100644 (file)
@@ -337,6 +337,8 @@ class FileResponse(Response):
         )
         if scope["method"].upper() == "HEAD":
             await send({"type": "http.response.body", "body": b"", "more_body": False})
+        elif "http.response.pathsend" in scope["extensions"]:
+            await send({"type": "http.response.pathsend", "path": str(self.path)})
         else:
             async with await anyio.open_file(self.path, mode="rb") as file:
                 more_body = True
index 6bccd23f2f1662ec748fca8137c4db54e336cc0d..625a12956e3fe21b83b1d08b15be13c77ea3f766 100644 (file)
@@ -329,6 +329,38 @@ def test_file_response_with_method_warns(tmpdir, test_client_factory):
         FileResponse(path=tmpdir, filename="example.png", method="GET")
 
 
+@pytest.mark.anyio
+async def test_file_response_with_pathsend(tmpdir: Path):
+    path = os.path.join(tmpdir, "xyz")
+    content = b"<file content>" * 1000
+    with open(path, "wb") as file:
+        file.write(content)
+
+    app = FileResponse(path=path, filename="example.png")
+
+    async def receive() -> Message:  # type: ignore[empty-body]
+        ...  # pragma: no cover
+
+    async def send(message: Message) -> None:
+        if message["type"] == "http.response.start":
+            assert message["status"] == status.HTTP_200_OK
+            headers = Headers(raw=message["headers"])
+            assert headers["content-type"] == "image/png"
+            assert "content-length" in headers
+            assert "content-disposition" in headers
+            assert "last-modified" in headers
+            assert "etag" in headers
+        elif message["type"] == "http.response.pathsend":
+            assert message["path"] == str(path)
+
+    # Since the TestClient doesn't support `pathsend`, we need to test this directly.
+    await app(
+        {"type": "http", "method": "get", "extensions": {"http.response.pathsend": {}}},
+        receive,
+        send,
+    )
+
+
 def test_set_cookie(test_client_factory, monkeypatch):
     # Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
     mocked_now = dt.datetime(2037, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)