mirror of
https://github.com/open-webui/open-webui.git
synced 2026-07-16 23:21:44 -05:00
[PR #23709] [MERGED] fix(middleware): replace BaseHTTPMiddleware HTTP middlewares with pure ASGI implementations #114638
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/23709
Author: @Classic298
Created: 4/14/2026
Status: ✅ Merged
Merged: 4/14/2026
Merged by: @tjbck
Base:
dev← Head:claude/fix/pure-asgi-middleware📝 Commits (2)
225e62afix(middleware): replace BaseHTTPMiddleware HTTP middlewares with pure ASGI implementations3a20b2dfix(middleware): CommitSessionMiddleware — rollback on downstream error, never commit failed requests📊 Changes
2 files changed (+290 additions, -96 deletions)
View changed files
📝
backend/open_webui/main.py(+17 -96)➕
backend/open_webui/utils/asgi_middleware.py(+273 -0)📄 Description
Starlette's BaseHTTPMiddleware (and the @app.middleware('http') decorator that uses it) wraps the downstream app in an anyio task group whose cancel scope tears down the inner task on every exit — client disconnect, response complete, or any outer middleware bailing. That CancelledError gets injected into whatever the inner task was awaiting, so DB queries, embedding calls, and other long awaits get killed mid-flight. Under aiosqlite the cleanup path then logs a multi-page
terminate_force_close() not implementedtraceback at ERROR for every cancelled DB call.Open WebUI had four such middlewares stacked
(
commit_session_after_request,check_url,inspect_websocket,RedirectMiddleware) so a single cancellation would compound through all four.Move the four middlewares to a new
open_webui.utils.asgi_middlewaremodule as plain ASGI classes (__call__(scope, receive, send)):CommitSessionMiddleware— wascommit_session_after_request;now also rolls back if commit fails
before releasing the connection.
AuthTokenMiddleware— wascheck_url; sets request.statetoken + enable_api_keys + stamps
X-Process-Time via a wrapped send.
WebsocketUpgradeGuardMiddleware— was
inspect_websocket; rejects/ws/socket.io HTTP requests that
claim transport=websocket without a
proper Upgrade/Connection header.
RedirectMiddleware— was the BaseHTTPMiddleware subclass;same /watch + share-target rewrites.
Pure ASGI does not introduce a cancel scope around the downstream app, so client disconnects propagate via
receive()(the way ASGI was designed) instead of being injected as CancelledError. Middleware ordering is preserved.Contributor License Agreement
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.