Golar v0.5.1

Middleware

github.com/dayanchm/golar/http/middleware provides standard func(http.Handler) http.Handler middleware. Each one is opt-in and works with app.Use, the router, http.ServeMux or any other Go router.

app.Use(
    middleware.RequestID(),
    middleware.Logger(),
    middleware.Recovery(),
    middleware.SecureHeaders(),
    middleware.CORS(middleware.CORSConfig{AllowOrigins: []string{"https://app.example.com"}}),
    middleware.BodyLimit(10 << 20),
    middleware.Timeout(30 * time.Second),
    middleware.Compress(),
)

Middleware runs in registration order, with the first outermost. Generated projects use RequestID, Logger, Recovery, SecureHeaders, a 10 MiB BodyLimit and Compress.

Logger and Recovery

Logger logs method, path, status, bytes, duration and the request ID when RequestID is used. Recovery turns panics into a logged stack trace and a generic 500. Put Logger before Recovery so recovered panics are logged with status 500. Both accept an optional *log.Logger.

RequestID

middleware.RequestID()
middleware.RequestID(middleware.RequestIDConfig{Header: "X-Trace-ID", Generate: newID})
id := middleware.RequestIDFrom(r.Context())

An incoming ID made of up to 128 letters, digits, ., _, : or - is reused; anything else is replaced with 16 random bytes in hex. The ID is set on the response and stored in the request context. Clients can choose their own IDs, so use them for correlation, not for security decisions.

CORS

middleware.CORS(middleware.CORSConfig{
    AllowOrigins:     []string{"https://app.example.com"},
    AllowOriginFunc:  func(origin string) bool { return strings.HasSuffix(origin, ".preview.example.com") },
    AllowCredentials: true,
    ExposeHeaders:    []string{"X-Request-ID"},
    MaxAge:           10 * time.Minute,
})

Origins match exactly. Allowed origins get Access-Control-Allow-Origin and, with credentials, Access-Control-Allow-Credentials. Preflight requests (OPTIONS with Access-Control-Request-Method) are answered with 204 and do not reach routes. Other origins get no CORS headers, so browsers block the response.

AllowMethods defaults to GET, HEAD, POST, PUT, PATCH and DELETE, and AllowHeaders to Accept, Authorization, Content-Type, X-CSRF-Token and X-Requested-With. "*" allows any origin but cannot be combined with AllowCredentials; CORS panics on that configuration.

Cookie-authenticated APIs that accept other origins still need CSRF protection.

SecureHeaders

middleware.SecureHeaders() uses DefaultSecureHeaders:

Header Value
Strict-Transport-Security max-age=31536000; includeSubDomains, only over TLS or with X-Forwarded-Proto: https
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin

Pass a SecureHeadersConfig to choose your own values; empty fields are omitted. ContentSecurityPolicy is off by default because a useful policy depends on the application. Headers are set before the handler runs, so a handler can override them for one response.

BodyLimit

middleware.BodyLimit(10 << 20) answers requests that declare a larger Content-Length with 413. Other bodies stop with *http.MaxBytesError when the limit is reached; the default Golar error handler turns that error, including from c.Validate, into 413.

Timeout

middleware.Timeout(30 * time.Second) cancels the request context after the duration. Handlers must pass r.Context() to database and HTTP calls and return when it is done; Timeout cannot stop code that ignores it. If the deadline passes before anything is written, the client gets 503. Responses that have started are left alone.

Compress

middleware.Compress()
middleware.Compress(middleware.CompressConfig{Level: gzip.BestSpeed, MinSize: 512})

Responses are gzipped when the client sends Accept-Encoding: gzip and the body reaches MinSize (1024 bytes by default). HEAD requests, 204, 206 and 304 responses, responses that already set Content-Encoding, text/event-stream and already-compressed types (images other than SVG, video, audio, fonts, archives, PDF and WebAssembly) are passed through. Vary: Accept-Encoding is always set.

Flushing with http.NewResponseController(w).Flush() sends buffered output immediately. A flush before MinSize bytes sends the response uncompressed.

Edit this page on GitHub