mirror of
https://github.com/caddyserver/caddy.git
synced 2025-11-26 08:15:21 -05:00
reverseproxy: Fix retries for requests with bodies (#7360)
Some checks failed
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 47s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m49s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Some checks failed
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 47s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m49s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* capture the buffered body once, then reset clonedReq.Body before each retry * no copy * keep receiver name * set the buf to nil after extraction and only return it to pool if not nil --------- Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
This commit is contained in:
parent
2cb426776c
commit
67a9e0657e
@ -437,6 +437,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||
reqHost := clonedReq.Host
|
||||
reqHeader := clonedReq.Header
|
||||
|
||||
// If the cloned request body was fully buffered, keep a reference to its
|
||||
// buffer so we can reuse it across retries and return it to the pool
|
||||
// once we’re done.
|
||||
var bufferedReqBody *bytes.Buffer
|
||||
if reqBodyBuf, ok := clonedReq.Body.(bodyReadCloser); ok && reqBodyBuf.body == nil && reqBodyBuf.buf != nil {
|
||||
bufferedReqBody = reqBodyBuf.buf
|
||||
reqBodyBuf.buf = nil
|
||||
|
||||
defer func() {
|
||||
bufferedReqBody.Reset()
|
||||
bufPool.Put(bufferedReqBody)
|
||||
}()
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
// total proxying duration, including time spent on LB and retries
|
||||
@ -455,8 +469,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||
// 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()))
|
||||
if bufferedReqBody != nil {
|
||||
clonedReq.Body = io.NopCloser(bytes.NewReader(bufferedReqBody.Bytes()))
|
||||
}
|
||||
|
||||
var done bool
|
||||
@ -1538,7 +1552,12 @@ type bodyReadCloser struct {
|
||||
}
|
||||
|
||||
func (brc bodyReadCloser) Close() error {
|
||||
bufPool.Put(brc.buf)
|
||||
// Inside this package this will be set to nil for fully-buffered
|
||||
// requests due to the possibility of retrial.
|
||||
if brc.buf != nil {
|
||||
bufPool.Put(brc.buf)
|
||||
}
|
||||
// For fully-buffered bodies, body is nil, so Close is a no-op.
|
||||
if brc.body != nil {
|
||||
return brc.body.Close()
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user