@marcel91200 / caddy Mirror · read-only / commits / 53aa60af
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, 05:15 UTC

reverseproxy: Handle "operation was canceled" errors (#3816)

* fix(caddy): Avoid "operation was canceled" errors - Also add error handling for StatusGatewayTimeout * revert(caddy): Revert 504 handling - This will potentially break load balancing and health checks * Handle client cancellation as different error Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>

Daniel Santos committed Nov 25, 2020 at 10:54 UTC 53aa60afff2e66b4a2b2ca6968fe5cb6c8567437
1 file changed +24 -3
modules/caddyhttp/reverseproxy/reverseproxy.go
+24 -3
@@ -18,6 +18,7 @@ import (
18 "bytes"
19 "context"
20 "encoding/json"
21 + "errors"
22 "fmt"
23 "io"
24 "io/ioutil"
@@ -384,8 +385,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
385 // DialInfo struct should have valid network address syntax
386 dialInfo, err := upstream.fillDialInfo(r)
387 if err != nil {
387 - err = fmt.Errorf("making dial info: %v", err)
388 - return caddyhttp.Error(http.StatusBadGateway, err)
388 + return statusError(fmt.Errorf("making dial info: %v", err))
389 }
390
391 // attach to the request information about how to dial the upstream;
@@ -438,7 +438,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
438 }
439 }
440
441 - return caddyhttp.Error(http.StatusBadGateway, proxyErr)
441 + return statusError(proxyErr)
442 }
443
444 // prepareRequest modifies req so that it is ready to be proxied,
@@ -762,6 +762,27 @@ func removeConnectionHeaders(h http.Header) {
762 }
763 }
764
765 +// statusError returns an error value that has a status code.
766 +func statusError(err error) error {
767 + // errors proxying usually mean there is a problem with the upstream(s)
768 + statusCode := http.StatusBadGateway
769 +
770 + // if the client canceled the request (usually this means they closed
771 + // the connection, so they won't see any response), we can report it
772 + // as a client error (4xx) and not a server error (5xx); unfortunately
773 + // the Go standard library, at least at time of writing in late 2020,
774 + // obnoxiously wraps the exported, standard context.Canceled error with
775 + // an unexported garbage value that we have to do a substring check for:
776 + // https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430
777 + if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "operation was canceled") {
778 + // regrettably, there is no standard error code for "client closed connection", but
779 + // for historical reasons we can use a code that a lot of people are already using;
780 + // using 5xx is problematic for users; see #3748
781 + statusCode = 499
782 + }
783 + return caddyhttp.Error(statusCode, err)
784 +}
785 +
786 // LoadBalancing has parameters related to load balancing.
787 type LoadBalancing struct {
788 // A selection policy is how to choose an available backend.