ipthrottle

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: 8 Imported by: 0

README

ipthrottle middleware

Applies per-IP and global fixed-window limits with bounded IP cardinality.

app.Use(ipthrottle.New(ipthrottle.Config{
    MaxPerIP: 100,
    GlobalMax: 10_000,
    MaxIPs: 65_536,
    Window: time.Minute,
    Store: kv.NewMemoryStore(),
}))

When MaxIPs is exhausted the middleware fails closed. Install trusted real-IP normalization first when requests arrive through a proxy.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrCapacityExceeded = errors.New("ipthrottle: capacity exceeded")

ErrCapacityExceeded is returned by a Store's Increment when the store is already tracking cfg.MaxIPs distinct keys and the incoming key is new.

Functions

func Increment

func Increment(store kv.Store, key string, window time.Duration, maxKeys int, now time.Time) (int, error)

Increment records one request for key within window (relative to now) against store, resetting the count if the previous window has elapsed, and returns the count so far in the current window. Per-key state (count, window start) is JSON-encoded and mutated race-free via kv.Store.Mutate, which reproduces the exact "reset on elapsed window, otherwise increment" semantics this package has always had, on top of any kv.Store implementation (kv.MemoryStore, kv.FileStore, or otherwise).

If store is not yet tracking key and is already at maxKeys distinct keys (maxKeys <= 0 means unlimited), Increment returns ErrCapacityExceeded without recording the request.

The maxKeys cardinality guard is checked via a Get+Len pair before the Mutate call, since only Mutate holds the per-key lock and kv.Store has no facility for locking a key that does not exist yet while a separate, store-wide Len() check runs. This means the check is a soft/approximate cap under heavy concurrent creation of brand-new distinct keys: two concurrent Increment calls for two different new keys can both observe Len() < maxKeys and both be admitted, so the cardinality bound can be exceeded by a small margin under contention. The pre-retrofit implementation held a single mutex across the entire check-then-insert sequence and so enforced an exact bound; kv.Store's per-key Mutate lock does not extend to a store-wide cardinality decision, so this is a deliberate, documented trade-off rather than an oversight.

func New

func New(cfg Config) fh.HandlerFunc

Types

type Config

type Config struct {
	MaxPerIP  int
	GlobalMax int
	MaxIPs    int
	Window    time.Duration
	KeyFunc   func(fh.Ctx) string
	Reject    RejectHandler

	// Store holds per-IP window/count state, keyed by IP and JSON-encoded
	// via Increment. Defaults to a new kv.MemoryStore, which reproduces the
	// exact map+mutex+sweep behavior this package has always used. Pass a
	// kv.FileStore (see github.com/oarkflow/fh/pkg/storage/kv) to persist
	// counters across restarts.
	Store kv.Store
}

type RejectHandler

type RejectHandler func(fh.Ctx, string, int) error

Jump to

Keyboard shortcuts

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