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:
50
server/middleware_recovery.go
Normal file
50
server/middleware_recovery.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// RecoveryOption configures the Recovery middleware.
|
||||
type RecoveryOption func(*recoveryOptions)
|
||||
|
||||
type recoveryOptions struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// WithRecoveryLogger sets the logger for the Recovery middleware.
|
||||
// If not set, panics are recovered silently (500 is still returned).
|
||||
func WithRecoveryLogger(l *slog.Logger) RecoveryOption {
|
||||
return func(o *recoveryOptions) { o.logger = l }
|
||||
}
|
||||
|
||||
// Recovery returns a middleware that recovers from panics in downstream
|
||||
// handlers. A recovered panic results in a 500 Internal Server Error
|
||||
// response and is logged (if a logger is configured) with the stack trace.
|
||||
func Recovery(opts ...RecoveryOption) Middleware {
|
||||
o := &recoveryOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if v := recover(); v != nil {
|
||||
if o.logger != nil {
|
||||
o.logger.LogAttrs(r.Context(), slog.LevelError, "panic recovered",
|
||||
slog.Any("panic", v),
|
||||
slog.String("stack", string(debug.Stack())),
|
||||
slog.String("method", r.Method),
|
||||
slog.String("path", r.URL.Path),
|
||||
slog.String("request_id", RequestIDFromContext(r.Context())),
|
||||
)
|
||||
}
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user