Introduces server/ sub-package as the server-side companion to the existing Client. Includes Router (over http.ServeMux with groups and mounting), graceful shutdown with signal handling, health endpoints (/healthz, /readyz), and built-in middlewares (RequestID, Recovery, Logging). Zero external dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package server
|
|
|
|
import (
|
|
"log/slog"
|
|
"time"
|
|
)
|
|
|
|
type serverOptions struct {
|
|
addr string
|
|
readTimeout time.Duration
|
|
readHeaderTimeout time.Duration
|
|
writeTimeout time.Duration
|
|
idleTimeout time.Duration
|
|
shutdownTimeout time.Duration
|
|
logger *slog.Logger
|
|
middlewares []Middleware
|
|
onShutdown []func()
|
|
}
|
|
|
|
// Option configures a Server.
|
|
type Option func(*serverOptions)
|
|
|
|
// WithAddr sets the listen address. Default is ":8080".
|
|
func WithAddr(addr string) Option {
|
|
return func(o *serverOptions) { o.addr = addr }
|
|
}
|
|
|
|
// WithReadTimeout sets the maximum duration for reading the entire request.
|
|
func WithReadTimeout(d time.Duration) Option {
|
|
return func(o *serverOptions) { o.readTimeout = d }
|
|
}
|
|
|
|
// WithReadHeaderTimeout sets the maximum duration for reading request headers.
|
|
func WithReadHeaderTimeout(d time.Duration) Option {
|
|
return func(o *serverOptions) { o.readHeaderTimeout = d }
|
|
}
|
|
|
|
// WithWriteTimeout sets the maximum duration before timing out writes of the response.
|
|
func WithWriteTimeout(d time.Duration) Option {
|
|
return func(o *serverOptions) { o.writeTimeout = d }
|
|
}
|
|
|
|
// WithIdleTimeout sets the maximum amount of time to wait for the next request
|
|
// when keep-alives are enabled.
|
|
func WithIdleTimeout(d time.Duration) Option {
|
|
return func(o *serverOptions) { o.idleTimeout = d }
|
|
}
|
|
|
|
// WithShutdownTimeout sets the maximum duration to wait for active connections
|
|
// to close during graceful shutdown. Default is 15 seconds.
|
|
func WithShutdownTimeout(d time.Duration) Option {
|
|
return func(o *serverOptions) { o.shutdownTimeout = d }
|
|
}
|
|
|
|
// WithLogger sets the structured logger used by the server for lifecycle events.
|
|
func WithLogger(l *slog.Logger) Option {
|
|
return func(o *serverOptions) { o.logger = l }
|
|
}
|
|
|
|
// WithMiddleware appends server middlewares to the chain.
|
|
// These are applied to the handler in the order given.
|
|
func WithMiddleware(mws ...Middleware) Option {
|
|
return func(o *serverOptions) { o.middlewares = append(o.middlewares, mws...) }
|
|
}
|
|
|
|
// WithOnShutdown registers a function to be called during graceful shutdown,
|
|
// before the HTTP server begins draining connections.
|
|
func WithOnShutdown(fn func()) Option {
|
|
return func(o *serverOptions) { o.onShutdown = append(o.onShutdown, fn) }
|
|
}
|
|
|
|
// Defaults returns a production-ready set of options including standard
|
|
// middleware (RequestID, Recovery, Logging), sensible timeouts, and the
|
|
// provided logger.
|
|
//
|
|
// Middleware order: RequestID → Recovery → Logging → user handler.
|
|
func Defaults(logger *slog.Logger) []Option {
|
|
return []Option{
|
|
WithReadHeaderTimeout(10 * time.Second),
|
|
WithIdleTimeout(120 * time.Second),
|
|
WithShutdownTimeout(15 * time.Second),
|
|
WithLogger(logger),
|
|
WithMiddleware(
|
|
RequestID(),
|
|
Recovery(WithRecoveryLogger(logger)),
|
|
Logging(logger),
|
|
),
|
|
}
|
|
}
|