Help us shape the future of Go! Take the 2026 Go Developer Survey

ratelimit

package
v0.26.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package ratelimit limits requests per key (token, user, IP). Memory uses GCRA, a smooth token bucket that needs one timestamp per key, and fits one process; Window counts fixed windows in a shared store (the database cache, so several servers on one SQL database share budgets); drivers/redis provides a shared GCRA limiter.

Middleware applies a Limiter to a route, keyed by a KeyFunc such as ByIP or ByToken; it sets the RateLimit-* headers, answers 429 with Retry-After and denies when the limiter fails. Apps build limiters with app.RateLimiters(), configured by RATE_LIMIT_STORE.

See https://github.com/AlexGalhardo/vukem/blob/main/docs/subsystems.md.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/AlexGalhardo/vukem/ratelimit"
)

func main() {
	limiter := ratelimit.NewMemory(2, time.Minute)
	h := ratelimit.Middleware(limiter, ratelimit.ByIP)(
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			w.WriteHeader(http.StatusNoContent)
		}),
	)

	for range 3 {
		req := httptest.NewRequest(http.MethodGet, "/api/posts", http.NoBody)
		rec := httptest.NewRecorder()
		h.ServeHTTP(rec, req)
		fmt.Println(rec.Code, rec.Header().Get("RateLimit-Remaining"))
	}
}
Output:
204 1
204 0
429 0

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ByIP

func ByIP(r *http.Request) string

ByIP keys requests by client IP (honouring trusted proxies).

func ByToken

func ByToken(r *http.Request) string

ByToken keys requests by bearer token (hashed, never stored in clear), falling back to the client IP. Use it after authentication (auth.Required), behind an IP limit: before the token is verified, a client rotating made-up tokens gets a fresh bucket on every request.

func Middleware

func Middleware(l Limiter, key KeyFunc) func(http.Handler) http.Handler

Middleware limits requests with l, keyed by key (ByIP when nil). It sets RateLimit-Limit/Remaining/Reset on every response and answers 429 with Retry-After when the bucket is empty. A failing limiter denies the request (fail closed).

Types

type Counter added in v0.23.0

type Counter interface {
	Increment(ctx context.Context, key string, delta int64, ttl time.Duration) (int64, error)
}

Counter is the store a Window counts in: a cache.Store (database, Redis), a *cache.Cache (adds its key prefix) or anything whose Increment is atomic across the servers sharing it.

type Factory added in v0.23.0

type Factory func(name string, limit int, window time.Duration) Limiter

Factory builds a limiter of limit requests per window; name separates the budgets of limiters sharing one store ("login", "api").

type KeyFunc

type KeyFunc func(r *http.Request) string

KeyFunc picks the bucket for a request; "" skips limiting.

type Limiter

type Limiter interface {
	Allow(ctx context.Context, key string) (Result, error)
}

Limiter decides whether a request for key may proceed.

type Memory

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

Memory is an in-process GCRA limiter: limit requests per window, with bursts up to limit.

func NewMemory

func NewMemory(limit int, window time.Duration) *Memory

NewMemory allows limit requests per window for each key.

func (*Memory) Allow

func (m *Memory) Allow(_ context.Context, key string) (Result, error)

Allow implements Limiter.

type Result

type Result struct {
	Allowed    bool
	Limit      int
	Remaining  int
	ResetAfter time.Duration // until the bucket is full again
	RetryAfter time.Duration // when denied: until the next request is allowed
}

Result is the outcome of one Allow call.

type Window added in v0.23.0

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

Window is a fixed-window limiter over a shared Counter: every server using the same store shares one budget per key. Windows are aligned to the clock, so a client can send up to 2×limit around a window boundary (limit at the end of one, limit at the start of the next); use Memory or the Redis GCRA limiter when bursts must stay within limit.

func NewWindow added in v0.23.0

func NewWindow(c Counter, name string, limit int, window time.Duration) *Window

NewWindow allows limit requests per window for each key, counted in c under "ratelimit:<name>:<SHA-256 of key>:<window start>". name is 1-64 letters, digits, '.', '_' or '-'; with any other name every Allow fails, so the route denies (503) instead of sharing budgets.

func (*Window) Allow added in v0.23.0

func (w *Window) Allow(ctx context.Context, key string) (Result, error)

Allow implements Limiter. Denied requests count too, so a client that keeps hammering stays limited until the window ends.

Jump to

Keyboard shortcuts

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