Adapter parity: fastapi-guard 8.1.0 (PureASGISecurityMiddleware)
Assessment of the W10 parity wave (2026-10-13): what fastapi-guard 8.1.0 changed, how this adapter's native idiom maps onto it, and which of the pure-streaming properties hold here. Conclusion up front: the servlet filter idiom already satisfies the cancellation and body-replay properties; the unbuffered-streaming property does NOT hold (the response wrapper buffers), which is a deliberate, documented trade of this adapter and not something this wave changes.
What upstream shipped
fastapi-guard 8.1.0 (2026-10-10, #156) added PureASGISecurityMiddleware: the
full screening pipeline running on raw ASGI without BaseHTTPMiddleware. It
exists to win three properties the classic middleware loses to Starlette's
anyio task group:
- No downstream wrapping that could cancel in-flight work (a client disconnect can never cancel an in-flight database call).
- Unbuffered response streaming (security and CORS headers injected directly
on
http.response.start, body bytes never held). - A caching receive that replays the request body to the application after the guard reads it.
Scope note from the upstream release: decorator-driven response rules (return
patterns) and custom_response_modifier need a materialized Response
object; they stay on the classic middleware and both classes ship side by
side.
The idiom mapping
This adapter's native idiom is the servlet envelope: GuardFilter, a Spring
OncePerRequestFilter placed pre-routing so unmatched paths are also
scanned. The servlet container hands the filter the raw request/response and
the downstream chain; there is no BaseHTTPMiddleware analog to avoid -
nothing ever wrapped the downstream app in a task group here. The PureASGI
release validates an idiom this family already holds natively, the same way
fastapi-guard's Starlette-only constraint never applied to the servlet lane.
Property-by-property
1. No downstream cancelling wrap: HOLDS
GuardFilter.doFilterInternal runs filterChain.doFilter synchronously on
the same servlet thread. No coroutine scope wraps the downstream call: the
guard's own suspend stages bridge through runBlocking confined to the guard
entry point (the facade's xBlocking bridge convention), and even the
security-bypass path's downstream leg runs under plain runBlocking, which
has no cancellation source. A client disconnect cannot cancel in-flight
handler work in this model; it surfaces later, as a write failure on the
committed response. This is the property PureASGI exists to win on Starlette;
the servlet lane never had the problem.
2. A caching receive that replays the body: HOLDS
GuardHttpServletRequest (ServletGuardRequest.kt) reads the servlet input
stream exactly once into a single-source byte cache and hands the downstream
chain a replaying ServletInputStream: the bytes the penetration scan saw
are the bytes the handler sees, with bounded prefix reads stopping the wire
pull at the cap. This is the exact contract of the reference's caching
receive (and Starlette's own _body cache it mirrors).
3. Unbuffered response streaming: HOLDS since the Unreleased change (history below)
Update: GuardFilter now streams by default. It wraps the response in CommitHookResponse, which runs the facade's process_response exit path and the CORS injection once, just before the first byte can leave (first write, flush, close, sendError, sendRedirect, or the completion of an async handler), the servlet analog of http.response.start. Only requests where a custom_response_modifier is configured or a route or global return-pattern rule inspects the body keep the buffering wrapper. The finding below is the original W10 assessment, kept for history.
GuardFilter wraps the response in Spring's ContentCachingResponseWrapper
and calls copyBodyToResponse() only after the facade's process_response
exit path and CORS injection run. Every response body is fully materialized
in memory before the first byte reaches the client: SSE streams, file
downloads, and large bodies included. The wrapper is what lets the adapter
post-process a handler's response before it commits (behavioral return rules,
the custom response modifier, response-time metrics, CORS into the buffered
response), which is the classic-middleware feature set.
This is the trade fastapi-guard 8.1.0's scope note describes, seen from the
other side: response rules and the response modifier need a materialized
response, so they live on the buffering idiom. The servlet adapter IS the
classic middleware of the JVM family; the buffering is its cost, not a parity
bug. Reported, not fixed, per the W10 scope: the fix shape, if ever needed
for SSE-at-scale deployments, is a second header-pre-commit filter variant
shipping beside GuardFilter (the PureASGI-beside-SecurityMiddleware
pattern), not a change to GuardFilter's contract.
Verdict
Zero code changes in the 8.1.0 parity wave. Two of the three pure-streaming properties hold natively; the third (unbuffered streaming) is a documented deliberate trade of the buffering idiom this adapter is built on, priced exactly as upstream prices it: the features that need a materialized response are the features this adapter ships.