From d93e027e01905406671ed8df295d5e48d16645c2 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Mon, 22 Apr 2024 15:22:50 -0600 Subject: [PATCH] reverseproxy: Reuse buffered request body even if partially drained Previous commit only works when the backends don't read any of the body first. --- modules/caddyhttp/reverseproxy/reverseproxy.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 048539e9..980e259c 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -439,6 +439,15 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht var proxyErr error var retries int for { + // if the request body was buffered (and only the entire body, hence no body + // set to read from after the buffer), make reading from the body idempotent + // and reusable, so if a backend partially or fully reads the body but then + // produces an error, the request can be repeated to the next backend with + // the full body (retries should only happen for idempotent requests) (see #6259) + if reqBodyBuf, ok := r.Body.(bodyReadCloser); ok && reqBodyBuf.body == nil { + r.Body = io.NopCloser(bytes.NewReader(reqBodyBuf.buf.Bytes())) + } + var done bool done, proxyErr = h.proxyLoopIteration(clonedReq, r, w, proxyErr, start, retries, repl, reqHeader, reqHost, next) if done {