Documentation
¶
Overview ¶
Package middleware ships zip's canonical generic middleware stack. Use these via app.Use(middleware.Recover(), middleware.RequestID(), ...).
Every middleware here is a zip.Handler (NOT a raw fiber.Handler) so the user-facing handler signature stays uniform.
Auth-specific middleware (JWT validation, identity-header stripping) lives in github.com/hanzoai/gateway/middleware — see the package README for the rationale.
Index ¶
- func CORS(cfg CORSConfig) zip.Handler
- func Logger(base luxlog.Logger) zip.Handler
- func MaxBody(n int) zip.Handler
- func RateLimit(cfg RateLimitConfig) zip.Handler
- func Recover() zip.Handler
- func RequestID() zip.Handler
- func Telemetry(sink O11ySink) zip.Handler
- func Timeout(d time.Duration) zip.Handler
- type Breaker
- type BreakerConfig
- type BreakerState
- type CORSConfig
- type O11ySink
- type RateLimitConfig
- type Stats
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Logger ¶
Logger logs each request with method, path, status, duration. Adds request_id / org / user to the request-scoped logger via SetLog.
func RateLimit ¶
func RateLimit(cfg RateLimitConfig) zip.Handler
RateLimit returns a per-org (or per-IP fallback) token-bucket limiter. Buckets are kept in-memory; suitable for single-pod deployments. Multi-pod deployments should use a distributed limiter at the gateway.
func Recover ¶
Recover catches handler panics and turns them into a 500 JSON response. Always include this first in the chain.
func RequestID ¶
RequestID injects an X-Request-Id header (incoming if present; else 16-byte hex). Available via c.RequestID().
Types ¶
type Breaker ¶
type Breaker struct {
// contains filtered or unexported fields
}
Breaker is one circuit-breaker instance. Safe for concurrent use.
Breaker is intentionally per-process: it is a memoized recent-failure view, not a coordinated cross-replica policy. N replicas of the same service running their own breaker is the correct shape — each replica sheds load it cannot serve. There is no shared state, no leader, no coordination overhead.
func NewBreaker ¶
func NewBreaker(cfg BreakerConfig) *Breaker
NewBreaker constructs a Breaker with defaults applied.
func (*Breaker) Allow ¶
Allow reports whether the breaker permits a new request right now. Callers that need to wrap arbitrary code (not a zip handler) can use this directly and then call Report(err, status) when the work completes.
In half-open state, Allow returns true for exactly one in-flight request; subsequent callers see false until the in-flight one reports.
func (*Breaker) Middleware ¶
Breaker returns the zip middleware form of b. The middleware:
- Calls b.Allow() before delegating to the next handler.
- If Allow returns false, short-circuits with 503 and a brief {"error":"upstream unavailable"} body. NO retry. NO queue. Fail fast — let the client back off.
- Otherwise runs c.Continue() and reports the outcome.
The breaker is single-purpose: it sheds load when the protected resource is in trouble. Combine with other middleware (Logger, Recover, Timeout) the usual way.
func (*Breaker) Report ¶
Report records the outcome of a request that previously Allow()ed. err is the handler error; status is the final response status. The breaker uses cfg.FailureClassifier to decide whether it was a failure.
func (*Breaker) State ¶
func (b *Breaker) State() BreakerState
State returns the breaker's current state. Cheap; lock-free.
type BreakerConfig ¶
type BreakerConfig struct {
// FailureThreshold is the number of consecutive failures (or the
// failure count within the rolling window) that trips the breaker.
// Default: 5.
FailureThreshold int
// SuccessThreshold is the number of consecutive successes in
// half-open that closes the breaker. Default: 1 (the first
// half-open success closes immediately).
SuccessThreshold int
// OpenWindow is how long the breaker stays open before transitioning
// to half-open. Default: 5s.
OpenWindow time.Duration
// FailureClassifier classifies an outcome as a failure. By default a
// non-nil handler error OR a 5xx response body is a failure.
FailureClassifier func(err error, status int) bool
// Now is the clock. Inject for tests. Default: time.Now.
Now func() time.Time
// OnStateChange, if set, is called every time the breaker transitions.
// Useful for metrics. Called from the breaker's critical section, so
// the callback MUST NOT block.
OnStateChange func(prev, next BreakerState)
}
BreakerConfig configures a single circuit breaker. The breaker is a composable middleware: failures are anything that returns a non-nil error OR writes a 5xx status. The "target" abstraction is the caller's — wrap one breaker per upstream where you want isolation.
type BreakerState ¶
type BreakerState int32
BreakerState is the externally observable state of a circuit breaker.
const ( // BreakerClosed allows traffic. Failures are counted toward the trip // threshold. BreakerClosed BreakerState = iota // BreakerOpen rejects every request with 503 until the open window // elapses, at which point the breaker transitions to half-open. BreakerOpen // BreakerHalfOpen permits one request at a time. A success closes // the breaker; a failure re-opens it. BreakerHalfOpen )
type CORSConfig ¶
type CORSConfig struct {
AllowOrigins []string // "*" or explicit list. Default: ["*"]
AllowMethods []string // Default: GET,POST,PUT,DELETE,PATCH,OPTIONS
AllowHeaders []string // Default: Content-Type,Authorization,X-Request-Id
ExposeHeaders []string
AllowCreds bool
MaxAge int // seconds
}
CORSConfig configures the CORS middleware.
type O11ySink ¶
type O11ySink interface {
// Record reports a single request to the o11y backend.
Record(method, path string, status int, dur time.Duration, attrs map[string]string)
}
O11ySink is the minimum interface zip's telemetry middleware consumes. Real implementations live in hanzoai/insights / o11y SDKs. nil sink = no-op middleware.
type RateLimitConfig ¶
type RateLimitConfig struct {
// Limit is the number of requests permitted per Window.
Limit int
// Window is the time window (e.g. 1*time.Minute).
Window time.Duration
// KeyFn extracts the bucket key from the request. Default: c.Org()
// if present, else c.Fiber().IP().
KeyFn func(c *zip.Ctx) string
}
RateLimitConfig configures the per-key token bucket.