Thanks to visit codestin.com
Credit goes to github.com

Skip to content

a2wsgi can't work with BaseHTTPMiddleware in Starlette #45

Description

@fnep

For the sake of backwards compatibility, I'm running a complicated stack of apache (mod_wsgi) -> a2wsgi (ASGIMiddleware) -> fastapi, and with the update of fastapi==0.107.0 this broke, after they have internally switched to a relatively new version of Starlette (0.28.0).

In apache I'm getting the error RuntimeError: Unexpected message received: http.request, but this is here only to make this issue easier to find later.

I now tried to reduce this stack to make it easier to understand and think the a2wsgi part might be the issue or at least you may know what it actually is.

Based on the starlette hello-world i added a custom middleware and the a2wsgi.ASGIMiddleware. Removing the custom middle middleware fixes the issue (but removes the functionality).

from starlette.applications import Starlette
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware import Middleware
from starlette.responses import JSONResponse
from starlette.routing import Route
from a2wsgi import ASGIMiddleware


class HeaderAddTestMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers["X-Test"] = "Test"
        return response


async def homepage(request):
    return JSONResponse({"hello": "world"})


app = Starlette(
    debug=True,
    middleware=[
        Middleware(HeaderAddTestMiddleware),
    ],
    routes=[
        Route("/", homepage),
    ],
)

application = ASGIMiddleware(app)

To run it i use uwsgi like this uwsgi --http :8000 --wsgi-file main.py.

Using starlette==0.27.0 and a2wsgi==1.10.0 this just works, but with starlette==0.28.0 and later (current version is starlette==0.37.0) i get this error on request.

❯ curl -v http://127.0.0.1:8000
*   Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 17
< content-type: application/json
< x-test: Test
<
* Connection #0 to host 127.0.0.1 left intact
{"hello":"world"}
[...]
[pid: 17764|app: 0|req: 28/28] 127.0.0.1 () {28 vars in 291 bytes} [Wed Feb  7 09:22:15 2024] GET / => generated 17 bytes in 5 msecs (HTTP/1.1 200) 2 headers in 71 bytes (1 switches on core 0)
Traceback (most recent call last):
  File ".venv/lib/python3.12/site-packages/a2wsgi/asgi.py", line 235, in __call__
    yield from self.error_response(start_response, message["exception"])
  File ".venv/lib/python3.12/site-packages/a2wsgi/asgi.py", line 266, in error_response
    start_response(
OSError: headers already sent
[pid: 17764|app: 0|req: 29/29] 127.0.0.1 () {28 vars in 291 bytes} [Wed Feb  7 09:22:17 2024] GET / => generated 17 bytes in 7 msecs (HTTP/1.1 200) 2 headers in 71 bytes (2 switches on core 0)
[...]

The hello-world response gets shipped anyway, and even the header is set as wanted. It also gives this error when i just return await call_next(request) in the dispatch method without actually modifying any header.

I would normally assume the issue is somewhere in BaseHTTPMiddleware, but running the same code using asgi via uvicorn (uvicorn main:app) it also works.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions