ratelimit

package
v0.14.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package ratelimit provides HTTP rate limiting middleware with support for per-principal and per-application rate limits.

The package supports multiple storage backends (in-memory, Redis) and integrates with Cedar policies for dynamic tier-based rate limiting.

Index

Constants

View Source
const DefaultMaxKeys = 100_000

DefaultMaxKeys is the number of keys MemoryStorage tracks by default.

Variables

View Source
var (
	ErrRateLimitExceeded = errors.New("rate limit exceeded")
	ErrStorageFailure    = errors.New("rate limit storage failure")
)

Common errors returned by the rate limiter.

View Source
var ErrClosed = errors.New("ratelimit: storage closed")

ErrClosed is returned by a closed storage.

View Source
var ErrTooManyKeys = errors.New("ratelimit: too many tracked keys")

ErrTooManyKeys is returned when a new key arrives while the storage is tracking its maximum number of live keys. Callers should treat it as a refusal: keys are attacker-influenced on pre-authentication endpoints, and admitting them without bound would exhaust memory.

Functions

func ForwardedClientIP added in v0.14.0

func ForwardedClientIP(trustedProxies ...string) (func(*http.Request) string, error)

ForwardedClientIP returns a function that finds the client's address behind reverse proxies. When the direct peer is one of the trusted proxy networks, it reads X-Forwarded-For from the right, skipping addresses that are themselves trusted proxies, and returns the first address a trusted proxy did not vouch for: a client can put anything at the left of the header, but not past the proxy that appends to it. Requests whose peer is not a trusted proxy, or whose header holds nothing usable, are attributed to the peer itself.

func NewRedisClient

func NewRedisClient(cfg *RedisConfig) redis.UniversalClient

NewRedisClient creates a Redis client from configuration.

func PeerIP added in v0.14.0

func PeerIP(r *http.Request) string

PeerIP returns the IP address of the connection's direct peer, with the port removed and IPv4-mapped IPv6 addresses unmapped. It is the one address a client cannot choose, so it is what limits key on by default. It returns "" when the peer address cannot be parsed.

Types

type CoreAPIResolver

type CoreAPIResolver struct {
	// contains filtered or unexported fields
}

CoreAPIResolver adapts coreapi.PolicyStore to the LimitResolver interface. It looks up rate limit policies based on the OAuth client ID from JWT claims.

func NewCoreAPIResolver

func NewCoreAPIResolver(store coreapi.PolicyStore, opts ...CoreAPIResolverOption) *CoreAPIResolver

NewCoreAPIResolver creates a LimitResolver that uses coreapi.PolicyStore.

func (*CoreAPIResolver) GetPolicyForRequest

func (r *CoreAPIResolver) GetPolicyForRequest(ctx context.Context, req *http.Request) *coreapi.RateLimitPolicy

GetPolicyForRequest returns the policy that would be applied to a request. Useful for debugging and displaying policy info in responses.

func (*CoreAPIResolver) InvalidateCache

func (r *CoreAPIResolver) InvalidateCache(clientID string)

InvalidateCache clears the policy cache.

func (*CoreAPIResolver) Resolve

func (r *CoreAPIResolver) Resolve(ctx context.Context, key string, req *http.Request) Limit

Resolve implements LimitResolver by looking up the policy for the OAuth client.

type CoreAPIResolverOption

type CoreAPIResolverOption func(*CoreAPIResolver)

CoreAPIResolverOption configures a CoreAPIResolver.

func WithResolverCache

func WithResolverCache(ttl time.Duration) CoreAPIResolverOption

WithResolverCache enables caching of policy lookups.

type KeyFunc

type KeyFunc func(r *http.Request) string

KeyFunc extracts the rate limit key from a request. Common implementations include extracting principal ID or client ID from JWT.

func ClientEndpointKey

func ClientEndpointKey() KeyFunc

ClientEndpointKey is a convenience function combining ClientKey with EndpointKey.

func ClientKey

func ClientKey() KeyFunc

ClientKey returns a KeyFunc that extracts the OAuth client ID (azp) from JWT claims. Falls back to IP address if no client ID is present.

func CompositeKey

func CompositeKey() KeyFunc

CompositeKey returns a KeyFunc that combines principal and client IDs. Format: "pid:{principal_id}:client:{client_id}" or fallback to IP.

func EndpointKey

func EndpointKey(inner KeyFunc) KeyFunc

EndpointKey wraps another KeyFunc to add endpoint-specific rate limiting. Format: "{inner_key}:path:{path}:method:{method}"

func ForwardedIPKey added in v0.14.0

func ForwardedIPKey(trustedProxies ...string) (KeyFunc, error)

ForwardedIPKey is IPKey for a server behind reverse proxies: it takes the client address from X-Forwarded-For when the request arrives from one of the trusted proxy networks (see ForwardedClientIP).

func IPKey

func IPKey() KeyFunc

IPKey returns a KeyFunc that keys on the IP address of the direct peer (see PeerIP). Behind a reverse proxy every client would share the proxy's address; use ForwardedIPKey there. Forwarding headers are never believed unless a trusted proxy is configured, because any client can send them.

func PrincipalEndpointKey

func PrincipalEndpointKey() KeyFunc

PrincipalEndpointKey is a convenience function combining PrincipalKey with EndpointKey.

func PrincipalKey

func PrincipalKey() KeyFunc

PrincipalKey returns a KeyFunc that extracts the principal ID from JWT claims. Falls back to IP address if no principal is authenticated.

type Limit

type Limit struct {
	// Rate is the number of requests allowed per Period.
	Rate int

	// Period is the time window for the rate limit (e.g., time.Minute).
	Period time.Duration

	// Burst is the maximum number of requests allowed in a single burst.
	// If zero, defaults to Rate.
	Burst int
}

Limit defines rate limit parameters.

func DefaultLimit

func DefaultLimit() Limit

DefaultLimit returns a sensible default rate limit (100 requests per minute).

func PerHour

func PerHour(n int) Limit

PerHour creates a rate limit of n requests per hour.

func PerMinute

func PerMinute(n int) Limit

PerMinute creates a rate limit of n requests per minute.

func PerSecond

func PerSecond(n int) Limit

PerSecond creates a rate limit of n requests per second.

type LimitResolver

type LimitResolver interface {
	// Resolve returns the rate limit for the given key and context.
	// The key is typically derived from the principal ID or client ID.
	Resolve(ctx context.Context, key string, r *http.Request) Limit
}

LimitResolver determines the rate limit for a given request. This allows dynamic rate limiting based on user tier, endpoint, etc.

type Limiter

type Limiter struct {
	// contains filtered or unexported fields
}

Limiter is the main rate limiter type.

func New

func New(storage Storage, opts ...Option) *Limiter

New creates a new Limiter with the given storage and options.

func (*Limiter) Allow

func (l *Limiter) Allow(r *http.Request) (Result, error)

Allow checks if a request should be allowed.

func (*Limiter) Close

func (l *Limiter) Close() error

Close releases resources held by the limiter.

func (*Limiter) Middleware

func (l *Limiter) Middleware() func(http.Handler) http.Handler

Middleware returns an HTTP middleware that enforces rate limits.

type MemoryOption

type MemoryOption func(*MemoryStorage)

MemoryOption configures MemoryStorage.

func WithCleanupInterval

func WithCleanupInterval(d time.Duration) MemoryOption

WithCleanupInterval sets the interval for cleaning up expired windows.

func WithMaxKeys added in v0.14.0

func WithMaxKeys(n int) MemoryOption

WithMaxKeys sets the number of keys the storage tracks at once (default DefaultMaxKeys). Expired windows are discarded to make room first.

type MemoryStorage

type MemoryStorage struct {
	// contains filtered or unexported fields
}

MemoryStorage is an in-memory rate limit storage using sliding window algorithm. Suitable for single-instance deployments and development/testing.

func NewMemoryStorage

func NewMemoryStorage(opts ...MemoryOption) *MemoryStorage

NewMemoryStorage creates a new in-memory rate limit storage.

func (*MemoryStorage) Allow

func (m *MemoryStorage) Allow(ctx context.Context, key string, limit Limit) (Result, error)

Allow implements Storage.Allow using a sliding window algorithm.

func (*MemoryStorage) Close

func (m *MemoryStorage) Close() error

Close implements Storage.Close.

func (*MemoryStorage) Reset

func (m *MemoryStorage) Reset(ctx context.Context, key string) error

