@marcel91200 / caddy Mirror · read-only / commits / 2a6859a5
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 12, 22:00 UTC

reverseproxy: Fix retries on "upstreams unavailable" error (#5841)

Francis Lavoie committed Oct 10, 2023 at 18:07 UTC 2a6859a5e46227473220c653668fa9da9f00b14e
1 file changed +17 -9
modules/caddyhttp/reverseproxy/reverseproxy.go
+17 -9
@@ -357,7 +357,7 @@ func (h *Handler) Provision(ctx caddy.Context) error {
357 // set defaults on passive health checks, if necessary
358 if h.HealthChecks.Passive != nil {
359 h.HealthChecks.Passive.logger = h.logger.Named("health_checker.passive")
360 - if h.HealthChecks.Passive.FailDuration > 0 && h.HealthChecks.Passive.MaxFails == 0 {
360 + if h.HealthChecks.Passive.MaxFails == 0 {
361 h.HealthChecks.Passive.MaxFails = 1
362 }
363 }
@@ -480,7 +480,7 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
480 upstream := h.LoadBalancing.SelectionPolicy.Select(upstreams, r, w)
481 if upstream == nil {
482 if proxyErr == nil {
483 - proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, fmt.Errorf("no upstreams available"))
483 + proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, noUpstreamsAvailable)
484 }
485 if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r) {
486 return true, proxyErr
@@ -1010,17 +1010,23 @@ func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, retries int
1010 // should be safe to retry, since without a connection, no
1011 // HTTP request can be transmitted; but if the error is not
1012 // specifically a dialer error, we need to be careful
1013 - if _, ok := proxyErr.(DialError); proxyErr != nil && !ok {
1013 + if proxyErr != nil {
1014 + _, isDialError := proxyErr.(DialError)
1015 + herr, isHandlerError := proxyErr.(caddyhttp.HandlerError)
1016 +
1017 // if the error occurred after a connection was established,
1018 // we have to assume the upstream received the request, and
1019 // retries need to be carefully decided, because some requests
1020 // are not idempotent
1018 - if lb.RetryMatch == nil && req.Method != "GET" {
1019 - // by default, don't retry requests if they aren't GET
1020 - return false
1021 - }
1022 - if !lb.RetryMatch.AnyMatch(req) {
1023 - return false
1021 + if !isDialError && !(isHandlerError && errors.Is(herr, noUpstreamsAvailable)) {
1022 + if lb.RetryMatch == nil && req.Method != "GET" {
1023 + // by default, don't retry requests if they aren't GET
1024 + return false
1025 + }
1026 +
1027 + if !lb.RetryMatch.AnyMatch(req) {
1028 + return false
1029 + }
1030 }
1031 }
1032
@@ -1421,6 +1427,8 @@ func (c ignoreClientGoneContext) Err() error {
1427 // from the proxy handler.
1428 const proxyHandleResponseContextCtxKey caddy.CtxKey = "reverse_proxy_handle_response_context"
1429
1430 +var noUpstreamsAvailable = fmt.Errorf("no upstreams available")
1431 +
1432 // Interface guards
1433 var (
1434 _ caddy.Provisioner = (*Handler)(nil)