Skip to content

fix(filter): stream responses unbuffered unless a body feature is active - #34

Merged
rennf93 merged 3 commits into
masterfrom
fix/response-streaming-j1
Oct 17, 2026
Merged

rennf93 merged 3 commits into
masterfrom
fix/response-streaming-j1

Conversation

@rennf93

@rennf93 rennf93 commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Closes #32
Relates to #33

I replaced the always-on ContentCachingResponseWrapper in GuardFilter with a CommitHookResponse that streams the body and runs the process_response exit path (metrics, status based behavior rules, security headers, CORS) once, just before the first byte can leave: first write, flush, close, sendError, sendRedirect, or the completion of an async handler. This is the servlet analog of http.response.start in fastapi-guard guard/asgi.py. The buffering wrapper is kept only on requests where a custom_response_modifier is configured or a route or global return-pattern rule inspects the body. Guard-generated rejections are unchanged.

While there I made the buffered response implement BoundedResponseBodyReader. Without it the engine could never evaluate a body based return pattern in this adapter.

Tests added (GuardStreamingResponseTest, real Tomcat): a 64 MB StreamingResponseBody whose first chunk arrives before the handler finishes, SSE events arriving incrementally, a DeferredResult, a plain JSON response, a redirect, a status return pattern that bans without buffering, and a body return pattern that buffers only its route and still bans. All seven fail on master. A plain JSON endpoint measured over 10000 requests: 8.0 to 9.0 ms mean before, 8.2 to 9.0 ms after, about 6.4 to 6.7 MB allocated per request in both, so no regression.

For #33 I reproduced the failure with the published 1.1.0 in a Maven app (spring-boot-dependencies 3.5.6) and a Gradle app (Boot plugin 3.5.14): the BOM downgrades Kotlin and coroutines and startup fails with NoSuchMethodError runBlockingK. Aligning kotlin.version and the coroutines version fixes both, and I documented how in the README and docs/installation.md. The facade built against older coroutines would be an engine change, so it is not part of this PR.

@github-actions github-actions Bot added the documentation Docs site, README, CHANGELOG, license files label Oct 17, 2026
@rennf93
rennf93 merged commit 3cf5998 into master Oct 17, 2026
9 checks passed
@rennf93
rennf93 deleted the fix/response-streaming-j1 branch October 17, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Docs site, README, CHANGELOG, license files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GuardFilter buffers every response, so streamed downloads and SSE are held in memory

1 participant