Documentation
¶
Overview ¶
Package server provides a production-ready HTTP server wrapping a rux.Router.
The Server adds sensible timeouts, graceful shutdown, lifecycle hooks, and liveness/readiness endpoints on top of the bare router. Defaults are tuned for containerized deployments; override any field before calling Run.
Index ¶
- Constants
- Variables
- func MountEchoRoutes(r *rux.Router)
- type Server
- func (s *Server) Err() error
- func (s *Server) IsListening() bool
- func (s *Server) ListenAddr() string
- func (s *Server) ListenPort() int
- func (s *Server) Listener() net.Listener
- func (s *Server) LocalURL() string
- func (s *Server) MountHealthChecks()
- func (s *Server) Run() error
- func (s *Server) ServeListener(ln net.Listener) error
- func (s *Server) SetAddr(host string, port uint)
- func (s *Server) SetHostPort(host string, port uint)
- func (s *Server) SetListener(ln net.Listener)
- func (s *Server) Shutdown(ctx context.Context) error
- func (s *Server) Start() error
- func (s *Server) Stop()
- func (s *Server) String() string
- func (s *Server) WaitListening(ctx context.Context) error
Constants ¶
const ( DefaultAddr = ":8080" DefaultReadHeaderTimeout = 2 * time.Second DefaultReadTimeout = 10 * time.Second DefaultWriteTimeout = 30 * time.Second DefaultIdleTimeout = 120 * time.Second DefaultMaxHeaderBytes = 1 << 20 // 1 MiB DefaultShutdownTimeout = 25 * time.Second DefaultDrainDelay = 5 * time.Second )
Default configuration values applied by New. All durations are conservative enough to defend against common slow-attacks while remaining friendly to typical request/response cycles.
Variables ¶
var ErrNotListening = errors.New("rux: server is not listening")
ErrNotListening is returned by WaitListening when there is no listener to wait for: startup aborted before binding, or a run already came and went, so the address can never become final in this run.
Functions ¶
func MountEchoRoutes ¶
MountEchoRoutes attaches all httpbin-style endpoints to an existing router. Use this when embedding echo functionality into a larger app (e.g. as a debug helper under /debug prefix via Router.Group).
Types ¶
type Server ¶
type Server struct {
*rux.Router
// Addr is the listen address ("host:port" or ":port"). Defaults to ":8080".
Addr string
// Host is kept for backward compatibility; New does not populate it.
// SetHostPort writes both Host/Port and Addr.
Host string
Port uint
// Optional TLS. If both files are set, Start uses ListenAndServeTLS.
TLSCertFile string
TLSKeyFile string
// HTTP server timeouts. Zero means "use net/http default" (NOT recommended).
ReadHeaderTimeout time.Duration // default: 2s (slowloris defense)
ReadTimeout time.Duration // default: 10s
WriteTimeout time.Duration // default: 30s
IdleTimeout time.Duration // default: 120s
MaxHeaderBytes int // default: 1 << 20 (1 MiB)
// ShutdownTimeout bounds how long Shutdown waits for in-flight requests.
ShutdownTimeout time.Duration // default: 25s
// DrainDelay is how long after receiving a stop signal Run keeps serving
// before calling Shutdown. During drain, /readyz reports 503 so the
// upstream LB can drain traffic. Set to 0 to skip drain.
DrainDelay time.Duration // default: 5s
// StopSignals received by Run trigger graceful shutdown.
StopSignals []os.Signal // default: SIGINT, SIGTERM
// Lifecycle hooks. Hooks of the same kind run in slice order.
// PreStart errors abort startup; other hook errors are logged.
PreStart []func(ctx context.Context) error
PostStart []func(ctx context.Context) error
PreShutdown []func(ctx context.Context) error
PostShutdown []func(ctx context.Context) error
// ReadyChecks evaluate /readyz. /readyz returns 503 if any returns error
// OR if the server is draining. Liveness (/healthz) is independent — it's
// 200 as long as the process is alive. Use MountHealthChecks() to attach.
ReadyChecks []func(ctx context.Context) error
// Logger receives lifecycle and error messages. Defaults to log.Printf.
Logger func(format string, args ...any)
// contains filtered or unexported fields
}
Server is a production-ready HTTP server wrapping a rux.Router. Defaults are sane for containerized deployments; override fields before Run().
func New ¶
New constructs a Server with sane defaults plus PanicsHandler middleware. When debugMode is true, also installs RequestLogger and enables rux.Debug.
func NewEchoServer ¶
func NewEchoServer() *Server
NewEchoServer builds a Server with httpbin-style echo endpoints pre-mounted. Use Server.Run() to start with graceful shutdown / lifecycle hooks.
func (*Server) IsListening ¶ added in v2.1.0
IsListening reports whether a listener is currently bound and serving. It is false before Run/Start binds and again once Shutdown completes.
func (*Server) ListenAddr ¶ added in v2.1.0
ListenAddr returns the address the server listens on.
Before the listener is bound it is the configured Addr (which may still carry port 0, i.e. "127.0.0.1:0"). Once bound, Start reflects the resolved listener address back here, so an OS-assigned port shows up as a real one ("127.0.0.1:50447", or "[::]:50447" / "0.0.0.0:50447" for wildcard binds). Use IsListening/WaitListening to know when the value is final, and LocalURL when you need a URL that is usable in a browser.
Safe to call from any goroutine (unlike reading the Addr field directly).
func (*Server) ListenPort ¶ added in v2.1.0
ListenPort returns the resolved TCP port of the listener. It returns 0 when the listener is not bound yet (including the ":0" request for an OS-assigned port) or when Addr carries no port.
func (*Server) Listener ¶ added in v2.1.0
Listener returns the listener the server is currently serving on, or nil when it is not listening.
func (*Server) LocalURL ¶ added in v2.1.0
LocalURL returns a loopback URL for the bound listener, e.g. "http://127.0.0.1:50447". Wildcard hosts ("", "0.0.0.0", "::") are mapped onto 127.0.0.1 so the result can be handed straight to a browser or an open-in-browser flag. The scheme is "https" when TLS is configured.
It returns "" while the port is not usable yet (no listener, or Addr still asking for port 0), which lets callers wait on WaitListening instead of opening a bogus URL.
func (*Server) MountHealthChecks ¶
func (s *Server) MountHealthChecks()
MountHealthChecks attaches GET /healthz (liveness) and GET /readyz (readiness) to the router. Call this before Run() — the router freezes on first request.
/healthz: always 200 "ok" if the process is alive. /readyz: 200 if ready && !draining && all ReadyChecks pass; else 503 with details.
func (*Server) Run ¶
Run is the one-shot lifecycle: PreStart → Start (background) → wait for the listener to bind → PostStart → wait for stop signal → PreShutdown → drain → Shutdown → PostShutdown.
The listener is bound before PostStart hooks run, so hooks (and anyone using WaitListening) observe the resolved address. When Addr requests an OS-assigned port ("host:0"), ListenAddr/ListenPort/LocalURL report the port the kernel picked.
Returns:
- the first PreStart error (startup aborted before listening), OR
- the Start error (if Start returned anything other than http.ErrServerClosed), OR
- the Shutdown error, OR
- nil on clean shutdown.
Hook errors after PreStart are logged but do not affect the return value.
func (*Server) ServeListener ¶ added in v2.1.0
ServeListener serves on an already-bound listener in the current goroutine, using the configured timeouts, TLS files, address reflection and lifecycle signals. Like Start it runs no hooks; use SetListener + Run for the full lifecycle.
Use it when the caller owns the socket: socket activation (systemd LISTEN_FDS), a pre-bound port handed over by a test, or a Unix socket.
func (*Server) SetHostPort ¶
SetHostPort is kept for backward compatibility — same as SetAddr.
func (*Server) SetListener ¶ added in v2.1.0
SetListener makes the next Start/Run serve on an already-bound listener instead of binding Addr. The listener's address is reflected into Addr/Host/Port exactly like a self-bound one.
A listener is good for one run: the underlying http.Server closes it on shutdown, so calling Run again on the same Server needs a fresh listener.
func (*Server) Shutdown ¶
Shutdown gracefully stops the server, bounded by ShutdownTimeout if ctx has no deadline. Idempotent. Does NOT call any hooks; use Run() for full lifecycle.
func (*Server) Start ¶
Start begins serving in the current goroutine. Blocks until the underlying http.Server.Serve returns (typically only after Shutdown).
The listener is bound explicitly before serving, so when Addr asks for an OS-assigned port ("host:0") the resolved address is reflected back into Addr/Host/Port and reported by ListenAddr/ListenPort/LocalURL. As soon as binding succeeds, waiting WaitListening callers (and Run) are woken with the final address instead of polling for it.
If SetListener was called, the supplied listener is served instead of binding Addr.
Returns http.ErrServerClosed on clean shutdown, or another error on failure.
Note: Start does NOT call any hooks. Use Run() for the full lifecycle.
func (*Server) Stop ¶
func (s *Server) Stop()
Stop requests a graceful shutdown from a running Run() loop. Safe to call from any goroutine and any number of times. Useful when the caller wants to trigger shutdown without sending an OS signal (tests, programmatic stop).
func (*Server) WaitListening ¶ added in v2.1.0
WaitListening blocks until the listener is bound and its address is final (the port-0 / `--open` case), or until ctx is done. It returns:
- nil once the server is listening,
- the error that aborted startup, if the run failed before binding,
- ErrNotListening if there is no listener to wait for,
- ctx.Err() otherwise (e.g. context deadline exceeded).
Waiting before Run is safe: it blocks until that run binds. Typical use:
go func() { _ = srv.Run() }()
if err := srv.WaitListening(ctx); err == nil {
openInBrowser(srv.LocalURL()) // e.g. http://127.0.0.1:50447
}