1
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-16 21:56:40 -05:00

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>
This commit is contained in:
Daniel Santos 2020-11-25 10:54:23 -07:00 committed by GitHub
parent b0f8fc7aae
commit 53aa60afff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -384,8 +385,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
// DialInfo struct should have valid network address syntax // DialInfo struct should have valid network address syntax
dialInfo, err := upstream.fillDialInfo(r) dialInfo, err := upstream.fillDialInfo(r)
if err != nil { if err != nil {
err = fmt.Errorf("making dial info: %v", err) return statusError(fmt.Errorf("making dial info: %v", err))
return caddyhttp.Error(http.StatusBadGateway, err)
} }
// attach to the request information about how to dial the upstream; // 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
} }
} }
return caddyhttp.Error(http.StatusBadGateway, proxyErr) return statusError(proxyErr)
} }
// prepareRequest modifies req so that it is ready to be proxied, // prepareRequest modifies req so that it is ready to be proxied,
@ -762,6 +762,27 @@ func removeConnectionHeaders(h http.Header) {
} }
} }
// statusError returns an error value that has a status code.
func statusError(err error) error {
// errors proxying usually mean there is a problem with the upstream(s)
statusCode := http.StatusBadGateway
// if the client canceled the request (usually this means they closed
// the connection, so they won't see any response), we can report it
// as a client error (4xx) and not a server error (5xx); unfortunately
// the Go standard library, at least at time of writing in late 2020,
// obnoxiously wraps the exported, standard context.Canceled error with
// an unexported garbage value that we have to do a substring check for:
// https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430
if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "operation was canceled") {
// regrettably, there is no standard error code for "client closed connection", but
// for historical reasons we can use a code that a lot of people are already using;
// using 5xx is problematic for users; see #3748
statusCode = 499
}
return caddyhttp.Error(statusCode, err)
}
// LoadBalancing has parameters related to load balancing. // LoadBalancing has parameters related to load balancing.
type LoadBalancing struct { type LoadBalancing struct {
// A selection policy is how to choose an available backend. // A selection policy is how to choose an available backend.