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