Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 01:30 UTC
reverseproxy: Prevent copying the response if a response handler ran (#4388)
Francis Lavoie committed
Oct 18, 2021 at 14:00 UTC
b092061591d673d5a886881531df2b39cd7722e3
1 file changed
+27
-12
modules/caddyhttp/reverseproxy/reverseproxy.go
+27
-12
@@ -614,6 +614,11 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, repl *
614
res.Body = h.bufferedBody(res.Body)
615
}
616
617
+ // the response body may get closed by a response handler,
618
+ // and we need to keep track to make sure we don't try to copy
619
+ // the response if it was already closed
620
+ bodyClosed := false
621
+
622
// see if any response handler is configured for this response from the backend
623
for i, rh := range h.HandleResponse {
624
if rh.Match != nil && !rh.Match.Match(res.StatusCode, res.Header) {
@@ -638,8 +643,6 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, repl *
643
continue
644
}
645
641
- res.Body.Close()
642
-
646
// set up the replacer so that parts of the original response can be
647
// used for routing decisions
648
for field, value := range res.Header {
@@ -649,7 +652,17 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, repl *
652
repl.Set("http.reverse_proxy.status_text", res.Status)
653
654
h.logger.Debug("handling response", zap.Int("handler", i))
652
- if routeErr := rh.Routes.Compile(next).ServeHTTP(rw, req); routeErr != nil {
655
+
656
+ // pass the request through the response handler routes
657
+ routeErr := rh.Routes.Compile(next).ServeHTTP(rw, req)
658
+
659
+ // always close the response body afterwards since it's expected
660
+ // that the response handler routes will have written to the
661
+ // response writer with a new body
662
+ res.Body.Close()
663
+ bodyClosed = true
664
+
665
+ if routeErr != nil {
666
// wrap error in roundtripSucceeded so caller knows that
667
// the roundtrip was successful and to not retry
668
return roundtripSucceeded{routeErr}
@@ -690,15 +703,17 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, repl *
703
}
704
705
rw.WriteHeader(res.StatusCode)
693
- err = h.copyResponse(rw, res.Body, h.flushInterval(req, res))
694
- res.Body.Close() // close now, instead of defer, to populate res.Trailer
695
- if err != nil {
696
- // we're streaming the response and we've already written headers, so
697
- // there's nothing an error handler can do to recover at this point;
698
- // the standard lib's proxy panics at this point, but we'll just log
699
- // the error and abort the stream here
700
- h.logger.Error("aborting with incomplete response", zap.Error(err))
701
- return nil
706
+ if !bodyClosed {
707
+ err = h.copyResponse(rw, res.Body, h.flushInterval(req, res))
708
+ res.Body.Close() // close now, instead of defer, to populate res.Trailer
709
+ if err != nil {
710
+ // we're streaming the response and we've already written headers, so
711
+ // there's nothing an error handler can do to recover at this point;
712
+ // the standard lib's proxy panics at this point, but we'll just log
713
+ // the error and abort the stream here
714
+ h.logger.Error("aborting with incomplete response", zap.Error(err))
715
+ return nil
716
+ }
717
}
718
719
if len(res.Trailer) > 0 {