Open-Source Wikis

/

Caddy

/

Systems

/

Listeners

caddyserver/caddy

Listeners

Caddy's socket layer. Lives mostly in listeners.go (~25 KB), with platform shims in listen.go, listen_unix.go, listen_unix_setopt*.go, listen_reuseUnixSocket*.go.

Purpose

Provide a uniform way for apps to obtain net.Listener and net.PacketConn instances by address string, with:

  • Cross-platform handling (unix, tcp, udp — Linux, macOS, Windows, FreeBSD).
  • Process-level socket sharing via SO_REUSEPORT so reloads can hand off without dropping connections.
  • A pluggable wrapper layer so listeners can be decorated with PROXY-protocol parsing, HTTP→HTTPS redirect, TLS termination, etc.
  • HTTP/3 / QUIC (net.PacketConn) support via quic-go.

Directory layout

Path Role
listeners.go NetworkAddress, ParseNetworkAddress, Listen/ListenPacket, listener wrapper interface
listeners_test.go, listeners_fuzz.go Tests and a fuzz harness for address parsing
listen.go Platform-agnostic Listen helpers
listen_unix.go Linux/macOS/Unix SO_REUSEPORT setup
listen_unix_setopt.go, listen_unix_setopt_freebsd.go OS-specific setsockopt
listen_reuseUnixSocket.go, listen_reuseUnixSocket_windows.go Graceful Unix socket reuse during reload
modules/caddyhttp/proxyprotocol/ caddy.listeners.proxy_protocol listener wrapper
modules/caddyhttp/httpredirectlistener.go caddy.listeners.http_redirect

Key abstractions

Type Where Description
NetworkAddress listeners.go Parsed <network>/<host>:<startPort>-<endPort> (and Unix paths)
Listen(ctx, network, address) listeners.go Returns a net.Listener, internally pooled
ListenQUIC / ListenPacket listeners.go UDP listener for HTTP/3
ListenerWrapper listeners.go Module interface; wraps a base listener (caddy.listeners.*)
fakeCloseListener listeners.go Lets multiple consumers "close" the same shared listener

How it works

graph TD
    Server[Server.Provision] -->|caddy.Listen| Listeners
    Listeners -->|consult cache| Pool[shared listener pool<br/>keyed by network+address]
    Pool -->|reuse| Sock[OS socket]
    Pool -->|miss| New[new listener via SO_REUSEPORT]
    New --> Sock
    Server -->|wraps| Wrappers
    Wrappers --> ProxyProto[proxy_protocol]
    Wrappers --> Redir[http_redirect]
    Wrappers --> TLS[tls wrapper]
    Wrappers --> ServeLoop[serve loop]

Address parsing

ParseNetworkAddress accepts strings like:

  • :8080, 127.0.0.1:8080, [::1]:8080
  • tcp/:8080, tcp4/0.0.0.0:8080
  • udp/:443 (HTTP/3)
  • unix//tmp/caddy.sock
  • :8080-8090 (port range — Caddy will listen on each)

The result is a NetworkAddress that knows its network type, host, and port range. Listen opens the socket; ListenAll spans the port range.

Shared listener pool

Apps that listen on the same address (different Servers in the HTTP app, multiple in-process consumers) get the same listener back, wrapped in a fakeCloseListener whose Close just decrements a refcount. The real socket is closed only when no consumer remains.

This is the basis for graceful reload: when a new config is loaded, both old and new servers can hold the same listener for a moment. Existing connections drain on the old server while new connections go to the new one. Combined with SO_REUSEPORT (where available), even cross-process reloads keep the port bound.

Listener wrappers

Server.ListenerWrappersRaw is loaded as a list of modules in the caddy.listeners.* namespace. Each implements:

type ListenerWrapper interface {
    WrapListener(net.Listener) net.Listener
}

Bundled wrappers:

  • caddy.listeners.proxy_protocol (modules/caddyhttp/proxyprotocol/) — strips PROXY-protocol headers, exposes the real client IP.
  • caddy.listeners.http_redirect (modules/caddyhttp/httpredirectlistener.go) — peeks at the first bytes; redirects plaintext HTTP traffic on a TLS port.
  • caddy.listeners.tls (registered by modules/caddyhttp/caddyhttp.go's tlsPlaceholderWrapper) — placeholder slot for TLS termination (the actual TLS listener is wired by the HTTP app, but this exists so wrappers below it know their position).

HTTP/3 / QUIC

listeners.go exposes ListenQUIC which produces a quic.EarlyListener from quic-go. The HTTP app uses this when a server's experimental_http3 flag is set or HTTP/3 is auto-enabled on a TLS-bound port. QLog can be wired through h3qlog (also imported in listeners.go).

Rate limiting on accept

listeners.go includes a rate.Limiter-backed mechanism to slow down accept loops under attack (the golang.org/x/time/rate import). This is used to keep accept-loops from hard-spinning when the kernel rejects connections faster than userspace can re-arm.

Integration points

  • HTTP app: every Server calls caddy.Listen for each address in listen. The HTTP app also wires Server.AllowH2C, HTTP/3, and TLS connection policies on top.
  • Listener wrapper modules: any module under caddy.listeners.* becomes available wherever listener_wrappers is configured.
  • Reload: caddy.Load keeps the pool alive across configs.

Entry points for modification

  • Add a listener wrapper? Implement caddy.ListenerWrapper, register caddy.listeners.<name>. Look at modules/caddyhttp/proxyprotocol/ for a small example.
  • Tune accept-rate limiting? Edit listeners.go. The limiter is intentionally generous; tightening it without measurement is risky.
  • Add a new transport network? Extend ParseNetworkAddress and the dial/listen helpers.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Listeners – Caddy wiki | Factory