httprate

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Jul 25, 2024 License: MIT Imports: 9 Imported by: 293

README

httprate - HTTP Rate Limiter

CI workflow Benchmark workflow GoDoc Widget

net/http request rate limiter based on the Sliding Window Counter pattern inspired by CloudFlare https://blog.cloudflare.com/counting-things-a-lot-of-different-things.

The sliding window counter pattern is accurate, smooths traffic and offers a simple counter design to share a rate-limit among a cluster of servers. For example, if you'd like to use redis to coordinate a rate-limit across a group of microservices you just need to implement the httprate.LimitCounter interface to support an atomic increment and get.

Backends

Example

package main

import (
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/httprate"
)

func main() {
	r := chi.NewRouter()
	r.Use(middleware.Logger)

	// Enable httprate request limiter of 100 requests per minute.
	//
	// In the code example below, rate-limiting is bound to the request IP address
	// via the LimitByIP middleware handler.
	//
	// To have a single rate-limiter for all requests, use httprate.LimitAll(..).
	//
	// Please see _example/main.go for other more, or read the library code.
	r.Use(httprate.LimitByIP(100, time.Minute))

	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("."))
	})

	http.ListenAndServe(":3333", r)
}

Common use cases

Rate limit by IP and URL path (aka endpoint)
r.Use(httprate.Limit(
	10,             // requests
	10*time.Second, // per duration
	httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
))
Rate limit by arbitrary keys
r.Use(httprate.Limit(
	100,
	time.Minute,
	// an oversimplified example of rate limiting by a custom header
	httprate.WithKeyFuncs(func(r *http.Request) (string, error) {
		return r.Header.Get("X-Access-Token"), nil
	}),
))
Send specific response for rate limited requests
r.Use(httprate.Limit(
	10,
	time.Minute,
	httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "some specific response here", http.StatusTooManyRequests)
	}),
))
Customize response headers
r.Use(httprate.Limit(
	1000,
	time.Minute,
	httprate.WithResponseHeaders(httprate.ResponseHeaders{
		Limit:      "X-RateLimit-Limit",
		Remaining:  "X-RateLimit-Remaining",
		Reset:      "X-RateLimit-Reset",
		RetryAfter: "Retry-After",
		Increment:  "", // omit
	}),
))
Omit response headers
r.Use(httprate.Limit(
	1000,
	time.Minute,
	httprate.WithResponseHeaders(httprate.ResponseHeaders{}),
))

LICENSE

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func KeyByEndpoint

func KeyByEndpoint(r *http.Request) (string, error)

func KeyByIP

func KeyByIP(r *http.Request) (string, error)

func KeyByRealIP added in v0.6.0

func KeyByRealIP(r *http.Request) (string, error)

func Limit

func Limit(requestLimit int, windowLength time.Duration, options ...Option) func(next http.Handler) http.Handler

func LimitAll

func LimitAll(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler

func LimitByIP

func LimitByIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler

func LimitByRealIP added in v0.6.0

func LimitByRealIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler

func LimitCounterKey

func LimitCounterKey(key string, window time.Time) uint64

func NewLocalLimitCounter added in v0.12.0

func NewLocalLimitCounter(windowLength time.Duration) *localCounter

NewLocalLimitCounter creates an instance of localCounter, which is an in-memory implementation of http.LimitCounter.

func NewRateLimiter

func NewRateLimiter(requestLimit int, windowLength time.Duration, options ...Option) *rateLimiter

func WithIncrement added in v0.8.0

func WithIncrement(ctx context.Context, value int) context.Context

func WithRequestLimit added in v0.9.0

func WithRequestLimit(ctx context.Context, value int) context.Context

Types

type KeyFunc

type KeyFunc func(r *http.Request) (string, error)

type LimitCounter

type LimitCounter interface {
	Config(requestLimit int, windowLength time.Duration)
	Increment(key string, currentWindow time.Time) error
	IncrementBy(key string, currentWindow time.Time, amount int) error
	Get(key string, currentWindow, previousWindow time.Time) (int, int, error)
}

type Option

type Option func(rl *rateLimiter)

func WithKeyByIP added in v0.7.0

func WithKeyByIP() Option

func WithKeyByRealIP added in v0.7.0

func WithKeyByRealIP() Option

func WithKeyFuncs

func WithKeyFuncs(keyFuncs ...KeyFunc) Option

func WithLimitCounter

func WithLimitCounter(c LimitCounter) Option

func WithLimitHandler

func WithLimitHandler(h http.HandlerFunc) Option

func WithNoop added in v0.7.3

func WithNoop() Option

func WithResponseHeaders added in v0.11.0

func WithResponseHeaders(headers ResponseHeaders) Option

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.

Jump to

Keyboard shortcuts

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