simple-httpd is one process: a listen socket, a worker pool, a static file mapper, optional extras (proxy / CGI / FastCGI / SSI), and a few guards in front of it.
There is no worker-process model, no shared cache process, and no plugin ABI. Configuration is loaded at start and not reloaded.
src/main.cpp
load INI/YAML/JSON → env → CLI
validate paths
HttpServer::run()
bind / listen (TLS context if cert+key; ALPN if HTTP/2)
accept loop enqueues fds
worker threads → handleClient (HTTP/1.1 or HTTP/2)
simple-httpd_lib holds the implementation. Public headers live under include/simple-httpd/.
| Module | Role |
|---|---|
config |
INI / YAML / JSON, CLI, env, dump, validate |
logger |
Error + access logs |
http/parser |
Request line, headers, Content-Length / chunked bodies |
http/response |
Status, headers, HTML errors |
http2 |
nghttp2 session (h2c and ALPN h2) |
pipeline |
Shared rewrite / rate limit / auth / proxy / CGI / FastCGI / static / SSI / gzip / headers |
static_handler |
Document root, index, MIME, 304, 206, listing |
file_cache |
Small in-memory static bodies (mtime + TTL) |
ssi |
HTML <!--#include --> / <!--#echo --> (no exec) |
proxy |
HTTP reverse proxy, round-robin, WebSocket tunnel |
cgi / fastcgi |
CGI spawn; FastCGI TCP client |
connection / tls |
Byte I/O, OpenSSL accept, ALPN |
compress |
gzip / deflate |
auth |
HTTP Basic |
rate_limit |
Per-IP window |
rewrite |
Prefix from:to |
metrics |
Counters/gauges for /metrics and /status |
server |
Accept loop, worker pool, protocol selection, SIGHUP soft reload |
For each parsed request (HTTP/1.1 or HTTP/2):
- Rewrite the path (
rewriterules). - Rate limit unless the path is health or metrics.
- Health (
/healthz), metrics (/metrics), or status (/status) — short-circuit. - Method allow-list for static paths — else
405. Proxy, CGI, and FastCGI skip this. - HTTP Basic if
auth_basic_fileloaded — else401. - Reverse proxy, FastCGI, or CGI if the path matches a configured prefix.
- Static handler (vhost root from
Host), optional file cache, optional SSI on HTML, then gzip / deflate. - Stamp
Date/Server, HSTS if TLS, security headers, write bytes (or WebSocket tunnel after101), access log, metrics.
HTTP/1.1 keep-alive reuses the same connection and repeats from parse. HTTP/2 multiplexes streams on one session.
Host selects a vhost document root or the default. The URL path is joined under that root, percent-decoded, and rejected if it escapes. Index files fill in directories. Optional HTML listing is generated in-process. MIME comes from a compiled extension table.
getaddrinfo+ one listen fd (IPv4 and/or IPv6).accept, enqueue the client fd,worker_threadsworkers, capped bymax_connections.- Blocking reads/writes with socket timeouts; optional
TCP_NODELAY. - Cleartext static bodies may use
sendfile(Linux/macOS); TLS still copies through userspace. - TLS wraps the same
Connectioninterface as plain TCP. ALPN selectsh2orhttp/1.1. - Cleartext HTTP/2 is prior-knowledge only (24-byte preface). There is no HTTP/1.1
Upgradeto h2. WebSocketUpgradeis proxied on HTTP/1.1 whenproxymatches. SIGHUPsoft-reloads safe config keys; listen/TLS/worker count still need a restart.
This is simple and matches the product (static files, modest concurrency). It is not a replacement for an epoll/kqueue server.
HTTP/3, HTTPS origin proxy, FastCGI unix sockets / process management, SSI exec, a template language beyond SSI, privilege drop / daemonize, SNMP, and a write management API are not in the binary. Reverse proxy, CGI, FastCGI, and SSI exist as opt-in keys; they stay off unless you set them. Diagrams under diagrams/ follow this document.
- configuration.md — keys that feed this pipeline
- security.md — where the guards sit
- operations.md — health, metrics, restart