@marcel91200 / caddy Mirror · read-only / commits / b0920615
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 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 {