Files
aperture/proxy/ratelimit_config.go
Slyghtning a3ee1e0d17 proxy: implement a token-bucket rate limiter for aperture
Implement a token-bucket rate limiter for aperture that limits requests
per service endpoint. The rate limiter uses golang.org/x/time/rate and
provides per-key limiting with L402 token ID extraction (falling back
to IP address for unauthenticated requests).

Key components:
- RateLimitConfig: Configuration struct with path regex, requests/per/burst
- RateLimiter: Manages per-key rate.Limiter instances with LRU eviction
  to prevent memory exhaustion (default 10,000 entries)
- Prometheus metrics: allowed/denied counters, cache size, evictions

This addresses GitHub issue #200 for DoS protection on authenticated
endpoints that are free of charge after L402 payment.
2026-01-23 09:05:55 -05:00

57 lines
1.7 KiB
Go

package proxy
import (
"regexp"
"time"
)
// RateLimitConfig defines a rate limiting rule for a specific path pattern.
type RateLimitConfig struct {
// PathRegexp is a regular expression that matches request paths
// to which this rate limit applies. If empty, matches all paths.
PathRegexp string `long:"pathregexp" description:"Regular expression to match the path of the URL against for rate limiting"`
// Requests is the number of requests allowed per time window (Per).
Requests int `long:"requests" description:"Number of requests allowed per time window"`
// Per is the time window duration (e.g., 1s, 1m, 1h). Defaults to 1s.
Per time.Duration `long:"per" description:"Time window for rate limiting (e.g., 1s, 1m, 1h)"`
// Burst is the maximum number of requests that can be made in a burst,
// exceeding the steady-state rate. Defaults to Requests if not set.
Burst int `long:"burst" description:"Maximum burst size (defaults to Requests if not set)"`
// compiledPathRegexp is the compiled version of PathRegexp.
compiledPathRegexp *regexp.Regexp
}
// Rate returns the rate.Limit value (requests per second) for this
// configuration.
func (r *RateLimitConfig) Rate() float64 {
if r.Per == 0 {
return 0
}
return float64(r.Requests) / r.Per.Seconds()
}
// EffectiveBurst returns the burst value, defaulting to Requests if Burst
// is 0.
func (r *RateLimitConfig) EffectiveBurst() int {
if r.Burst == 0 {
return r.Requests
}
return r.Burst
}
// Matches returns true if the given path matches this rate limit's path
// pattern.
func (r *RateLimitConfig) Matches(path string) bool {
if r.compiledPathRegexp == nil {
return true // No pattern means match all
}
return r.compiledPathRegexp.MatchString(path)
}