Documentation
¶
Overview ¶
Package http2 provides an opinionated HTTP/2 server wrapper that implements lifecycle.Runner. It ships with built-in /livez, /readyz, and /metrics endpoints, a slog-aware recovery middleware, an opt-in otelhttp instrumentation hook, and a context-safe shutdown contract.
Typical usage:
cfg := http2.Config{Port: "8080"}
srv := http2.NewServer(cfg,
http2.WithOtel(),
http2.WithRecovery(),
http2.WithHealthcheckFrom(app),
)
srv.Mount("/api", apiHandler)
a := app.New()
a.Add(srv)
log.Fatal(a.Run())
Index ¶
- Constants
- Variables
- type Config
- type Option
- func WithHealthcheckFrom(h lifecycle.Healthchecker) Option
- func WithListener(l net.Listener) Option
- func WithLogger(l *slog.Logger) Option
- func WithMetricsHandler(h http.Handler) Option
- func WithMiddleware(mw ...func(http.Handler) http.Handler) Option
- func WithOtel() Option
- func WithReadyzCheck(name string, fn func(ctx context.Context) error) Option
- func WithRecovery() Option
- type Server
Constants ¶
const ( // LivenessPath returns 200 OK unconditionally — a signal that the // process is up and the event loop is scheduling. Does NOT check // dependencies; that is /readyz's job. LivenessPath = "/livez" // ReadinessPath returns 200 OK if every Healthchecker wired via // WithHealthcheckFrom / WithReadyzCheck reports healthy, or 503 with // the error body otherwise. ReadinessPath = "/readyz" // MetricsPath serves the handler passed via WithMetricsHandler, or 404 // if no handler was set. Typical use: pass the promhttp handler that // scrapes the otel PrometheusExporter. MetricsPath = "/metrics" )
Built-in route paths served by every Server.
Variables ¶
var ErrServerAlreadyStarted = errors.New("http2: server already started")
ErrServerAlreadyStarted is returned by Server.Run if the server has already been started. A Server instance is single-use: the underlying http.Server cannot be reused after Shutdown.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Port is the TCP port the listener binds to. Default: "80".
Port string
// ReadTimeout caps the time taken to read the full request including
// body. Default: 1 second. Keep small for non-upload endpoints.
ReadTimeout time.Duration
// WriteTimeout caps the time taken to write the full response body.
// For server-streaming endpoints this MUST be large — a 24h value
// is common for long-lived SSE / server-streaming RPCs. Default: 10s.
WriteTimeout time.Duration
// ShutdownTimeout caps the graceful shutdown phase. If in-flight
// requests do not complete within this window, the server is
// force-closed. Default: 10 seconds.
ShutdownTimeout time.Duration
}
Config holds the server's network and timeout settings. All fields are optional — zero values are replaced with defaults in NewServer.
type Option ¶ added in v1.3.0
type Option func(*Server)
Option configures a new Server.
func WithHealthcheckFrom ¶ added in v1.3.0
func WithHealthcheckFrom(h lifecycle.Healthchecker) Option
WithHealthcheckFrom wires a lifecycle.Healthchecker (typically an *app.App) into the /readyz handler. On every /readyz request, the passed Healthchecker's Healthcheck method is invoked and its result aggregated with any WithReadyzCheck-registered checks.
If no healthchecker is set, /readyz returns 200 OK unconditionally (same as /livez).
func WithListener ¶ added in v1.3.0
WithListener overrides the default TCP listener. Useful for tests (net.Listen on :0) or for exposing the server on a unix socket.
func WithLogger ¶ added in v1.3.0
WithLogger attaches a *slog.Logger to the server. Used by the recovery middleware and for lifecycle events. Defaults to slog.Default() when omitted.
func WithMetricsHandler ¶ added in v1.3.0
WithMetricsHandler registers an http.Handler at MetricsPath. Typical use: pass the promhttp.Handler wrapping an otel Prometheus exporter. Kept as a generic Handler parameter so core/http2 does not depend on prometheus or otel/exporters/prometheus directly.
Without this option, /metrics returns 404.
func WithMiddleware ¶ added in v1.3.0
WithMiddleware appends one or more middleware functions to the chain. The chain is applied outer-first: the first middleware passed is the outermost wrapper (sees the request first, writes the response last).
Built-in middlewares (WithOtel, WithRecovery) are applied AFTER user middlewares so that panics from inside user middleware are still caught and traces span user middleware work.
func WithOtel ¶ added in v1.3.0
func WithOtel() Option
WithOtel enables OpenTelemetry instrumentation by wrapping the final handler with otelhttp.NewHandler. Uses the global TracerProvider and MeterProvider, so otel.Setup must have been called and registered with app.App before NewServer so the providers are non-noop.
WithOtel records one span per request with the route pattern matched by the underlying ServeMux, including HTTP method, status code, and latency.
func WithReadyzCheck ¶ added in v1.3.0
WithReadyzCheck adds a named inline readiness check. Useful for app-level gates that are not tied to a registered Resource/Runner (e.g., "warm cache loaded", "migration finished").
The check function receives the request's context and must respect its deadline. Returning a non-nil error fails the /readyz response.
func WithRecovery ¶ added in v1.3.0
func WithRecovery() Option
WithRecovery installs a panic-recovery middleware that logs the panic with a stack trace (via the configured slog.Logger) and responds with HTTP 500. Without this, a panic in a handler crashes the goroutine and terminates the connection without a response.
Recommended for every production-facing server.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is an HTTP/2-capable http.Server wrapped with middleware chain, built-in /livez, /readyz, /metrics routes, and a context-safe graceful shutdown. Implements lifecycle.Runner.
func NewServer ¶
NewServer constructs a Server with the given Config and options. Zero values in cfg are replaced with defaults (see defaultXxx constants).
The returned *Server implements lifecycle.Runner. Register it with app.App via a.Add(srv); do not call Run directly outside of tests.
func (*Server) Mount ¶
Mount registers handler at pattern on the underlying ServeMux. Patterns follow net/http.ServeMux rules (see https://pkg.go.dev/net/http#ServeMux).
Mount must be called before Run. Calling it concurrently with Run is undefined behavior.
func (*Server) Run ¶
Run starts the HTTP listener and blocks until ctx is cancelled or the server fails. On ctx cancellation, Run invokes Shutdown with a FRESH timeout context (NOT the already-cancelled ctx) so graceful drain has its full budget.
Implements lifecycle.Runner.
Run must be called at most once per Server instance — the underlying http.Server cannot be reused after Shutdown. A second call returns ErrServerAlreadyStarted.