|
2 | 2 |
|
3 | 3 | from fastapi import Request |
4 | 4 | from pyinstrument import Profiler |
5 | | -from starlette.middleware.base import ( |
6 | | - BaseHTTPMiddleware, |
7 | | - RequestResponseEndpoint, |
8 | | -) |
9 | 5 | from starlette.responses import HTMLResponse, Response |
10 | 6 |
|
11 | 7 |
|
12 | | -class ProfilingMiddleware(BaseHTTPMiddleware): |
13 | | - async def dispatch( |
14 | | - self, request: Request, call_next: RequestResponseEndpoint |
15 | | - ) -> Response: |
| 8 | +class ProfilingMiddleware: |
| 9 | + def __init__(self, app): |
| 10 | + self.app = app |
| 11 | + |
| 12 | + async def __call__(self, scope, receive, send) -> Response: |
| 13 | + # Only handle HTTP requests; pass through other scope types (e.g. WebSocket). |
| 14 | + if scope.get("type") != "http": |
| 15 | + return await self.app(scope, receive, send) |
| 16 | + |
| 17 | + request = Request(scope, receive=receive) |
| 18 | + |
16 | 19 | if request.query_params.get("pyprofile") == "true": |
17 | 20 | profiler = Profiler(interval=0.001, async_mode="enabled") |
18 | 21 | profiler.start() |
19 | 22 |
|
20 | | - await call_next(request) |
| 23 | + async def _inner_send(message): |
| 24 | + # Swallow downstream response messages; we replace the response |
| 25 | + # entirely with the profiler HTML output, matching the previous behavior. |
| 26 | + return |
| 27 | + |
| 28 | + await self.app(scope, receive, _inner_send) |
21 | 29 |
|
22 | 30 | profiler.stop() |
23 | | - return HTMLResponse( |
| 31 | + response = HTMLResponse( |
24 | 32 | profiler.output_html(), |
25 | | - headers={"Content-Disposition": "attachment; filename=profile.html"}, |
| 33 | + headers={ |
| 34 | + "Content-Disposition": "attachment; filename=profile.html" |
| 35 | + }, |
26 | 36 | ) |
| 37 | + return await response(scope, receive, send) |
27 | 38 |
|
28 | | - return await call_next(request) |
| 39 | + return await self.app(scope, receive, send) |
0 commit comments