server

package
v2.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: MIT Imports: 22 Imported by: 0

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

View Source
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

View Source
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

func MountEchoRoutes(r *rux.Router)

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

func New(debugMode bool) *Server

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) Err

func (s *Server) Err() error

Err returns the last lifecycle error captured. Same value as Run's return.

func (*Server) IsListening added in v2.1.0

func (s *Server) IsListening() bool

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

func (s *Server) ListenAddr() string

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

func (s *Server) ListenPort() int

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

func (s *Server) Listener() net.Listener

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

func (s *Server) LocalURL() string

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

func (s *Server) Run() error

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

func (s *Server) ServeListener(ln net.Listener) error

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) SetAddr

func (s *Server) SetAddr(host string, port uint)

SetAddr is a convenience setter that composes host:port into Addr.

func (*Server) SetHostPort

func (s *Server) SetHostPort(host string, port uint)

SetHostPort is kept for backward compatibility — same as SetAddr.

func (*Server) SetListener added in v2.1.0

func (s *Server) SetListener(ln net.Listener)

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

func (s *Server) Shutdown(ctx context.Context) error

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

func (s *Server) Start() error

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) String

func (s *Server) String() string

String returns the server's listen address for logging.

func (*Server) WaitListening added in v2.1.0

func (s *Server) WaitListening(ctx context.Context) error

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
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL