Documentation
¶
Index ¶
- func CanonicalizeIP(ip string) string
- func Key(key string) func(r *http.Request) (string, error)
- func KeyByEndpoint(r *http.Request) (string, error)
- func KeyByIP(r *http.Request) (string, error)deprecated
- func KeyByRealIP(r *http.Request) (string, error)deprecated
- func Limit(requestLimit int, windowLength time.Duration, options ...Option) func(next http.Handler) http.Handlerdeprecated
- func LimitAll(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handlerdeprecated
- func LimitBy(requestLimit int, windowLength time.Duration, keyFn KeyFunc, options ...Option) func(next http.Handler) http.Handler
- func LimitByIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handlerdeprecated
- func LimitByRealIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handlerdeprecated
- func LimitCounterKey(key string, window time.Time) uint64
- func NewLocalLimitCounter(windowLength time.Duration) *localCounter
- func WithIncrement(ctx context.Context, value int) context.Context
- func WithRequestLimit(ctx context.Context, value int) context.Context
- type KeyFunc
- type LimitCounter
- type Option
- func WithErrorHandler(h func(http.ResponseWriter, *http.Request, error)) Option
- func WithKeyByIP() Optiondeprecated
- func WithKeyByRealIP() Optiondeprecated
- func WithKeyFuncs(keyFuncs ...KeyFunc) Option
- func WithLimitCounter(c LimitCounter) Option
- func WithLimitHandler(h http.HandlerFunc) Option
- func WithNoop() Option
- func WithResponseHeaders(headers ResponseHeaders) Option
- type RateLimiter
- func (l *RateLimiter) Counter() LimitCounter
- func (l *RateLimiter) Handler(next http.Handler) http.Handler
- func (l *RateLimiter) OnLimit(w http.ResponseWriter, r *http.Request, key string) bool
- func (l *RateLimiter) RespondOnLimit(w http.ResponseWriter, r *http.Request, key string) bool
- func (l *RateLimiter) Status(key string) (bool, float64, error)
- type ResponseHeaders
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CanonicalizeIP ¶ added in v0.16.0
CanonicalizeIP normalizes a client IP string for use as a rate-limit key:
- IPv4 addresses are returned unchanged.
- IPv6 addresses are reduced to their /64 prefix. An IPv6 client typically controls a whole /64 (2^64 addresses via SLAAC), so keying on the full address would let it rotate within its own /64 to win a fresh bucket per request and bypass a per-IP limit. Widen/narrow the prefix yourself if your clients are delegated a larger block (e.g. a /56 or /48).
- Any other string, including "", is returned unchanged.
httprate stays router-agnostic, so it does not resolve the client IP for you — pair CanonicalizeIP with whatever does. With chi's middleware.ClientIPFrom* (chi v5.3.0+) and middleware.GetClientIP:
r.Use(middleware.ClientIPFromXFF("10.0.0.0/8"))
r.Use(httprate.LimitBy(100, time.Minute, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
WARNING: if the resolver returns "" (e.g. no ClientIPFrom* middleware is installed upstream), CanonicalizeIP returns "" and every request shares a single global rate-limit bucket. Strictly more restrictive, but a footgun — make sure the client IP is actually resolved upstream.
func KeyByIP
deprecated
Deprecated: KeyByIP keys off r.RemoteAddr, the TCP peer that opened the connection. Unlike KeyByRealIP it is NOT spoofable (RemoteAddr is set by net/http, never from a header) — but behind a reverse proxy, load balancer, or CDN, r.RemoteAddr is the proxy's address, so every client sharing that proxy lands in one rate-limit bucket. That's usually the wrong key in production, and there is no safe default IP source.
State your trust model explicitly with one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP (CanonicalizeIP buckets IPv6 by /64):
// Directly exposed to clients (KeyByIP's exact behavior, made explicit):
r.Use(middleware.ClientIPFromRemoteAddr)
r.Use(httprate.LimitBy(100, time.Minute, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
// Behind a proxy: use ClientIPFromXFF / ClientIPFromHeader / ... instead.
KeyByIP returns the IPv4 address unchanged, or the /64 prefix for IPv6.
func KeyByRealIP
deprecated
added in
v0.6.0
Deprecated: KeyByRealIP trusts the client-supplied True-Client-IP, X-Real-IP, and X-Forwarded-For headers without verifying any proxy chain, so a remote attacker can forge the rate-limit key — see GHSA-9g5q-2w5x-hmxf, GHSA-rjr7-jggh-pgcp, GHSA-3fxj-6jh8-hvhx for the equivalent flaw in chi's middleware.RealIP. On a rate-limiter this is two-sided: an attacker can evade the limit by rotating the spoofed header (unbounded buckets) or lock a victim out by pinning the header to the victim's IP (exhausting their bucket).
Install one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP instead (CanonicalizeIP buckets IPv6 by /64):
r.Use(middleware.ClientIPFromXFF("10.0.0.0/8"))
r.Use(httprate.LimitBy(100, time.Minute, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
func Limit
deprecated
func Limit(requestLimit int, windowLength time.Duration, options ...Option) func(next http.Handler) http.Handler
Deprecated: Use LimitBy(requestLimit, windowLength, keyFn, options...) instead, which makes the rate-limit key an explicit, required argument rather than an optional WithKeyFuncs. Pass the key function directly (e.g. a KeyFunc that reads a trusted client IP — see CanonicalizeIP), or httprate.Key("*") for a single global bucket. The remaining options (WithLimitCounter, WithLimitHandler, WithResponseHeaders, ...) carry over unchanged as LimitBy's trailing variadic.
func LimitAll
deprecated
Deprecated: Use LimitBy(requestLimit, windowLength, Key("*")) instead — a single global rate-limit bucket keyed by a constant. (LimitAll already keys every request by "*" under the hood; this just makes that explicit.)
func LimitBy ¶ added in v0.16.0
func LimitBy(requestLimit int, windowLength time.Duration, keyFn KeyFunc, options ...Option) func(next http.Handler) http.Handler
LimitBy is the canonical entry point for rate-limiting by an explicit key.
It is shorthand for Limit with keyFn installed as the rate-limit key. The key is a required positional argument, so every call site has to state, on purpose, what it rate-limits by.
To rate-limit by a trusted client IP behind a proxy, resolve the IP with one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and read it back in the KeyFunc; CanonicalizeIP buckets IPv6 clients by their /64:
r.Use(middleware.ClientIPFromXFF("10.0.0.0/8"))
r.Use(httprate.LimitBy(100, time.Minute, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
Use JoinKeys to rate-limit by more than one dimension at once:
r.Use(httprate.LimitBy(100, time.Minute, httprate.JoinKeys(clientIPKey, httprate.KeyByEndpoint)))
func LimitByIP
deprecated
Deprecated: LimitByIP keys off r.RemoteAddr (see KeyByIP). It is not spoofable, but behind a reverse proxy, load balancer, or CDN r.RemoteAddr is the proxy's address, so every client sharing that proxy lands in one bucket — usually the wrong key in production. State your trust model explicitly: install one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP (CanonicalizeIP buckets IPv6 by /64):
// Directly exposed to clients (LimitByIP's exact behavior, made explicit):
r.Use(middleware.ClientIPFromRemoteAddr)
r.Use(httprate.LimitBy(requestLimit, windowLength, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
func LimitByRealIP
deprecated
added in
v0.6.0
func LimitByRealIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler
Deprecated: LimitByRealIP is built on the spoofable KeyByRealIP and lets a remote attacker forge the rate-limit key — see GHSA-9g5q-2w5x-hmxf, GHSA-rjr7-jggh-pgcp, GHSA-3fxj-6jh8-hvhx for the equivalent flaw in chi's middleware.RealIP. Install one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP instead (CanonicalizeIP buckets IPv6 by /64):
r.Use(middleware.ClientIPFromXFF("10.0.0.0/8"))
r.Use(httprate.LimitBy(requestLimit, windowLength, func(r *http.Request) (string, error) {
return httprate.CanonicalizeIP(middleware.GetClientIP(r.Context())), nil
}))
func NewLocalLimitCounter ¶ added in v0.12.0
NewLocalLimitCounter creates an instance of localCounter, which is an in-memory implementation of http.LimitCounter.
All methods are guaranteed to always return nil error.
func WithIncrement ¶ added in v0.8.0
Types ¶
type KeyFunc ¶
func JoinKeys ¶ added in v0.16.0
JoinKeys joins the results of several KeyFuncs into a single key with ":" separators, so they can be passed to LimitBy's positional key slot for multi-dimensional rate-limiting:
r.Use(httprate.LimitBy(100, time.Minute, httprate.JoinKeys(clientIPKey, httprate.KeyByEndpoint)))
where clientIPKey is your own client-IP KeyFunc (see LimitBy and CanonicalizeIP). It is the positional-argument equivalent of WithKeyFuncs. If any component KeyFunc returns an error, the joined key returns that error.
type LimitCounter ¶
type Option ¶
type Option func(rl *RateLimiter)
func WithErrorHandler ¶ added in v0.12.1
func WithKeyByIP
deprecated
added in
v0.7.0
func WithKeyByIP() Option
Deprecated: WithKeyByIP installs KeyByIP, which keys off r.RemoteAddr — the proxy's address behind a reverse proxy, load balancer, or CDN, and usually the wrong key in production (see KeyByIP). It is not a spoofing issue, but there is no safe default IP source. State your trust model explicitly: install one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP with LimitBy instead (see CanonicalizeIP).
func WithKeyByRealIP
deprecated
added in
v0.7.0
func WithKeyByRealIP() Option
Deprecated: WithKeyByRealIP installs the spoofable KeyByRealIP and lets a remote attacker forge the rate-limit key — see GHSA-9g5q-2w5x-hmxf, GHSA-rjr7-jggh-pgcp, GHSA-3fxj-6jh8-hvhx for the equivalent flaw in chi's middleware.RealIP. Install one of chi's middleware.ClientIPFrom* middlewares (chi v5.3.0+) and key off the resolved IP with LimitBy instead (see CanonicalizeIP).
func WithKeyFuncs ¶
func WithLimitCounter ¶
func WithLimitCounter(c LimitCounter) Option
func WithLimitHandler ¶
func WithLimitHandler(h http.HandlerFunc) Option
func WithResponseHeaders ¶ added in v0.11.0
func WithResponseHeaders(headers ResponseHeaders) Option
type RateLimiter ¶ added in v0.13.1
type RateLimiter struct {
// contains filtered or unexported fields
}
func NewRateLimiter ¶
func NewRateLimiter(requestLimit int, windowLength time.Duration, options ...Option) *RateLimiter
func (*RateLimiter) Counter ¶ added in v0.13.1
func (l *RateLimiter) Counter() LimitCounter
func (*RateLimiter) Handler ¶ added in v0.13.1
func (l *RateLimiter) Handler(next http.Handler) http.Handler
func (*RateLimiter) OnLimit ¶ added in v0.13.1
func (l *RateLimiter) OnLimit(w http.ResponseWriter, r *http.Request, key string) bool
OnLimit checks the rate limit for the given key and updates the response headers accordingly. If the limit is reached, it returns true, indicating that the request should be halted. Otherwise, it increments the request count and returns false. This method does not send an HTTP response, so the caller must handle the response themselves or use the RespondOnLimit() method instead.
func (*RateLimiter) RespondOnLimit ¶ added in v0.14.0
func (l *RateLimiter) RespondOnLimit(w http.ResponseWriter, r *http.Request, key string) bool
RespondOnLimit checks the rate limit for the given key and updates the response headers accordingly. If the limit is reached, it automatically sends an HTTP response and returns true, signaling the caller to halt further request processing. If the limit is not reached, it increments the request count and returns false, allowing the request to proceed.
type ResponseHeaders ¶ added in v0.11.0
type ResponseHeaders struct {
Limit string // Default: X-RateLimit-Limit
Remaining string // Default: X-RateLimit-Remaining
Increment string // Default: X-RateLimit-Increment
Reset string // Default: X-RateLimit-Reset
RetryAfter string // Default: Retry-After
}
Set custom response headers. If empty, the header is omitted.