fix(middleware): gate only body response modifiers

Replace the rewrite requirement check with a BodyResponseModifier
marker and treat header and body modifiers separately.

This ensures header/status rewrites still apply when body rewriting is
blocked (for binary, encoded, or chunked responses), while body changes
are skipped safely. It also avoids body reset/close side effects and
returns early on passthrough responses.

Update middleware tests to cover split header/body behavior and themed
middleware body-skip scenarios.
This commit is contained in:
yusing
2026-03-01 04:09:50 +08:00
parent 59238adb5b
commit 41de86de75
7 changed files with 118 additions and 144 deletions

View File

@@ -54,7 +54,11 @@ type (
RequestModifier interface {
before(w http.ResponseWriter, r *http.Request) (proceed bool)
}
ResponseModifier interface{ modifyResponse(r *http.Response) error }
ResponseModifier interface{ modifyResponse(r *http.Response) error }
BodyResponseModifier interface {
ResponseModifier
isBodyResponseModifier()
}
MiddlewareWithSetup interface{ setup() }
MiddlewareFinalizer interface{ finalize() }
MiddlewareFinalizerWithError interface {
@@ -208,8 +212,15 @@ func (m *Middleware) ServeHTTP(next http.HandlerFunc, w http.ResponseWriter, r *
next(w, r)
return
}
isBodyModifier := isBodyResponseModifier(exec)
lrm := httputils.NewLazyResponseModifier(w, canBufferAndModifyResponseBody)
shouldBuffer := canBufferAndModifyResponseBody
if !isBodyModifier {
// Header-only response modifiers do not need body rewrite capability checks.
// We still respect max buffer limits and may fall back to passthrough for large bodies.
shouldBuffer = func(http.Header) bool { return true }
}
lrm := httputils.NewLazyResponseModifier(w, shouldBuffer)
lrm.SetMaxBufferedBytes(maxModifiableBody)
defer func() {
_, err := lrm.FlushRelease()
@@ -225,6 +236,9 @@ func (m *Middleware) ServeHTTP(next http.HandlerFunc, w http.ResponseWriter, r *
}
rm := lrm.ResponseModifier()
if rm.IsPassthrough() {
return
}
currentBody := rm.BodyReader()
currentResp := &http.Response{
StatusCode: rm.StatusCode(),
@@ -246,7 +260,7 @@ func (m *Middleware) ServeHTTP(next http.HandlerFunc, w http.ResponseWriter, r *
maps.Copy(rm.Header(), respToModify.Header)
// override the body if changed
if respToModify.Body != currentBody {
if isBodyModifier && respToModify.Body != currentBody {
err := rm.SetBody(respToModify.Body)
if err != nil {
m.LogError(r).Err(err).Msg("failed to set response body")