Skip to content

Commit 0f8846b

Browse files
authored
fix: convert SecurityHeadersMiddleware to pure ASGI (open-webui#26924)
SecurityHeadersMiddleware was the last middleware in the stack still subclassing BaseHTTPMiddleware, after CommitSession, AuthToken, WebsocketUpgradeGuard and Redirect were all moved to pure ASGI in utils/asgi_middleware.py. BaseHTTPMiddleware re-buffers the response body through an anyio task group, which has known issues with streaming and Content-Length-bearing responses (e.g. the FileResponse returned by /api/v1/audio/speech). Reimplement it as a pure-ASGI middleware that stamps the configured security headers onto the http.response.start message via MutableHeaders and forwards all body chunks untouched, matching the pattern already used by its four siblings. set_security_headers() and all its helpers are unchanged. Co-authored-by: classic298 <classic298@users.noreply.github.com>
1 parent 42f5c3d commit 0f8846b

1 file changed

Lines changed: 25 additions & 7 deletions

File tree

backend/open_webui/utils/security_headers.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,33 @@
22
import re
33
from typing import Dict
44

5-
from fastapi import Request
6-
from starlette.middleware.base import BaseHTTPMiddleware
5+
from starlette.datastructures import MutableHeaders
6+
from starlette.types import ASGIApp, Message, Receive, Scope, Send
77

88

9-
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
10-
async def dispatch(self, request: Request, call_next):
11-
response = await call_next(request)
12-
response.headers.update(set_security_headers())
13-
return response
9+
class SecurityHeadersMiddleware:
10+
"""Apply configured security headers to every HTTP response.
11+
12+
Pure ASGI to avoid BaseHTTPMiddleware's response re-buffering. See
13+
open_webui.utils.asgi_middleware for the rationale.
14+
"""
15+
16+
def __init__(self, app: ASGIApp) -> None:
17+
self.app = app
18+
19+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
20+
if scope['type'] != 'http':
21+
await self.app(scope, receive, send)
22+
return
23+
24+
async def send_with_security_headers(message: Message) -> None:
25+
if message['type'] == 'http.response.start':
26+
headers = MutableHeaders(scope=message)
27+
for key, value in set_security_headers().items():
28+
headers[key] = value
29+
await send(message)
30+
31+
await self.app(scope, receive, send_with_security_headers)
1432

1533

1634
def set_security_headers() -> Dict[str, str]:

0 commit comments

Comments
 (0)