0
# Middleware
1
2
Built-in middleware components for common server functionality including WSGI adaptation, request routing, HTTPS redirection, and proxy header handling. These middleware classes provide reusable components for modifying request and response behavior.
3
4
## Capabilities
5
6
### WSGI Middleware for Asyncio
7
8
Middleware that enables WSGI application execution within asyncio event loops, handling the synchronous-to-asynchronous adaptation.
9
10
```python { .api }
11
class AsyncioWSGIMiddleware:
12
"""
13
WSGI middleware adapter for asyncio event loops.
14
15
Enables WSGI applications to run within asyncio-based servers
16
by providing the necessary synchronous execution context and
17
handling the translation between WSGI's blocking interface
18
and asyncio's non-blocking event loop.
19
"""
20
21
def __init__(self, wsgi_app: WSGIFramework, max_body_size: int = 16*1024*1024):
22
"""
23
Initialize asyncio WSGI middleware.
24
25
Args:
26
wsgi_app: WSGI application to wrap
27
max_body_size: Maximum request body size in bytes
28
"""
29
30
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
31
"""
32
Process request through WSGI application.
33
34
Args:
35
scope: ASGI scope dictionary
36
receive: ASGI receive callable
37
send: ASGI send callable
38
"""
39
```
40
41
### WSGI Middleware for Trio
42
43
Middleware that enables WSGI application execution within trio async frameworks, providing structured concurrency support.
44
45
```python { .api }
46
class TrioWSGIMiddleware:
47
"""
48
WSGI middleware adapter for trio async framework.
49
50
Enables WSGI applications to run within trio-based servers
51
with support for trio's structured concurrency model and
52
cancellation semantics.
53
"""
54
55
def __init__(self, wsgi_app: WSGIFramework, max_body_size: int = 16*1024*1024):
56
"""
57
Initialize trio WSGI middleware.
58
59
Args:
60
wsgi_app: WSGI application to wrap
61
max_body_size: Maximum request body size in bytes
62
"""
63
64
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
65
"""
66
Process request through WSGI application.
67
68
Args:
69
scope: ASGI scope dictionary
70
receive: ASGI receive callable
71
send: ASGI send callable
72
"""
73
```
74
75
### Dispatcher Middleware
76
77
Middleware for routing requests to different applications based on URL paths, enabling application composition and microservice architectures.
78
79
```python { .api }
80
class DispatcherMiddleware:
81
"""
82
Route requests to different applications based on path prefixes.
83
84
This is an alias for AsyncioDispatcherMiddleware for backward compatibility.
85
Enables mounting multiple applications at different URL paths,
86
similar to URL routing but at the application level.
87
"""
88
89
def __init__(self, mounts: dict[str, ASGIFramework]):
90
"""
91
Initialize dispatcher middleware.
92
93
Args:
94
mounts: Dictionary mapping path prefixes to ASGI applications
95
Format: {"/api": api_app, "/admin": admin_app}
96
"""
97
98
async def __call__(self, scope: Scope, receive: Callable, send: Callable):
99
"""
100
Route request to appropriate application.
101
102
Args:
103
scope: ASGI scope dictionary (path is examined for routing)
104
receive: ASGI receive callable
105
send: ASGI send callable
106
107
Routes based on longest matching path prefix.
108
"""
109
110
class AsyncioDispatcherMiddleware:
111
"""
112
Asyncio-specific dispatcher middleware for routing requests.
113
114
Routes requests to different applications based on path prefixes,
115
optimized for asyncio event loops.
116
"""
117
118
def __init__(self, mounts: dict[str, ASGIFramework]):
119
"""
120
Initialize asyncio dispatcher middleware.
121
122
Args:
123
mounts: Dictionary mapping path prefixes to ASGI applications
124
"""
125
126
async def __call__(self, scope: Scope, receive: Callable, send: Callable):
127
"""
128
Route request to appropriate application.
129
130
Args:
131
scope: ASGI scope dictionary
132
receive: ASGI receive callable
133
send: ASGI send callable
134
"""
135
136
class TrioDispatcherMiddleware:
137
"""
138
Trio-specific dispatcher middleware for routing requests.
139
140
Routes requests to different applications based on path prefixes,
141
optimized for trio async framework with structured concurrency.
142
"""
143
144
def __init__(self, mounts: dict[str, ASGIFramework]):
145
"""
146
Initialize trio dispatcher middleware.
147
148
Args:
149
mounts: Dictionary mapping path prefixes to ASGI applications
150
"""
151
152
async def __call__(self, scope: Scope, receive: Callable, send: Callable):
153
"""
154
Route request to appropriate application.
155
156
Args:
157
scope: ASGI scope dictionary
158
receive: ASGI receive callable
159
send: ASGI send callable
160
"""
161
```
162
163
### HTTP to HTTPS Redirect Middleware
164
165
Middleware that automatically redirects HTTP requests to their HTTPS equivalents, enforcing secure connections.
166
167
```python { .api }
168
class HTTPToHTTPSRedirectMiddleware:
169
"""
170
Redirect HTTP requests to HTTPS.
171
172
Automatically redirects all HTTP requests to their HTTPS
173
equivalents with a 301 permanent redirect. Useful for
174
enforcing HTTPS-only access to applications.
175
"""
176
177
def __init__(self, app: ASGIFramework | WSGIFramework, host: str | None = None):
178
"""
179
Initialize HTTPS redirect middleware.
180
181
Args:
182
app: Application to wrap (receives HTTPS requests)
183
host: Optional host to redirect to. If None, uses request host
184
"""
185
186
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
187
"""
188
Process request and redirect HTTP to HTTPS.
189
190
Args:
191
scope: ASGI scope dictionary (scheme is checked)
192
receive: ASGI receive callable
193
send: ASGI send callable
194
195
HTTP requests receive a 301 redirect to the HTTPS URL.
196
HTTPS requests are passed through to the wrapped application.
197
"""
198
```
199
200
### Proxy Fix Middleware
201
202
Middleware for handling proxy headers to correctly determine client information when running behind reverse proxies or load balancers.
203
204
```python { .api }
205
class ProxyFixMiddleware:
206
"""
207
Handle proxy headers for client information.
208
209
Corrects client IP addresses, schemes, and hosts when running
210
behind reverse proxies or load balancers by processing standard
211
proxy headers like X-Forwarded-For, X-Forwarded-Proto, etc.
212
"""
213
214
def __init__(
215
self,
216
app: ASGIFramework | WSGIFramework,
217
mode: Literal["legacy", "modern"] = "legacy",
218
trusted_hops: int = 1
219
):
220
"""
221
Initialize proxy fix middleware.
222
223
Args:
224
app: Application to wrap
225
mode: Header parsing mode ("legacy" or "modern")
226
trusted_hops: Number of proxy hops to trust
227
228
The trusted_hops parameter controls how many proxy entries
229
are trusted to prevent header spoofing from untrusted sources.
230
"""
231
232
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
233
"""
234
Process request and fix proxy headers.
235
236
Args:
237
scope: ASGI scope dictionary (headers are modified)
238
receive: ASGI receive callable
239
send: ASGI send callable
240
241
Modifies the scope to reflect the actual client information
242
based on trusted proxy headers.
243
"""
244
```
245
246
## Usage Examples
247
248
### WSGI Middleware Usage
249
250
```python
251
from hypercorn.middleware import AsyncioWSGIMiddleware
252
253
def wsgi_app(environ, start_response):
254
status = '200 OK'
255
headers = [('Content-Type', 'text/plain')]
256
start_response(status, headers)
257
return [b'Hello from WSGI middleware']
258
259
# Wrap WSGI app for asyncio
260
middleware = AsyncioWSGIMiddleware(wsgi_app, max_body_size=1024*1024)
261
262
# Use with Hypercorn
263
from hypercorn.config import Config
264
from hypercorn.asyncio import serve
265
266
config = Config()
267
asyncio.run(serve(middleware, config))
268
```
269
270
### Dispatcher Middleware
271
272
```python
273
from hypercorn.middleware import DispatcherMiddleware
274
275
# Define different applications
276
async def api_app(scope, receive, send):
277
await send({'type': 'http.response.start', 'status': 200})
278
await send({'type': 'http.response.body', 'body': b'API Response'})
279
280
async def admin_app(scope, receive, send):
281
await send({'type': 'http.response.start', 'status': 200})
282
await send({'type': 'http.response.body', 'body': b'Admin Panel'})
283
284
# Create dispatcher
285
dispatcher = DispatcherMiddleware({
286
'/api': api_app,
287
'/admin': admin_app,
288
'/': main_app # Default application
289
})
290
291
# Requests to /api/* go to api_app
292
# Requests to /admin/* go to admin_app
293
# All other requests go to main_app
294
```
295
296
### HTTPS Redirect Middleware
297
298
```python
299
from hypercorn.middleware import HTTPToHTTPSRedirectMiddleware
300
301
# Wrap application to enforce HTTPS
302
https_app = HTTPToHTTPSRedirectMiddleware(app)
303
304
# Or redirect to specific host
305
https_app = HTTPToHTTPSRedirectMiddleware(app, host="secure.example.com")
306
307
# HTTP requests get 301 redirect to HTTPS
308
# HTTPS requests are passed through to app
309
```
310
311
### Proxy Fix Middleware
312
313
```python
314
from hypercorn.middleware import ProxyFixMiddleware
315
316
# Basic proxy fix (trust 1 level of proxies)
317
proxy_app = ProxyFixMiddleware(app)
318
319
# Behind multiple proxies/load balancers
320
proxy_app = ProxyFixMiddleware(
321
app,
322
x_for=2, # Trust last 2 X-Forwarded-For entries
323
x_proto=1, # Trust last 1 X-Forwarded-Proto entry
324
x_host=1, # Trust last 1 X-Forwarded-Host entry
325
x_prefix=1 # Trust last 1 X-Forwarded-Prefix entry
326
)
327
328
# Now scope['client'] reflects actual client IP
329
# scope['scheme'] reflects actual scheme (http/https)
330
```
331
332
### Combining Middleware
333
334
```python
335
from hypercorn.middleware import (
336
ProxyFixMiddleware,
337
HTTPToHTTPSRedirectMiddleware,
338
DispatcherMiddleware
339
)
340
341
# Layer middleware (innermost to outermost)
342
app = my_asgi_app
343
344
# Fix proxy headers first
345
app = ProxyFixMiddleware(app, x_for=1, x_proto=1)
346
347
# Then enforce HTTPS
348
app = HTTPToHTTPSRedirectMiddleware(app)
349
350
# Finally, add routing if needed
351
apps = {'/api': api_app, '/': app}
352
final_app = DispatcherMiddleware(apps)
353
354
# Use final_app with Hypercorn
355
```
356
357
### Custom Middleware Pattern
358
359
```python
360
class CustomMiddleware:
361
def __init__(self, app):
362
self.app = app
363
364
async def __call__(self, scope, receive, send):
365
# Pre-processing
366
if scope['type'] == 'http':
367
# Modify request
368
scope['headers'].append([b'x-custom', b'value'])
369
370
# Call wrapped application
371
await self.app(scope, receive, self.send_wrapper(send))
372
373
def send_wrapper(self, send):
374
async def wrapped_send(message):
375
# Post-processing
376
if message['type'] == 'http.response.start':
377
# Modify response headers
378
message.setdefault('headers', [])
379
message['headers'].append([b'x-processed', b'true'])
380
await send(message)
381
return wrapped_send
382
383
# Usage
384
wrapped_app = CustomMiddleware(original_app)
385
```