Reset implements Storage.Reset.

type Option

type Option func(*Limiter)

Option configures a Limiter.

func WithFailOpen added in v0.14.0

func WithFailOpen() Option

WithFailOpen lets requests through when the storage fails. By default the middleware fails closed, answering 503, because a storage outage otherwise switches off the limits that protect authentication endpoints at the moment an attacker may be causing it.

func WithKeyFunc

func WithKeyFunc(keyFunc KeyFunc) Option

WithKeyFunc sets the function to extract rate limit keys from requests.

func WithLimit

func WithLimit(limit Limit) Option

WithLimit sets a static rate limit.

func WithLogger added in v0.14.0

func WithLogger(logger *slog.Logger) Option

WithLogger sets the logger for storage failures.

func WithObservability

func WithObservability(obs *observability.Observability) Option

WithObservability sets the observability provider for metrics and tracing.

func WithResolver

func WithResolver(resolver LimitResolver) Option

WithResolver sets a custom limit resolver.

type RedisConfig

type RedisConfig struct {
	// Addr is the Redis server address (e.g., "localhost:6379").
	Addr string

	// Password is the Redis password (optional).
	Password string

	// DB is the Redis database number (default 0).
	DB int

	// PoolSize is the maximum number of connections (default 10).
	PoolSize int

	// Cluster enables Redis Cluster mode.
	Cluster bool

	// ClusterAddrs are the cluster node addresses (used when Cluster is true).
	ClusterAddrs []string
}

RedisConfig contains configuration for connecting to Redis.

type RedisOption

type RedisOption func(*RedisStorage)

RedisOption configures RedisStorage.

func WithKeyPrefix

func WithKeyPrefix(prefix string) RedisOption

WithKeyPrefix sets a prefix for all rate limit keys in Redis.

type RedisStorage

type RedisStorage struct {
	// contains filtered or unexported fields
}

RedisStorage is a Redis-backed rate limit storage using sliding window algorithm. Suitable for distributed deployments where multiple instances share rate limit state.

func NewRedisStorage

func NewRedisStorage(client redis.UniversalClient, opts ...RedisOption) *RedisStorage

NewRedisStorage creates a new Redis-backed rate limit storage. The client can be *redis.Client, *redis.ClusterClient, or any UniversalClient.

func (*RedisStorage) Allow

func (r *RedisStorage) Allow(ctx context.Context, key string, limit Limit) (Result, error)

Allow implements Storage.Allow using a sliding window algorithm with Redis.

func (*RedisStorage) Close

func (r *RedisStorage) Close() error

Close implements Storage.Close. Note: This does NOT close the Redis client since it may be shared.

func (*RedisStorage) Reset

func (r *RedisStorage) Reset(ctx context.Context, key string) error

Reset implements Storage.Reset.

type Result

type Result struct {
	// Allowed is true if the request should be permitted.
	Allowed bool

	// Remaining is the number of requests remaining in the current window.
	Remaining int

	// ResetAt is when the rate limit window resets.
	ResetAt time.Time

	// RetryAfter is the duration to wait before retrying (only set if not allowed).
	RetryAfter time.Duration
}

Result contains the outcome of a rate limit check.

type StaticResolver

type StaticResolver struct {
	Limit Limit
}

StaticResolver always returns the same limit.

func (*StaticResolver) Resolve

func (s *StaticResolver) Resolve(ctx context.Context, key string, r *http.Request) Limit

Resolve implements LimitResolver.

type Storage

type Storage interface {
	// Allow checks if a request should be allowed and updates the counter.
	// Returns the result of the check and any error.
	Allow(ctx context.Context, key string, limit Limit) (Result, error)

	// Reset clears the rate limit state for a key.
	Reset(ctx context.Context, key string) error

	// Close releases any resources held by the storage.
	Close() error
}

Storage defines the interface for rate limit state storage.

type TieredResolver

type TieredResolver struct {
	Tiers    map[string]Limit
	Default  Limit
	TierFunc func(ctx context.Context, key string, r *http.Request) string
	// contains filtered or unexported fields
}

TieredResolver returns different limits based on tier.

func (*TieredResolver) Resolve

func (t *TieredResolver) Resolve(ctx context.Context, key string, r *http.Request) Limit

Resolve implements LimitResolver.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL