From 8ac99e49550963e875221d7963a384e26e5b787a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janek=20Nouvertn=C3=A9?= <25355197+provinzkraut@users.noreply.github.com> Date: Sat, 1 Feb 2025 11:01:58 +0100 Subject: [PATCH] fix connection state leak --- src/hypercorn/asyncio/tcp_server.py | 2 +- src/hypercorn/protocol/http_stream.py | 2 +- src/hypercorn/protocol/ws_stream.py | 2 +- src/hypercorn/trio/tcp_server.py | 2 +- tests/e2e/test_httpx.py | 43 +++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/hypercorn/asyncio/tcp_server.py b/src/hypercorn/asyncio/tcp_server.py index bf9d9fea..36919fb0 100644 --- a/src/hypercorn/asyncio/tcp_server.py +++ b/src/hypercorn/asyncio/tcp_server.py @@ -60,7 +60,7 @@ async def run(self) -> None: self.config, self.context, task_group, - ConnectionState(self.state.copy()), + ConnectionState(self.state), ssl, client, server, diff --git a/src/hypercorn/protocol/http_stream.py b/src/hypercorn/protocol/http_stream.py index 7ffac1de..1e59f700 100644 --- a/src/hypercorn/protocol/http_stream.py +++ b/src/hypercorn/protocol/http_stream.py @@ -97,7 +97,7 @@ async def handle(self, event: Event) -> None: "headers": event.headers, "client": self.client, "server": self.server, - "state": event.state, + "state": event.state.copy(), "extensions": {}, } diff --git a/src/hypercorn/protocol/ws_stream.py b/src/hypercorn/protocol/ws_stream.py index 7b39815d..be0e2fcf 100644 --- a/src/hypercorn/protocol/ws_stream.py +++ b/src/hypercorn/protocol/ws_stream.py @@ -219,7 +219,7 @@ async def handle(self, event: Event) -> None: "headers": event.headers, "client": self.client, "server": self.server, - "state": event.state, + "state": event.state.copy(), "subprotocols": self.handshake.subprotocols or [], "extensions": {"websocket.http.response": {}}, } diff --git a/src/hypercorn/trio/tcp_server.py b/src/hypercorn/trio/tcp_server.py index 5e2b633a..47d43668 100644 --- a/src/hypercorn/trio/tcp_server.py +++ b/src/hypercorn/trio/tcp_server.py @@ -63,7 +63,7 @@ async def run(self) -> None: self.config, self.context, task_group, - ConnectionState(self.state.copy()), + ConnectionState(self.state), ssl, client, server, diff --git a/tests/e2e/test_httpx.py b/tests/e2e/test_httpx.py index 9ce2cc74..b96978e9 100644 --- a/tests/e2e/test_httpx.py +++ b/tests/e2e/test_httpx.py @@ -55,3 +55,46 @@ async def serve() -> None: result.raise_for_status() shutdown.set() + + +@pytest.mark.trio +async def test_handle_isolate_state(): + config = Config() + config.bind = ["0.0.0.0:1234"] + config.accesslog = "-" # Log to stdout/err + config.errorlog = "-" + + async def app(scope, receive, send): + assert scope["type"] == "http" + + await send( + { + "type": "http.response.start", + "status": 200, + "headers": [[b"content-type", b"text/plain"]], + } + ) + await send( + {"type": "http.response.body", "body": scope["state"].get("key", b"")} + ) + scope["state"]["key"] = b"one" + + async with trio.open_nursery() as nursery: + shutdown = trio.Event() + + async def serve() -> None: + await hypercorn.trio.serve(app, config, shutdown_trigger=shutdown.wait) + + nursery.start_soon(serve) + + await trio.testing.wait_all_tasks_blocked() + + client = httpx.AsyncClient() + + result = await client.get("http://0.0.0.0:1234/") + assert result.content == b"" + + result = await client.get("http://0.0.0.0:1234/") + assert result.content == b"" + + shutdown.set()