Documentation
¶
Overview ¶
Package ratelimit provides a small token-bucket rate-limit middleware used to protect anonymous attack surfaces (login, enroll) from brute-force / password-spray.
The Limiter is keyed by a caller-supplied function (IP, IP+username, etc.) so the same primitive can fan out to per-endpoint policies.
Index ¶
Constants ¶
const DefaultMaxBuckets = 100_000
DefaultMaxBuckets is the cap on the per-key map size. Once exceeded, new keys all share a single overflow bucket, so an attacker churning arbitrary keys (X-Forwarded-For spoofing or a similar primitive in a future surface) cannot grow the limiter's memory footprint unbounded.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter is a sharded map of token buckets keyed by an arbitrary string. Buckets age out after `evictAfter` of inactivity so the map doesn't grow unbounded. Eviction is amortized — the full O(N) scan runs at most once per `evictAfter/2` so a single hot-path Allow doesn't pay the cost. When the map exceeds maxBuckets, new keys collapse onto a shared overflow bucket; the spray still gets rate-limited (just not per-key) and memory stays bounded.
func New ¶
New returns a Limiter that allows up to `burst` events per key over `per`, with steady-state refill at `burst/per`. evictAfter is the inactivity window after which a key's bucket is forgotten — pick something larger than `per` so genuine retries don't reset their bucket.
The bucket map is capped at DefaultMaxBuckets entries. Operators that need a different cap can construct via NewWithCap.
func NewWithCap ¶
NewWithCap is New with an explicit ceiling on the per-key map size.
func (*Limiter) Allow ¶
Allow returns true if the supplied key can perform one event under the current bucket state. Side-effect: the bucket is created on first use and idle buckets are GC'd opportunistically (at most once per evictInterval to keep the hot path constant-time). When the map is already at maxBuckets and the key has no existing bucket, the call falls back to the shared overflow bucket so memory stays bounded.
func (*Limiter) HTTPMiddleware ¶
func (l *Limiter) HTTPMiddleware(keyFn func(*http.Request) string, onReject func(*http.Request, string)) func(http.Handler) http.Handler
HTTPMiddleware returns a middleware that rejects requests with 429 when `keyFn(r)` exceeds the limit. keyFn is responsible for choosing the dimension (e.g., utils.GetIP(r), or `utils.GetIP(r) + ":" + username`).
onReject is invoked synchronously when a request is rejected — use it to emit an audit-log entry. May be nil.