httpsd
A pure-Rust HTTP server (HTTP/1.1, HTTP/2, and HTTP/3) with a sans-I/O core and pluggable runtimes.
Use it as a library — drop in your own handler and pick the runtime that fits (blocking thread pool, tokio, or mio) — or as a command-line server that serves a directory or a TOML config file.
- Sans-I/O protocol core.
proto::H1Connturns bytes intoRequests and serializesResponses back into bytes. It owns no socket, so it is trivially testable and reusable across every runtime. - HTTP/1.1, HTTP/2, and HTTP/3. One synchronous
Handlerserves all three. HTTP/2 is negotiated via ALPN on the TLS server; HTTP/3 runs over QUIC/UDP. - TLS & QUIC via purecrypto. HTTPS is built on purecrypto's own sans-I/O TLS 1.2/1.3 engine and HTTP/3 on its QUIC stack — no OpenSSL, no C.
- Compression via compcol. gzip/deflate response compression, plus HPACK (HTTP/2) and QPACK (HTTP/3) header coding.
- Automatic certificates (ACME). On-demand issuance keyed on SNI
(Let's Encrypt or any ACME CA), TLS-ALPN-01 + HTTP-01 challenges, on-disk
persistence and renewal. HTTP is redirected to HTTPS by default; bare-IP
requests are redirected to a resolvable
g-dns.nethost. - No mandatory async runtime. The default build is a blocking thread pool; tokio and mio are opt-in.
Quick start (CLI)
# Serve the current directory over HTTP on 127.0.0.1:8080
# Serve a specific directory on a chosen address
# HTTPS with a real certificate
# HTTPS with an ephemeral self-signed cert (development)
# Also serve HTTP/3 over QUIC/UDP on the same port (requires TLS)
# Automatic HTTPS with Let's Encrypt (issues certs on demand, per SNI)
# Run from a config file (see samples/config.toml)
HTTP/2 is negotiated automatically over HTTPS (ALPN h2); clients that don't
support it fall back to HTTP/1.1. HTTP/3 is served on UDP by default whenever
a TLS certificate is configured (use --no-http3 to disable), and advertised via
Alt-Svc so browsers upgrade after the first request.
HTTP/3 currently requires a static certificate (
--tls-cert/--self-signed). On-demand ACME certificates over HTTP/3 await a QUIC ClientHello/SNI peek inpurecrypto(QUIC carries the same TLS SNI, but inside the encrypted Initial packet, so per-connection selection needs the engine to expose it pre-handshake — the h3 analog of the TCP path'speek_client_hello).
Run httpsd --help for all options.
Quick start (library)
use ;
Serve static files:
use Server;
bind?
.serve_dir
.workers
.run?;
Enable HTTPS:
use ;
let acceptor = from_pem_files?;
bind?
.serve_dir
.tls
.run?;
Choosing a runtime
The same Server drives any compiled-in runtime:
server.run?; // rt-threadpool: blocking accept loop + worker pool
server.run_tokio.await?; // rt-tokio: one async task per connection
server.run_mio?; // rt-mio: single-thread readiness event loop
server.run_h3?; // h3: QUIC/UDP event loop (HTTP/3)
The TCP runtimes serve HTTP/1.1 and (over TLS) HTTP/2; run_h3 serves HTTP/3
on UDP. To offer all three, run a TCP runtime and run_h3 on separate threads
sharing the same TlsAcceptor.
A custom handler is just Fn(&Request) -> Response (or anything implementing
Handler). Because the core is sans-I/O, one synchronous
handler works identically under all three runtimes.
Automatic certificates (ACME)
With --acme-accept-tos (you accept the CA's terms of service) the server
obtains certificates on demand: when a TLS ClientHello arrives for a host it has
no certificate for, it issues one from the ACME CA and persists it, then serves
it. Defaults chosen for safety:
- Challenges: TLS-ALPN-01 (all on 443) is tried first; HTTP-01 is a fallback
when the
--httplistener is running. - Storage:
/var/lib/httpsd(FHS persistent state) → falls back to$XDG_DATA_HOME/httpsdwhen not root. Account key0600, dirs0700, atomic writes. (Never/run— tmpfs would cause re-issuance storms.) - Loopback peers never trigger issuance — they get a self-signed cert.
- Scope:
--host-whitelist a.com,b.comrestricts which hosts may be issued; otherwise any SNI host is attempted. First issuance blocks the handshake (single-flight per host); renewal happens within 30 days of expiry. - HTTPS-first: plain-HTTP requests are
308-redirected to HTTPS unless--allow-http. A request to a bare IP (or with noHost) is redirected tohttps://<base32(ip)>.g-dns.net/…, which resolves back to that IP so the follow-up request carries a real SNI host. - HSTS:
--hsts(optionally--hsts-max-age,--hsts-include-subdomains,--hsts-preload) sendsStrict-Transport-Securityon HTTPS responses only.
Drop --acme-staging for production Let's Encrypt; --acme-directory URL points
at any other ACME CA. ACME currently runs under the thread-pool runtime.
Configuration file
= "0.0.0.0:8080" # or ["127.0.0.1:8080", "[::1]:8080"]
= "./public" # document root for static file serving
= "httpsd"
= 8
[]
= "cert.pem" # PEM chain, leaf first
= "key.pem" # PKCS#8 / PKCS#1 RSA / SEC1 EC
# self_signed = ["localhost"] # alternatively, generate an ephemeral cert
[]
= true
= 256
See samples/config.toml.
Feature flags
| Feature | Default | Description |
|---|---|---|
cli |
✓ | The httpsd binary (implies config + rt-threadpool). |
rt-threadpool |
✓ | Blocking accept loop backed by a worker thread pool. |
tls |
✓ | HTTPS via purecrypto. |
compress |
✓ | gzip/deflate response compression via compcol. |
h2 |
✓ | HTTP/2 over TLS (ALPN); HPACK via compcol. |
h3 |
✓ | HTTP/3 over QUIC/UDP; QPACK via compcol, QUIC via purecrypto. |
acme |
Automatic certificates (ACME); ACME HTTP client via rsurl. |
|
config |
TOML configuration loading (pulled in by cli). |
|
rt-tokio |
Asynchronous tokio runtime. | |
rt-mio |
Single-thread mio event-loop runtime. |
To use httpsd as a lean embeddable library — say, tokio HTTPS without the CLI:
[]
= { = "0.1", = false, = ["rt-tokio", "tls", "compress"] }
Capabilities & limits
- HTTP/1.0 and HTTP/1.1 with persistent connections (keep-alive).
- HTTP/2 (RFC 9113) over TLS: HPACK, stream multiplexing, connection and per-stream flow control, SETTINGS/WINDOW_UPDATE/PING/RST_STREAM/GOAWAY.
- HTTP/3 (RFC 9114) over QUIC: control + QPACK streams, HEADERS/DATA framing, one connection per peer on a single UDP socket.
- Request bodies via
Content-Lengthand chunkedTransfer-Encoding(buffered), with configurable size limits. GET/HEADstatic file serving with MIME detection, directoryindex.html,ETag/Last-Modifiedconditional requests, and single-range (206) support.- Path-traversal protection (rejects
.., canonicalizes against the root). - Response compression negotiated from
Accept-Encoding, skipping already-compressed media types and tiny bodies. - Automatic ACME certificates (RFC 8555) with TLS-ALPN-01 (RFC 8737) and
HTTP-01 challenges, on-disk persistence, and renewal; HTTP→HTTPS and
bare-IP→
g-dns.netredirects.
Verified against curl for HTTP/1.1, --http2, and --http3-only.
Out of scope for this version: streaming request/response bodies, HTTP/2 server push, QUIC connection migration, and async handler traits (handlers are synchronous by design). HTTP/3 demultiplexes connections by peer address.
License
MIT