Documentation
¶
Overview ¶
Package mw provides transport-level HTTP middleware for Einherjar services.
All middleware functions return func(http.Handler) http.Handler and are composed via [server.WithMiddleware] or chi's Use method.
Recommended middleware order (outermost first) ¶
server.WithMiddleware(
mw.Recover(),
mw.RequestID(uuid.NewString),
mw.RequestLogger(logger),
mw.CORS([]string{"https://example.com"}),
)
Rate limiting ¶
// In-memory (default — no extra dependencies) store := mw.NewInMemoryRateLimiterStore(100, 20) srv.Use(mw.IPRateLimit(store, logger)) // Distributed — swap store, middleware unchanged store := valkeymw.NewRateLimiterStore(client, 100, 20) srv.Use(mw.IPRateLimit(store, logger))
Index ¶
- func CORS(origins []string) func(http.Handler) http.Handler
- func CORSAllowAll() func(http.Handler) http.Handler
- func IPRateLimit(store RateLimiterStore, logger logging.Logger) func(http.Handler) http.Handler
- func Recover(logger logging.Logger) func(http.Handler) http.Handler
- func RequestID(generator func() string) func(http.Handler) http.Handler
- func RequestLogger(logger logging.Logger) func(http.Handler) http.Handler
- func UserRateLimit(store RateLimiterStore, logger logging.Logger) func(http.Handler) http.Handler
- type InMemoryRateLimiterStore
- type RateLimiterStore
- type StatusRecorder
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CORS ¶
CORS sets cross-origin resource sharing headers for the provided origins. Returns 204 No Content for OPTIONS preflight requests. Pass the outermost origins first; an empty slice is a no-op.
func CORSAllowAll ¶
CORSAllowAll is a convenience wrapper that allows any origin. Use only in development — never in production.
func IPRateLimit ¶
IPRateLimit returns middleware that rate-limits requests by client IP address. The IP is extracted from X-Forwarded-For (first value) or RemoteAddr. When the store returns an error the middleware fails open: the error is logged and the request is allowed through.
func Recover ¶
Recover catches panics in downstream handlers, writes a 500 response, and logs the recovered value with a stack trace. Place it as the outermost middleware.
func RequestID ¶
RequestID injects a unique request ID into the context (via logz.WithRequestID) and sets the X-Request-ID response header. generator is called once per request — pass uuid.NewString or a custom function.
func RequestLogger ¶
RequestLogger logs each request after the handler returns, including method, path, status code, and latency. Uses StatusRecorder to capture the status. Place after RequestID so the request ID is available in the log record.
func UserRateLimit ¶
UserRateLimit returns middleware that rate-limits by authenticated user ID. Falls back to client IP when no security.Identity is present in the context. When the store returns an error the middleware fails open.
Types ¶
type InMemoryRateLimiterStore ¶
type InMemoryRateLimiterStore struct {
// contains filtered or unexported fields
}
InMemoryRateLimiterStore is a per-key token-bucket rate limiter backed by an in-memory map. Suitable for single-instance deployments or development. For distributed rate limiting implement RateLimiterStore with a Valkey or Redis backend and pass it to IPRateLimit or UserRateLimit instead.
Stale entries are evicted every 5 minutes by a background goroutine. [Allow] always returns a nil error — in-memory never has infrastructure failures.
func NewInMemoryRateLimiterStore ¶
func NewInMemoryRateLimiterStore(rps float64, burst int) *InMemoryRateLimiterStore
NewInMemoryRateLimiterStore creates a per-key token bucket store. rps is the sustained request rate per second per key. burst is the maximum instantaneous burst per key.
type RateLimiterStore ¶
type RateLimiterStore interface {
// Allow returns true when the request for the given key is within the rate limit.
// A non-nil error means the store is temporarily unavailable; middleware fails open.
Allow(ctx context.Context, key string) (bool, error)
}
RateLimiterStore is the pluggable backend for rate-limiting middleware. The key is determined by the caller (client IP or user ID).
Shipped implementations:
- InMemoryRateLimiterStore — per-key token bucket, stdlib only (this package)
- einherjar/cache-valkey — Valkey-backed store for distributed deployments
cache-valkey satisfies this interface via Go duck typing — it never imports web/mw.
type StatusRecorder ¶
type StatusRecorder struct {
http.ResponseWriter
Status int
}
StatusRecorder wraps http.ResponseWriter to capture the written status code. Used by RequestLogger to log the response status after the handler returns.
func (*StatusRecorder) WriteHeader ¶
func (r *StatusRecorder) WriteHeader(code int)
WriteHeader captures the status code and delegates to the underlying writer.