ratelimiter

package
v0.0.26 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 9 Imported by: 0

README

Rate Limiter Middleware

What it does

Limits request rate by IP, user, tenant, API key, or custom key to protect fairness and prevent abuse.

How to implement

package main

import (
	"time"

	"github.com/oarkflow/fh"
	"github.com/oarkflow/fh/mw/ratelimiter"
)

func main() {
	app := fh.New()
	app.Use(ratelimiter.New(ratelimiter.Config{
		Max:         100,
		Window:      time.Minute,
		SendHeaders: true,
	}))

	app.Get("/", func(c fh.Ctx) error {
		return c.Status(fh.StatusOK).SendString("ok")
	})
}

Impact

Controls abuse and overload. Store choice affects memory usage and multi-node correctness.

Ordering guidance

Run after real IP and authentication if the key depends on client identity. Run before expensive handlers.

Production considerations

Use distributed stores for multi-node deployments. Return clear 429 responses with retry headers. Track limit hits by route and key type.

Persistent storage

Config.Store is a kv.Store and defaults to kv.NewMemoryStore. For counters that must survive a process restart on a single node, construct kv.NewFileStore(dir, kv.WithFileGCInterval(gcInterval)) and pass it as Config.Store. It is not a substitute for a distributed store across multiple nodes.

Documentation

Index

Constants

View Source
const (
	HeaderLimit      = "X-RateLimit-Limit"
	HeaderRemaining  = "X-RateLimit-Remaining"
	HeaderReset      = "X-RateLimit-Reset"
	HeaderRetryAfter = "Retry-After"
)

Variables

View Source
var ErrLimitReached = errors.New("ratelimiter: limit reached")

Functions

func DefaultLimitReachedHandler

func DefaultLimitReachedHandler(ctx fh.Ctx, result Result) error

func New

func New(config Config) fh.HandlerFunc

func NewAdaptive

func NewAdaptive(cfg AdaptiveConfig) (fh.HandlerFunc, func())

Types

type AdaptiveConfig

type AdaptiveConfig struct {
	BaseConfig      Config
	HighWaterMark   int64
	ScaleDownFactor float64
	ScaleUpFactor   float64
	MinLimit        int
	MaxLimit        int
	CheckInterval   time.Duration
}

type Config

type Config struct {
	Max    int
	Window time.Duration

	KeyFunc KeyFunc
	Skip    SkipFunc

	// Store backs the rate-limit counters. Defaults to an in-process
	// kv.MemoryStore. Pass a kv.NewFileStore(dir, ...) to persist counters
	// across restarts on a single node, or any other kv.Store implementation
	// for a distributed backend.
	Store kv.Store

	SendHeaders bool

	LimitReached LimitReachedHandler
}

type KeyFunc

type KeyFunc func(ctx fh.Ctx) string

type LimitReachedHandler

type LimitReachedHandler func(ctx fh.Ctx, result Result) error

type LoadAwareLimiter

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

func NewLoadAware

func NewLoadAware(baseCfg Config, highWater int64) *LoadAwareLimiter

func (*LoadAwareLimiter) CurrentLimit

func (l *LoadAwareLimiter) CurrentLimit() int

func (*LoadAwareLimiter) Middleware

func (l *LoadAwareLimiter) Middleware() fh.HandlerFunc

func (*LoadAwareLimiter) SetBaseLimit

func (l *LoadAwareLimiter) SetBaseLimit(limit int)

func (*LoadAwareLimiter) Stop

func (l *LoadAwareLimiter) Stop()

type Result

type Result struct {
	Allowed    bool
	Limit      int
	Remaining  int
	Used       int
	ResetAt    time.Time
	RetryAfter time.Duration
}

func Allow

func Allow(store kv.Store, key string, limit int, window time.Duration, now time.Time) (Result, error)

Allow applies a fixed-window rate-limit check for key against store, admitting up to limit requests per window. One file-per-key or shard-per-key counter is read, incremented and written back atomically via kv.Store.Mutate, so the same algorithm works unchanged whether store is a kv.MemoryStore, a kv.FileStore, or any other kv.Store implementation.

type SkipFunc

type SkipFunc func(ctx fh.Ctx) bool

Jump to

Keyboard shortcuts

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