Middleware — current state¶
Version: 0.4 · Last Updated: 2026-09-08 · Status: 🔴 evidence refreshed; design ratification unchanged
Verified against source revision 2465fcc (develop baseline). Test references
below identify the executable contracts; they are not a new coverage percentage.
Chain assembly and effective defaults¶
MiddlewareMixin builds the chain once from its registry and switches. Lowest
middleware_order is outermost. Unknown names and unconsumed constructor options
raise ValueError and TypeError respectively. get_middleware(cls) reads an
installed layer, or returns None.
| Layer | Order | Class default |
|---|---|---|
| ErrorMiddleware | 100 | enabled |
| WellKnownMiddleware | 150 | disabled |
| LoggingMiddleware | 200 | disabled |
| CORSMiddleware | 300 | disabled |
| SessionMiddleware | 400 | disabled |
| AuthMiddleware | 450 | disabled |
AsgiServer nevertheless enables session and auth through their capability
mixins' setdefault; an explicit False wins. Therefore a bare composition
normally has errors, session and auth. Extra registry classes are Python
constructor options; the recipe middleware element names only the six built-ins.
Claim anchors: MiddlewareMixin, get_middleware.
HTTP errors and challenges¶
Only HTTP enters the middleware chain. ErrorMiddleware records whether a
response started; an error after start is re-raised rather than answered twice.
Before start, an HTTPException retains its status and headers, a Redirect
retains its Location, and other exceptions become a generic logged 500.
With login enabled, a 401 from browser navigation becomes a 302 to
/_server/login_page with a validated next path; an API request receives a
401 with login_url. Error bodies otherwise follow Accept negotiation.
Response.ERROR_MAP is a standalone helper table, not the middleware policy.
The error middleware writes through its own outer send. Thus an error
response bypasses inner CORS/session response wrappers and may lack their
headers. Disabling errors is accepted and allows exceptions to escape the
composition. Those limitations are not resolved by this documentation update.
Claim anchors: ErrorMiddleware.
Behavior evidence: _challenge_response, _error_response.
Sessions, authentication, CORS and logging¶
SessionMiddleware reconnects or creates a session and writes back only dirty
sessions. It sets the cookie only for a new session, with Max-Age = ttl * 24.
AuthMiddleware writes server.authenticate(scope) to scope['auth'] after
the session layer has run. Shared header and cookie readers live in base.py.
CORS handles preflight and response headers. Wellknown raises 404 for its
reserved paths. Logging records request outcome and duration; its level
selects the emitted severity, and an unknown level falls back to INFO.
Claim anchors: SessionMiddleware, AuthMiddleware.
Behavior evidence: CORSMiddleware, LoggingMiddleware.
WSX handshake boundary¶
WebSocket and lifespan scopes bypass the chain. WsxConnection checks Origin
and obtains handshake identity/session directly, using get_middleware and
SessionMiddleware.get_session as read helpers. A raw serve_websocket
application owns its Origin/authentication policy after the server's state gate.
The absence of a WebSocket middleware pass is not the absence of an Origin gate.
Claim anchors: WsxConnection, get_middleware, SessionMiddleware, get_session.
Source and test evidence¶
- src/genro_asgi/middleware/init.py
- src/genro_asgi/middleware/base.py
- src/genro_asgi/middleware/errors.py
- src/genro_asgi/middleware/cors.py
- src/genro_asgi/middleware/session.py
- src/genro_asgi/middleware/authentication.py
- src/genro_asgi/middleware/logging.py
- src/genro_asgi/middleware/wellknown.py
- src/genro_asgi/wsx.py
- tests/core/test_middleware.py
- tests/core/test_middleware_std.py
- tests/core/test_login_flow.py
- tests/core/test_wsx_connection.py