Skip to content

Latest commit

 

History

History
81 lines (62 loc) · 4.17 KB

File metadata and controls

81 lines (62 loc) · 4.17 KB

Architecture

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.

Process

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

Request pipeline

For each parsed request (HTTP/1.1 or HTTP/2):

  1. Rewrite the path (rewrite rules).
  2. Rate limit unless the path is health or metrics.
  3. Health (/healthz), metrics (/metrics), or status (/status) — short-circuit.
  4. Method allow-list for static paths — else 405. Proxy, CGI, and FastCGI skip this.
  5. HTTP Basic if auth_basic_file loaded — else 401.
  6. Reverse proxy, FastCGI, or CGI if the path matches a configured prefix.
  7. Static handler (vhost root from Host), optional file cache, optional SSI on HTML, then gzip / deflate.
  8. Stamp Date / Server, HSTS if TLS, security headers, write bytes (or WebSocket tunnel after 101), access log, metrics.

HTTP/1.1 keep-alive reuses the same connection and repeats from parse. HTTP/2 multiplexes streams on one session.

Static mapping

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.

I/O model

  • getaddrinfo + one listen fd (IPv4 and/or IPv6).
  • accept, enqueue the client fd, worker_threads workers, capped by max_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 Connection interface as plain TCP. ALPN selects h2 or http/1.1.
  • Cleartext HTTP/2 is prior-knowledge only (24-byte preface). There is no HTTP/1.1 Upgrade to h2. WebSocket Upgrade is proxied on HTTP/1.1 when proxy matches.
  • SIGHUP soft-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.

What is intentionally missing

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.

Further reading