]> git.ipfire.org Git - thirdparty/starlette.git/commitdiff
Mark ExceptionMiddleware.http_exception as async to prevent thread creation. (#2922)
authorDan Lapid <dan.lapid@gmail.com>
Wed, 16 Apr 2025 12:57:27 +0000 (13:57 +0100)
committerGitHub <noreply@github.com>
Wed, 16 Apr 2025 12:57:27 +0000 (07:57 -0500)
starlette/middleware/exceptions.py
tests/test_exceptions.py

index 981d2fcaef87c2d3fadb54c83afeb88ccdbaf561..a99b44deee93c833eae1a87cfa99772050960735 100644 (file)
@@ -61,7 +61,7 @@ class ExceptionMiddleware:
 
         await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
 
-    def http_exception(self, request: Request, exc: Exception) -> Response:
+    async def http_exception(self, request: Request, exc: Exception) -> Response:
         assert isinstance(exc, HTTPException)
         if exc.status_code in {204, 304}:
             return Response(status_code=exc.status_code, headers=exc.headers)
index fe5da0ba685ffb9160fd59711d30f351ea49e79c..8682456509320b80423b9fd8ef611f5c4a8cf80d 100644 (file)
@@ -1,6 +1,8 @@
+import typing
 from collections.abc import Generator
 
 import pytest
+from pytest import MonkeyPatch
 
 from starlette.exceptions import HTTPException, WebSocketException
 from starlette.middleware.exceptions import ExceptionMiddleware
@@ -184,3 +186,22 @@ def test_request_in_app_and_handler_is_the_same_object(client: TestClient) -> No
     response = client.post("/consume_body_in_endpoint_and_handler", content=b"Hello!")
     assert response.status_code == 422
     assert response.json() == {"body": "Hello!"}
+
+
+def test_http_exception_does_not_use_threadpool(client: TestClient, monkeypatch: MonkeyPatch) -> None:
+    """
+    Verify that handling HTTPException does not invoke run_in_threadpool,
+    confirming the handler correctly runs in the main async context.
+    """
+    from starlette import _exception_handler
+
+    # Replace run_in_threadpool with a function that raises an error
+    def mock_run_in_threadpool(*args: typing.Any, **kwargs: typing.Any) -> None:
+        pytest.fail("run_in_threadpool should not be called for HTTP exceptions")  # pragma: no cover
+
+    # Apply the monkeypatch only during this test
+    monkeypatch.setattr(_exception_handler, "run_in_threadpool", mock_run_in_threadpool)
+
+    # This should succeed because http_exception is async and won't use run_in_threadpool
+    response = client.get("/not_acceptable")
+    assert response.status_code == 406