Overview

The reactor

Why one thread#

No locks, no cross-thread state, no thread-pool sizing to tune. The reactor multiplexes connections with epoll and hands each one its own coroutine; concurrency comes from I/O interleaving, not from parallel execution. For an I/O-bound server — which almost every HTTP server is — this trades away multi-core throughput for a dramatically simpler concurrency model with no data races by construction.

Three protocols, one port, one handler#

The headline capability: TLS/TCP with ALPN dispatch (h2 or http/1.1) and QUIC/UDP on the same port number, from one handler, on one thread — with Alt-Svc: h3=":<port>" set on every TCP response so browsers discover the HTTP/3 side on their own.

import serve, serve/reactorall
 
proc handler(req: Request): Response {.nimcall.} =
  response(200, "text/plain", "ok " & req.path & "\n")
 
serveAllReactor(8443, "cert.pem", "key.pem", handler)
bin/reactor_all 8443 cert.pem key.pem &
curl -k --http1.1 https://127.0.0.1:8443/hi
curl -k --http2   https://127.0.0.1:8443/hi
curl -k --http3   https://127.0.0.1:8443/hi   # curl built with HTTP/3

The QUIC path needs the glue shim on the loader path (quic/build.sh in the repo).

Individual protocol entry points#

When you don't need all three protocols behind one handler, call the specific entry point instead:

Shutdown and TLS#

Idle timeouts and graceful SIGINT/SIGTERM shutdown ship with the reactor — a kill on the process drains in-flight connections instead of dropping them. TLS entry points take a TlsContext you build yourself, so cipher suites, protocol versions, post-quantum groups and SNI certificates stay fully reachable — the reactor doesn't hide TLS configuration behind a simplified flag.

See Security for how to build that context.