2026-10-14 12:51:43 +01:00
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"math"
|
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
|
2026-10-19 12:28:55 +01:00
|
|
|
registryclient "github.com/git-pkgs/registries/client"
|
2026-10-14 12:51:43 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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 {
|
2026-10-19 12:28:55 +01:00
|
|
|
var held *registryclient.HeldError
|
2026-10-14 12:51:43 +01:00
|
|
|
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
|
|
|
|
|
}
|
2026-10-25 17:34:08 +01:00
|
|
|
|
|
|
|
|
// UpstreamHolds reports whether requests to a URL's host are held by rate
|
|
|
|
|
// limiting. *registryclient.RateLimitGate and the shared gate in
|
|
|
|
|
// internal/httpclient implement it.
|
|
|
|
|
type UpstreamHolds interface {
|
|
|
|
|
// Held returns a *registryclient.HeldError while rawURL's host is held.
|
|
|
|
|
Held(rawURL string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// upstreamHeld returns the hold on rawURL's host, if any.
|
|
|
|
|
func (p *Proxy) upstreamHeld(rawURL string) error {
|
|
|
|
|
if p.RateLimits == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return p.RateLimits.Held(rawURL)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reloadUpstreamHolds rereads holds shared with other instances, when the
|
|
|
|
|
// gate shares them. It is called after another instance's fetch failed,
|
|
|
|
|
// since a 429 there will have placed a hold this instance may not have read
|
|
|
|
|
// yet.
|
|
|
|
|
func (p *Proxy) reloadUpstreamHolds() {
|
|
|
|
|
if r, ok := p.RateLimits.(interface{ Reload() }); ok {
|
|
|
|
|
r.Reload()
|
|
|
|
|
}
|
|
|
|
|
}
|