@marcel91200 / caddy Mirror · read-only / commits / ad912569
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 11:00 UTC

reverseproxy: wraps request body to prevent closing if not read (#7719)

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

WeidiDeng committed May 21, 2026 at 01:35 UTC ad912569b55cd2fe6758069ff05f13b45ed6da63
1 file changed +34 -1
modules/caddyhttp/reverseproxy/reverseproxy.go
+34 -1
@@ -449,6 +449,39 @@ func (h *Handler) Cleanup() error {
449 return err
450 }
451
452 +// bodyNopCloserIfNotRead wraps a request body to prevent closing if not read, i.e., when
453 +// dialing to upstream fails.
454 +// It will close the body as normal if the body is read.
455 +type bodyNopCloserIfNotRead struct {
456 + io.ReadCloser
457 + read int // tracks the number of bytes read, -1 when first Read returns 0, io.EOF
458 +}
459 +
460 +func (b *bodyNopCloserIfNotRead) Read(p []byte) (int, error) {
461 + if b.read == -1 {
462 + return 0, io.EOF
463 + }
464 + n, err := b.ReadCloser.Read(p)
465 + // first Read returns 0, io.EOF
466 + if b.read == 0 && n == 0 && err == io.EOF {
467 + b.read = -1
468 + } else {
469 + b.read += n
470 + }
471 + return n, err
472 +}
473 +
474 +func (b *bodyNopCloserIfNotRead) Close() error {
475 + // don't close the body
476 + if b.read == 0 {
477 + return nil
478 + }
479 + // close as usual, when -1, any read will return EOF as the original read will do
480 + // in other cases, the read will fail as body is closed because we do not want partial bodies to be sent to the upstream
481 + // users can buffer the entire request body to allow the request to be resent
482 + return b.ReadCloser.Close()
483 +}
484 +
485 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
486 repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
487
@@ -510,7 +543,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
543 bufPool.Put(bufferedReqBody)
544 }()
545 } else {
513 - clonedReq.Body = io.NopCloser(clonedReq.Body)
546 + clonedReq.Body = &bodyNopCloserIfNotRead{ReadCloser: clonedReq.Body}
547 }
548 }
549