mirror of
https://github.com/git-pkgs/proxy.git
synced 2026-11-09 15:49:16 -05:00
A 429, or a 503 with Retry-After, from an upstream host now stops the proxy sending that host anything for as long as it asked (capped at an hour), or for a minute doubling up to fifteen when no Retry-After came. Held requests fail locally: cached metadata is served stale as before, and anything uncached answers 503 with the remaining Retry-After so the client paces itself too. The gate is a RoundTripper under the auth transport, so metadata, artifact and passthrough fetches all obey it. Metadata fetches had no retry or circuit breaker, so a rate-limited upstream kept receiving one request per cache miss for the whole block. /health lists held hosts under upstream_holds and proxy_upstream_rate_limit_holds_total counts holds.
27 lines
647 B
Go
27 lines
647 B
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/git-pkgs/proxy/internal/httpclient"
|
|
)
|
|
|
|
// writeUpstreamHeld answers a request the rate limit gate refused to send
|
|
// with 503 and the Retry-After the upstream asked for, and reports whether
|
|
// err was such a refusal.
|
|
func writeUpstreamHeld(w http.ResponseWriter, err error) bool {
|
|
var held *httpclient.HeldError
|
|
if !errors.As(err, &held) {
|
|
return false
|
|
}
|
|
secs := int(math.Ceil(held.RetryAfter.Seconds()))
|
|
if secs < 1 {
|
|
secs = 1
|
|
}
|
|
w.Header().Set("Retry-After", strconv.Itoa(secs))
|
|
http.Error(w, held.Error(), http.StatusServiceUnavailable)
|
|
return true
|
|
}
|