Documentation
¶
Overview ¶
Package middleware is the request-scoped plumbing every route needs before any handler runs: identity on the context, a trace id, a recovered panic, a deadline.
Order is not a style choice here. RequestID must precede Recover so a panic is logged with the id the client was given; Recover must precede everything that can panic; Timeout must precede the handler, not the logging. Chain gives you the order that is correct, and is what you should use unless you know why you are deviating.
Index ¶
- Constants
- func AccessLog(opt AccessLogOptions) fiber.Handler
- func CORS(opt CORSOptions) fiber.Handler
- func Chain(opt ChainOptions) []fiber.Handler
- func Identity(resolve IdentityResolver) fiber.Handler
- func RateLimit(opt RateLimitOptions) fiber.Handler
- func Recover() fiber.Handler
- func RequestID() fiber.Handler
- func SecureHeaders(opt SecureHeadersOptions) fiber.Handler
- func Timeout(d time.Duration) fiber.Handler
- type AccessLogOptions
- type CORSOptions
- type ChainOptions
- type IdentityResolver
- type MemoryRateLimitStore
- type RateLimitOptions
- type RateLimitStore
- type SecureHeadersOptions
Constants ¶
const HeaderRequestID = "X-Request-Id"
HeaderRequestID is the inbound/outbound correlation header.
const HeaderTraceID = "X-Trace-Id"
HeaderTraceID is the header a client quotes in a support ticket.
Variables ¶
This section is empty.
Functions ¶
func AccessLog ¶
func AccessLog(opt AccessLogOptions) fiber.Handler
AccessLog writes one line per request that failed, was slow, or — when LogSuccess is set — per request at all.
func CORS ¶
func CORS(opt CORSOptions) fiber.Handler
CORS answers preflights and sets the cross-origin headers.
It echoes the matched origin rather than sending the configured list, and sets Vary: Origin, so a shared cache cannot serve one tenant's origin header to another's browser.
func Chain ¶
func Chain(opt ChainOptions) []fiber.Handler
Chain returns the standard middleware stack in the order that is correct.
RequestID first so everything after it can log the id. Recover second so a panic anywhere below is caught WITH that id. Headers and logging next. Identity before Timeout, so an authentication failure is not reported as a timeout. The handler last.
func Identity ¶
func Identity(resolve IdentityResolver) fiber.Handler
Identity puts the tenant and actor on the Go context, where every layer below reads them from.
It does NOT decide whether the request is allowed — that is a guard's job. Keeping the two apart is what lets an endpoint be public and still know who the caller is when they happen to be signed in.
func RateLimit ¶
func RateLimit(opt RateLimitOptions) fiber.Handler
RateLimit refuses callers over their budget with 429 and the standard RateLimit-* headers so a well-behaved client can back off instead of hammering.
func Recover ¶
Recover turns a panic into a 500 in the standard envelope, with the stack in the log and nothing of it on the wire.
A panic that escapes to Fiber's own recovery loses the trace id and the request context, which is exactly the information the incident needs.
func RequestID ¶
RequestID establishes the correlation id for the request and puts it on the Fiber locals, on the Go context and on the response.
It TRUSTS an inbound X-Request-Id, which is what makes a trace span a gateway, a service and a worker. That trust is only safe behind your own edge: a public listener should strip the header there, or this becomes a log-injection vector.
func SecureHeaders ¶
func SecureHeaders(opt SecureHeadersOptions) fiber.Handler
SecureHeaders sets the response headers that cost nothing and close a category of attack each.
func Timeout ¶
Timeout bounds a handler.
It sets a deadline on the context; it cannot stop a handler that ignores it. That is a real limit, not an oversight — Go has no way to kill a goroutine — so the value of this middleware is entirely in handlers and drivers that honour ctx. Code that performs I/O without a context is the thing to fix.
Types ¶
type AccessLogOptions ¶
type AccessLogOptions struct {
// SkipPaths are not logged. Put the health and metrics endpoints here:
// a liveness probe every two seconds is 43,200 log lines a day saying
// nothing happened.
SkipPaths []string
// SlowThreshold promotes a slow request to warn. Zero disables it.
SlowThreshold time.Duration
// LogSuccess logs 2xx and 3xx as well. Off by default — the access log of
// a busy service is a metric, and metrics belong in a metrics system.
LogSuccess bool
}
AccessLogOptions configures AccessLog.
type CORSOptions ¶
type CORSOptions struct {
// AllowOrigins is an explicit list. "*" is honoured ONLY when
// AllowCredentials is false — the combination is forbidden by the spec and
// browsers reject it, so this middleware refuses to emit it rather than
// letting you ship a configuration that silently never works.
AllowOrigins []string
// AllowOriginFunc decides per request, for the wildcard-subdomain case.
// It takes precedence over AllowOrigins.
AllowOriginFunc func(origin string) bool
AllowMethods []string
AllowHeaders []string
ExposeHeaders []string
AllowCredentials bool
MaxAge time.Duration
}
CORSOptions configures CORS.
type ChainOptions ¶
type ChainOptions struct {
Timeout time.Duration
AccessLog AccessLogOptions
SecureHeaders *SecureHeadersOptions
Identity IdentityResolver
}
ChainOptions configures Chain.
type IdentityResolver ¶
IdentityResolver extracts the tenant and actor from a request. Return an error to refuse the request outright; return zero values to let it proceed anonymously and have a guard decide.
type MemoryRateLimitStore ¶
type MemoryRateLimitStore struct {
// contains filtered or unexported fields
}
MemoryRateLimitStore is a fixed-window counter in process memory.
It is correct for one instance and for development. It is NOT a distributed limiter: with N replicas the effective limit is N times the configured one, and the counts vanish on restart.
func NewMemoryRateLimitStore ¶
func NewMemoryRateLimitStore() *MemoryRateLimitStore
NewMemoryRateLimitStore returns an in-process store.
type RateLimitOptions ¶
type RateLimitOptions struct {
// Max requests per Window. Zero disables the middleware entirely, which
// is the honest behaviour for an unset config — better than a limit of
// zero that refuses everything.
Max int
Window time.Duration
// KeyFunc identifies the caller. The default is the client IP, which is
// the right default and the wrong one behind a proxy that does not set
// X-Forwarded-For: everyone shares one key and one user exhausts it for
// all. Prefer an account or api-key based function.
KeyFunc func(c fiber.Ctx) string
// Skip exempts a request — a health probe, an internal caller.
Skip func(c fiber.Ctx) bool
Store RateLimitStore
// FailOpen decides what happens when the store errors. True (the default)
// serves the request: a Redis blip should not take the API down with it.
// Set false where the limit protects something that must not be exceeded.
FailOpen *bool
}
RateLimitOptions configures RateLimit.
type RateLimitStore ¶
type RateLimitStore interface {
// Incr adds one to key's count within window and returns the new count
// and the time the window resets.
Incr(key string, window time.Duration) (count int, resetAt time.Time, err error)
}
RateLimitStore counts requests per key. The in-process implementation below is correct for a single instance; behind more than one replica, back it with Redis so the limit is the limit rather than the limit times the replica count.
type SecureHeadersOptions ¶
type SecureHeadersOptions struct {
// HSTSMaxAge enables Strict-Transport-Security. Zero disables it — do not
// send HSTS from a service reachable over plain HTTP in development, or a
// developer's browser will pin localhost to HTTPS and they will lose an
// afternoon to it.
HSTSMaxAge time.Duration
// ContentSecurityPolicy is sent verbatim when non-empty. The default below
// suits a JSON API; a service that serves HTML needs its own.
ContentSecurityPolicy string
// FrameOptions defaults to DENY.
FrameOptions string
}
SecureHeadersOptions configures SecureHeaders.