Add production-ready HTTP server package with routing, health checks, and middleware

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>
This commit is contained in:
2026-03-21 13:41:54 +03:00
parent 6b901c931e
commit cea75d198b
12 changed files with 1215 additions and 0 deletions

89
server/options.go Normal file
View File

@@ -0,0 +1,89 @@
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),
),
}
}