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>
56 lines
1.4 KiB
Go
56 lines
1.4 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
// ReadinessChecker is a function that reports whether a dependency is ready.
|
|
// Return nil if healthy, or an error describing the problem.
|
|
type ReadinessChecker func() error
|
|
|
|
// HealthHandler returns an http.Handler that exposes liveness and readiness
|
|
// endpoints:
|
|
//
|
|
// - GET /healthz — liveness check, always returns 200 OK
|
|
// - GET /readyz — readiness check, returns 200 if all checkers pass, 503 otherwise
|
|
func HealthHandler(checkers ...ReadinessChecker) http.Handler {
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(healthResponse{Status: "ok"})
|
|
})
|
|
|
|
mux.HandleFunc("GET /readyz", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var errs []string
|
|
for _, check := range checkers {
|
|
if err := check(); err != nil {
|
|
errs = append(errs, err.Error())
|
|
}
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
_ = json.NewEncoder(w).Encode(healthResponse{
|
|
Status: "unavailable",
|
|
Errors: errs,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(healthResponse{Status: "ok"})
|
|
})
|
|
|
|
return mux
|
|
}
|
|
|
|
type healthResponse struct {
|
|
Status string `json:"status"`
|
|
Errors []string `json:"errors,omitempty"`
|
|
}
|