Documentation
¶
Overview ¶
Package middleware provides standard net/http server middleware for skit services.
It covers the cross-cutting concerns most HTTP services need: panic recovery, trace-context injection, structured access logging, request timeouts, body-size limits, CORS, security headers, response compression, and request-id echo. Every middleware has the signature func(http.Handler) http.Handler (aliased as Middleware), so it composes with router.Use, router.With, and go-pkgz/routegroup. Because TraceRequest seeds a trace id and AccessLog logs through the skit logger, log lines carry trace_id automatically.
These are the transport layer of the two-layer model (see the router package): they wrap the encoded response and every route — including raw http.Handlers and the typed rest boundary — via router.Use / router.With. Concerns that need the typed ResponseEncoder a handler returns (auth, validation, error localization) belong instead in the application layer as rest.MidFunc (router.UseApp / WithApp / per-route).
Ordering ¶
Register them outermost-first. Panics goes first so it wraps everything; TraceRequest next so downstream logs and spans share the request's trace context; then AccessLog, then the limit/timeout guards:
r := router.New(appMids...) r.Use( middleware.Panics(log), // recover -> 500, log stack middleware.TraceRequest(tracer), // extract/seed W3C trace context middleware.AccessLog(log), // one structured line per request middleware.SizeLimit(1<<20), // cap request body at 1 MiB middleware.Timeout(5*time.Second), // cancel ctx after 5s )
Middleware ¶
- Panics(log): recover from panics, log the stack, respond 500. A nil log skips logging.
- TraceRequest(tracer): extract incoming W3C trace context and ensure a trace id is available for logging.
- AccessLog(log): emit one structured line per request using OpenTelemetry HTTP semantic-convention field names. A nil log skips logging.
- Timeout(d): cancel the request context after d; a non-positive d disables it (returns next unchanged).
- SizeLimit(n): cap the request body at n bytes via http.MaxBytesReader; a non-positive n disables it.
- CORS(cfg): apply a cross-origin policy — set Access-Control-* headers on allowed origins and answer preflight OPTIONS with 204.
- SecureHeaders(): set conservative security response headers (nosniff, frame-deny, no-referrer) without overriding ones a handler set.
- Compress(): gzip responses when the client sends Accept-Encoding: gzip.
- RequestID(): echo the correlation id (incoming X-Request-ID, else the trace id) in the X-Request-ID response header.
The last four are not part of the generated scaffold's default chain — wire them per service (CORS in particular needs a per-deployment origin policy).
Index ¶
- Constants
- type CORSConfig
- type Middleware
- func AccessLog(log *logger.Logger) Middleware
- func CORS(cfg CORSConfig) Middleware
- func Compress() Middleware
- func Panics(log *logger.Logger) Middleware
- func RequestID() Middleware
- func SecureHeaders() Middleware
- func SizeLimit(n int64) Middleware
- func Timeout(d time.Duration) Middleware
- func TraceRequest(tracer trace.Tracer) Middleware
Constants ¶
const RequestIDHeader = "X-Request-ID"
RequestIDHeader is the header RequestID reads and echoes.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CORSConfig ¶ added in v0.6.0
type CORSConfig struct {
// AllowedOrigins is the set of permitted Origin values, e.g.
// {"https://app.example.com"}. The single entry "*" allows any origin.
AllowedOrigins []string
// AllowedMethods defaults to GET, POST, PUT, PATCH, DELETE, OPTIONS.
AllowedMethods []string
// AllowedHeaders defaults to Origin, Content-Type, Accept, Authorization.
AllowedHeaders []string
// ExposedHeaders lists response headers the browser is allowed to read.
ExposedHeaders []string
// AllowCredentials sets Access-Control-Allow-Credentials. With credentials
// the wildcard is never sent: the specific request Origin is echoed instead
// (browsers reject "*" with credentials).
AllowCredentials bool
// MaxAge is the preflight cache lifetime in seconds (Access-Control-Max-Age).
MaxAge int
}
CORSConfig configures the CORS middleware. The zero value allows no origins (every cross-origin request is passed through without CORS headers, so the browser blocks it) — set AllowedOrigins to opt in.
type Middleware ¶
Middleware is standard net/http middleware.
func AccessLog ¶
func AccessLog(log *logger.Logger) Middleware
AccessLog logs one structured line per request. Because it logs through the skit logger, each line carries the request's trace_id automatically.
func CORS ¶ added in v0.6.0
func CORS(cfg CORSConfig) Middleware
CORS returns middleware applying the policy in cfg. It sets the Access-Control-* headers on allowed cross-origin responses and answers the preflight OPTIONS request with 204. Requests without an Origin are passed through untouched; a request from a disallowed origin gets no CORS headers (so the browser blocks it), and a disallowed preflight is short-circuited with 204 and no Allow-* headers.
func Compress ¶ added in v0.6.0
func Compress() Middleware
Compress returns middleware that gzip-encodes responses when the client sends "Accept-Encoding: gzip". It sets Content-Encoding: gzip and Vary: Accept-Encoding, and drops the (now-wrong) Content-Length. Bodyless responses (1xx, 204, 304) are never encoded, and a handler that already set Content-Encoding is left alone. Flusher is preserved.
func Panics ¶
func Panics(log *logger.Logger) Middleware
Panics recovers panics, logs the stack, and responds with 500.
func RequestID ¶ added in v0.6.0
func RequestID() Middleware
RequestID echoes a request correlation id in the X-Request-ID response header. It reuses the client's incoming X-Request-ID when present; otherwise it falls back to the active trace id (so put this after TraceRequest in the chain). It never overwrites an id a handler set itself. When no id is available it does nothing — it does not synthesize one (tracing owns id generation).
func SecureHeaders ¶ added in v0.6.0
func SecureHeaders() Middleware
SecureHeaders sets a conservative set of security response headers on every response — safe defaults for a JSON API:
X-Content-Type-Options: nosniff // do not MIME-sniff the body X-Frame-Options: DENY // never framed (clickjacking) Referrer-Policy: no-referrer // do not leak the URL in Referer
It only sets a header when the handler has not already set it, so a handler that needs a different value (e.g. an endpoint meant to be embedded) wins.
func SizeLimit ¶
func SizeLimit(n int64) Middleware
SizeLimit caps the request body at n bytes. A non-positive n disables it.
func Timeout ¶
func Timeout(d time.Duration) Middleware
Timeout cancels the request context after d and (if nothing was written) lets downstream handlers observe ctx.Err. A non-positive d disables the middleware.
func TraceRequest ¶
func TraceRequest(tracer trace.Tracer) Middleware
TraceRequest seeds the request context with trace context extracted from incoming headers and ensures a trace id is available for logging.