MCPcopy
hub / github.com/caddyserver/caddy / tryAgain

Method tryAgain

modules/caddyhttp/reverseproxy/reverseproxy.go:1324–1398  ·  view source on GitHub ↗

tryAgain takes the time that the handler was initially invoked, the amount of retries already performed, as well as any error currently obtained, and the request being tried, and returns true if another attempt should be made at proxying the request. If true is returned, it has already blocked long

(ctx caddy.Context, start time.Time, retries int, proxyErr error, req *http.Request, logger *zap.Logger)

Source from the content-addressed store, hash-verified

1322// the next retry (i.e. no more sleeping is needed). If false is
1323// returned, the handler should stop trying to proxy the request.
1324func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, retries int, proxyErr error, req *http.Request, logger *zap.Logger) bool {
1325 // no retries are configured
1326 if lb.TryDuration == 0 && lb.Retries == 0 {
1327 return false
1328 }
1329
1330 // if we've tried long enough, break
1331 if lb.TryDuration > 0 && time.Since(start) >= time.Duration(lb.TryDuration) {
1332 return false
1333 }
1334
1335 // if we've reached the retry limit, break
1336 if lb.Retries > 0 && retries >= lb.Retries {
1337 return false
1338 }
1339
1340 // if the error occurred while dialing (i.e. a connection
1341 // could not even be established to the upstream), then it
1342 // should be safe to retry, since without a connection, no
1343 // HTTP request can be transmitted; but if the error is not
1344 // specifically a dialer error, we need to be careful
1345 if proxyErr != nil {
1346 _, isDialError := proxyErr.(DialError)
1347 _, isRetryableResponse := proxyErr.(retryableResponseError)
1348 herr, isHandlerError := proxyErr.(caddyhttp.HandlerError)
1349
1350 // if the error occurred after a connection was established,
1351 // we have to assume the upstream received the request, and
1352 // retries need to be carefully decided, because some requests
1353 // are not idempotent; retryableResponseError is excluded here
1354 // because its retry decision was already made in reverseProxy()
1355 // when the response matchers were evaluated
1356 if !isDialError && !isRetryableResponse && (!isHandlerError || !errors.Is(herr, errNoUpstream)) {
1357 if lb.RetryMatch == nil && req.Method != "GET" {
1358 // by default, don't retry requests if they aren't GET
1359 return false
1360 }
1361
1362 // set transport error flag so CEL expressions can use
1363 // {rp.is_transport_error} to decide whether to retry
1364 repl, _ := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
1365 if repl != nil {
1366 repl.Set("http.reverse_proxy.is_transport_error", true)
1367 defer repl.Delete("http.reverse_proxy.is_transport_error")
1368 }
1369
1370 match, err := lb.RetryMatch.AnyMatchWithError(req)
1371 if err != nil {
1372 logger.Error("error matching request for retry", zap.Error(err))
1373 return false
1374 }
1375 if !match {
1376 return false
1377 }
1378 }
1379 }
1380
1381 // fast path; if the interval is zero, we don't need to wait

Callers 1

proxyLoopIterationMethod · 0.80

Calls 7

DurationMethod · 0.80
AnyMatchWithErrorMethod · 0.80
StopMethod · 0.65
ValueMethod · 0.45
SetMethod · 0.45
DeleteMethod · 0.45
ErrorMethod · 0.45

Tested by

no test coverage detected