Unverified Commit 4cce1e35 authored by mvdbeek's avatar mvdbeek
Browse files

Add empty_response_middleware

Fixes noisy exceptions on routes that return no content, eg
https://sentry.galaxyproject.org/share/issue/8c46339f05c94f6a8c85c44bc71aea6a/
:
```
WouldBlock: null
  File "anyio/streams/memory.py", line 94, in receive
    return self.receive_nowait()
  File "anyio/streams/memory.py", line 89, in receive_nowait
    raise WouldBlock
EndOfStream: null
  File "starlette/middleware/base.py", line 43, in call_next
    message = await recv_stream.receive()
  File "anyio/streams/memory.py", line 114, in receive
    raise EndOfStream
RuntimeError: No response returned.
  File "uvicorn/protocols/http/h11_impl.py", line 366, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "uvicorn/middleware/proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "fastapi/applications.py", line 269, in __call__
    await super().__call__(scope, receive, send)
  File "starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "starlette_context/middleware/raw_middleware.py", line 96, in __call__
    await self.app(scope, receive, send_wrapper)
  File "starlette/middleware/base.py", line 68, in __call__
    response = await self.dispatch_func(request, call_next)
  File "galaxy/webapps/galaxy/fast_app.py", line 119, in add_send_file_header
    response = await call_next(request)
  File "starlette/middleware/base.py", line 47, in call_next
    raise RuntimeError("No response returned.")
```
on `/api/histories/14a1ba6a16ee6e2c/contents/bulk`.
parent 533a33f9
Loading
Loading
Loading
Loading
+22 −6
Original line number Diff line number Diff line
from fastapi import (
    FastAPI,
    Request,
    status,
)
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.middleware.base import (
    BaseHTTPMiddleware,
    RequestResponseEndpoint,
)
from starlette.responses import Response

try:
from starlette_context.middleware import RawContextMiddleware
from starlette_context.plugins import RequestIdPlugin
except ImportError:
    pass

from galaxy.exceptions import MessageException
from galaxy.web.framework.base import walk_controller_modules
@@ -20,6 +21,21 @@ from galaxy.web.framework.decorators import (
)


# Copied from https://stackoverflow.com/questions/71222144/runtimeerror-no-response-returned-in-fastapi-when-refresh-request/72677699#72677699
class SuppressNoResponseReturnedMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
        try:
            return await call_next(request)
        except RuntimeError as exc:
            if str(exc) == "No response returned." and await request.is_disconnected():
                return Response(status_code=status.HTTP_204_NO_CONTENT)
            raise


def add_empty_response_middleware(app: FastAPI) -> None:
    app.add_middleware(SuppressNoResponseReturnedMiddleware)


def add_exception_handler(app: FastAPI) -> None:
    @app.exception_handler(RequestValidationError)
    async def validate_exception_middleware(request: Request, exc: RequestValidationError) -> Response:
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ from starlette.responses import (

from galaxy.version import VERSION
from galaxy.webapps.base.api import (
    add_empty_response_middleware,
    add_exception_handler,
    add_request_id_middleware,
    include_all_package_routers,
@@ -176,6 +177,7 @@ def initialize_fast_app(gx_wsgi_webapp, gx_app):
    wsgi_handler = WSGIMiddleware(gx_wsgi_webapp)
    gx_app.haltables.append(("WSGI Middleware threadpool", wsgi_handler.executor.shutdown))
    app.mount("/", wsgi_handler)
    add_empty_response_middleware(app)
    if gx_app.config.galaxy_url_prefix != "/":
        parent_app = FastAPI()
        parent_app.mount(gx_app.config.galaxy_url_prefix, app=app)
+2 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from a2wsgi import WSGIMiddleware
from fastapi import FastAPI

from galaxy.webapps.base.api import (
    add_empty_response_middleware,
    add_exception_handler,
    add_request_id_middleware,
    include_all_package_routers,
@@ -22,4 +23,5 @@ def initialize_fast_app(gx_webapp):
    include_all_package_routers(app, "galaxy.webapps.reports.api")
    wsgi_handler = WSGIMiddleware(gx_webapp)
    app.mount("/", wsgi_handler)
    add_empty_response_middleware(app)
    return app
+2 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from a2wsgi import WSGIMiddleware
from fastapi import FastAPI

from galaxy.webapps.base.api import (
    add_empty_response_middleware,
    add_exception_handler,
    add_request_id_middleware,
    include_all_package_routers,
@@ -20,6 +21,7 @@ def initialize_fast_app(gx_webapp, tool_shed_app):
    wsgi_handler = WSGIMiddleware(gx_webapp)
    tool_shed_app.haltables.append(("WSGI Middleware threadpool", wsgi_handler.executor.shutdown))
    app.mount("/", wsgi_handler)
    add_empty_response_middleware(app)
    return app