Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/httpserver"
Builds and runs the HTTP server: your routes from a Binder, the operational routes from a built-in set, middleware, and a shutdown path that waits for in-flight work.
srv, err := httpserver.New(ctx, &binder{},
httpserver.WithServerAddr(":8080"),
httpserver.WithEnableDefaultRoutes(httpserver.PingRoute, httpserver.StatusRoute),
httpserver.WithRequestTimeout(30*time.Second),
httpserver.WithShutdownTimeout(10*time.Second),
)
srv.StartServer()
Misconfiguration is an error from New, not a panic
Nil handlers, nil middleware, duplicate routes, malformed routes and unknown default-route identifiers are all reported by New as wrapped sentinel errors.
This is the difference between a bad deployment failing at rollout and failing on the first request that happens to hit the broken route. A typo in a route pattern or a nil handler behind a conditional is caught before the listener is bound, so the process exits and the orchestrator does not route traffic to it.
Wrapped sentinels mean a caller can distinguish the cases with errors.Is rather than matching on message text.
The operational routes
Enabled selectively or all at once:
| Route | Purpose |
|---|---|
/ping | liveness, no dependency checks |
/status | readiness, runs the health checks |
/metrics | metrics payload, 501 until a backend replaces it |
/pprof/*option | profiling |
/ip | the service’s public address |
/ | generated route index |
Two of them behave differently from the rest.
The pprof route has its request timeout disabled, because /pprof/profile and /pprof/trace block for their requested duration by design. A 30-second CPU profile under a 30-second request timeout would be truncated. See profiling.
/metrics answers 501 until a metrics backend is wired, so the route exists from the start and the scrape configuration does not need to change when instrumentation is added.
/pprof, /metrics and /status disclose internals and should not be publicly reachable.
Lifecycle
StartServer does not block: the listener is bound before it returns, so a subsequent readiness check cannot race the socket. HTTPServer.Addr reports the bound address, which is how a test using port 0 discovers the ephemeral port.
Shutdown is driven by context cancellation or a signal channel, bounded by WithShutdownTimeout, and integrates with an external sync.WaitGroup so background workers registered elsewhere are waited for too. HTTPServer.ServeError reports abnormal termination.
The waitgroup integration is what makes shutdown correct rather than merely graceful. Draining in-flight HTTP requests while a periodic task is midway through writing is only half the job.
The timeout lattice
Several timeouts apply at once: read, read-header, write, idle, the per-request timeout, and the shutdown budget. They interact, and two relationships matter most.
The request timeout must exceed anything a handler legitimately does, including the retries a handler performs through httpretrier, or the handler is cut off mid-retry.
The shutdown timeout must exceed the request timeout, or shutdown kills requests that were still inside their own budget.
Routes carry a description
httpserver.Route includes a Description, which feeds the generated index at /. It costs a line per route and produces a self-describing service, which is the documentation most likely to stay current because it lives next to the handler.
Dependencies
Reaches github.com/julienschmidt/httprouter, plus httputil, healthcheck, profiling and ipify.
Links
- Reference documentation
- Source code
- In depth: Before the First Request and After the Last
- Wiring the whole service: bootstrap
- Getting started: /docs/getting-started/
- All nurago packages