Skip to content

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