Server configuration¶
HttpServerConfig is the value object passed to SingleHttpServer or MultiHttpServer. Build it before construction and keep it in application-owned code; listener binding, thread count, and protocol availability are construction-time choices. Every with...() function returns the configuration object, so settings chain naturally.
using namespace std::chrono_literals;
aeronet::HttpServerConfig config;
config.withPort(8080)
.withKeepAliveTimeout(15s)
.withHeaderReadTimeout(10s)
.withBodyReadTimeout(30s)
.withMaxHeaderBytes(16 * 1024)
.withMaxBodyBytes(32ULL * 1024 * 1024)
.withMaxOutboundBufferBytes(8ULL * 1024 * 1024)
.withMaxZerocopyPendingBytes(8ULL * 1024 * 1024)
.withTcpNoDelayMode(aeronet::TcpNoDelayMode::Auto);
HttpServer is also named MultiHttpServer. SingleHttpServer always has one event loop and rejects nbThreads > 1; a multi-server uses SO_REUSEPORT internally when it starts more than one worker. Use port = 0 during tests or when the OS should choose a port, then read server.port() after construction.
Listener, lifecycle, and parser limits¶
| Setting | Default | Purpose |
|---|---|---|
nbThreads / withNbThreads() |
0 | Multi-server worker count; 0 selects hardware concurrency (one for SingleHttpServer). |
port / withPort() |
0 | TCP listener port; 0 selects an ephemeral port. |
reusePort / withReusePort() |
false | Request SO_REUSEPORT for independent listeners. |
tcpNoDelay / withTcpNoDelay[Mode]() |
Auto | Nagle policy. Auto is the normal low-latency choice. |
enableKeepAlive / withKeepAliveMode() |
true | Allow HTTP/1.1 persistent connections. |
keepAliveTimeout / withKeepAliveTimeout() |
5 s | Idle time after a response before a persistent connection is closed. WebSocket connections have their own idle timeout. Also bounds the resolution of a CONNECT target (504 beyond). |
maxRequestsPerConnection |
100,000 | Bound requests served on a persistent connection. |
maxCachedConnections |
10 | Reusable closed ConnectionState objects retained to reduce allocations. |
maxAcceptBatchSize / withMaxAcceptBatchSize() |
64 | New accepts handled per event-loop iteration; 0 is unlimited. |
maxHeaderBytes / withMaxHeaderBytes() |
8 KiB | Maximum request line, headers, and terminating CRLFCRLF. Also bounds each chunk-size line and the trailers of a chunked request body. |
maxBodyBytes / withMaxBodyBytes() |
256 MiB | Decoded request body ceiling. |
headerReadTimeout / withHeaderReadTimeout() |
disabled | Deadline to receive a whole HTTP/1.1 request head: from the accept for the first request of a connection (TLS handshake included), from its first byte for the next ones. Slowloris protection; HTTP/2, WebSocket and tunnel traffic are not concerned. |
bodyReadTimeout / withBodyReadTimeout() |
disabled | Progress-based deadline while a handler waits for body data. |
lingeringCloseTimeout / withLingeringCloseTimeout() |
5 s | Maximum duration of the lingering close of an HTTP/1.1 connection closed while its client may still be sending; 0 closes it right away. |
mergeUnknownRequestHeaders / withMergeUnknownRequestHeaders() |
true | Comma-merge repeated unknown headers; use false for stricter custom-header semantics. |
traceMethodPolicy / withTracePolicy() |
Disabled | Disabled, EnabledPlainAndTLS, or EnabledPlainOnly. |
addTrailerHeader / withTrailerHeader() |
true | Add HTTP/1.1 Trailer listing for buffered responses with trailers. Streaming response headers are already sent before trailer names are known. |
Event-loop, buffering, and performance controls¶
| Setting | Default | Purpose |
|---|---|---|
maxOutboundBufferBytes / withMaxOutboundBufferBytes() |
4 MiB | Per-connection retained-output cap. HTTP/1.1 drains then closes on overflow; HTTP/2 pauses wire-input processing at the queue high-water mark and rejects work that cannot fit its queued plus flow-control-deferred budget. A CONNECT tunnel stops reading one side while this many bytes wait for the other, and HTTP/2 reads files ahead of the socket up to half of it. |
maxZerocopyPendingBytes / withMaxZerocopyPendingBytes() |
4 MiB | Response bytes a connection may retain until the completion of their Linux MSG_ZEROCOPY sends, each retained buffer counting for at least zerocopyMinBytes. Writes beyond it are copied until completions release retained buffers; a single larger response may use zerocopy when nothing is retained. See MSG_ZEROCOPY. |
minCapturedBodySize / withMinCapturedBodySize() |
1 KiB | Fixed response bodies below this size are kept with the response head for locality. |
pollInterval / withPollInterval() |
500 ms | Maximum idle event-loop wait. Lower values improve stop responsiveness but increase wakeups. |
pollIntervalMinFactor, pollIntervalMaxFactor / withPollIntervalFactors() |
1.0, 1.0 | Adaptive poll bounds relative to pollInterval; 0.0, 2.0 permits a saturated spin and idle backoff. |
minReadChunkBytes / withMinReadChunkBytes() |
4 KiB | Initial and smallest read size of a connection: doubled after each read filling it (up to 256 KiB, kMaxReadChunkBytes), halved after one filling less than a quarter of it. |
maxPerEventReadBytes / withMaxPerEventReadBytes() |
128 KiB | Fairness budget per connection event: bytes read plus bytes written while serving them. Raise for few large uploads or downloads; lower for latency across many connections. Each read is at most the read size of the connection (see minReadChunkBytes); std::numeric_limits<uint32_t>::max() approximates no limit. |
zerocopyMode / withZerocopyMode() |
Opportunistic | MSG_ZEROCOPY policy for the large response writes of plaintext HTTP/1.1 connections; Linux-specific. Opportunistic skips loopback peers and stops on a connection once the kernel reports copied sends; Enabled uses it for every connection. See MSG_ZEROCOPY. |
zerocopyMinBytes / withZerocopyMinBytes() |
128 KiB | Minimum write size for MSG_ZEROCOPY (cannot be 0). |
globalHeaders / withGlobalHeaders() / addGlobalHeader() |
server: aeronet |
Headers added unless the response already supplies that header. Maximum 256 entries. The reserved headers and location are rejected by validate(). |
HTTPS redirects and CONNECT¶
withHttpsRedirect(port, status) makes a plaintext listener redirect every request to the equivalent https:// URL. port = 0 disables it; 443 is omitted from the generated URL. Allowed statuses are 301, 302, 307, and 308. This is deliberately incompatible with TLS on the same listener. See HTTP to HTTPS redirect.
aeronet::HttpServerConfig redirect;
redirect.withPort(80).withHttpsRedirect(443, aeronet::http::StatusCodePermanentRedirect);
aeronet::HttpServerConfig https;
https.withPort(443).withTlsCertKey("/run/tls/tls.crt", "/run/tls/tls.key");
HTTP CONNECT tunnelling is fail-closed: its allowlist is empty by default. Set exact hostnames/IP strings in the connectAllowlist field, or with withConnectAllowlist(first, last). The special single entry "*" grants access to all hosts and ports and should be reserved for a deliberately access-controlled proxy. See CONNECT tunneling.
TLS configuration¶
TLS requires AERONET_ENABLE_OPENSSL=ON. The convenience server methods enable TLS as needed; advanced fields are on config.tls.
TLSConfig setting |
Default | Use |
|---|---|---|
enabled |
false | Master switch, enabled by certificate/key convenience methods. |
withCertFile() / withKeyFile() |
empty | PEM certificate chain (the leaf certificate, then its intermediate certificates, all sent to clients) and private-key paths. |
withCertPem() / withKeyPem() |
empty | In-memory PEM credentials, same format. Exclusive with the file settings: validate() rejects a configuration setting both. Encrypted private keys are rejected. |
withTlsMinVersion() / withTlsMaxVersion() |
TLS 1.2 / unset | Protocol bounds; accepted server strings are TLS1.2 and TLS1.3. TLS 1.2 is enforced when the minimum is not explicitly set. A maximum below the minimum is rejected. |
withCipherList() / withTlsCipherPolicy() |
empty / Default | OpenSSL TLS 1.2 ciphers or a Default, Modern, Compatibility, or Legacy policy. A cipher list requires the Default policy. |
withTlsAlpnProtocols() |
empty | Ordered ALPN preference list. Use { "h2", "http/1.1" } for TLS HTTP/2; "h2" is rejected when HTTP/2 is disabled. |
alpnMustMatch |
false | Fail a handshake with no ALPN overlap, including a client offering no protocol at all. |
requestClientCert, requireClientCert |
false | Request a certificate, or require and verify one for strict mTLS. Requiring implies requesting. |
withTlsTrustedClientCert() |
empty | Add a PEM trust anchor/leaf (or a bundle of them) for client-certificate validation. Their names are sent in the certificate request. |
handshakeTimeout |
10 s | Deadline from accept to TLS handshake completion; 0 disables it. |
maxConcurrentHandshakes |
0 | Concurrent handshake cap; 0 is unlimited. |
withTlsHandshakeRateLimit() |
0, 0 | Per-second handshake limit and burst cap. |
logHandshake |
false | Log negotiated ALPN, cipher, version, and peer subject. |
disableCompression |
true | Keep TLS-level compression off to mitigate CRIME. |
ktlsMode |
Opportunistic | Disabled, Opportunistic, Enabled (warn if inactive), or Required; Linux/OpenSSL capability dependent. |
sessionTickets |
disabled, 2 keys, 24 h | Enable ticket rotation, choose lifetime/key slots, or supply static 48-byte keys. Sessions only resume with tickets, accepted for their lifetime. |
withTlsSniCertificateFiles/Memory() |
none | Add exact or wildcard SNI certificate mappings. A pattern can only be mapped once. |
withTlsOcspStapleFile() |
none | Load one successful DER OCSP response into the context cache for the default certificate. No handshake-time fetch is performed. |
TLSConfig::withTlsSniOcspStapleFile() |
none | Associate a DER OCSP response with an existing exact or wildcard SNI certificate mapping. |
withTlsCrlFile(path, checkAll) |
none, false | Load a PEM/DER CRL for inbound client-certificate verification; checkAll checks the full verified chain. |
withTlsRevocationCallback() |
none | Apply an application revocation decision after normal verification for each inbound client certificate. |
withTlsKeyLogFile() |
none | Append NSS-compatible traffic secrets in debug builds only. Release builds reject this setting. |
CRL and callback settings require requestClientCert or requireClientCert. OCSP inputs are passive operator-managed caches: refresh them before expiry by replacing the file and posting a config update with postConfigUpdate(), which reloads the TLS files changed on disk even at an unchanged path. See TLS for the full lifecycle and security model and production patterns for a hardened listener profile.
HTTP/2 configuration¶
HTTP/2 requires AERONET_ENABLE_HTTP2=ON; Http2Config::enable defaults to true. TLS uses ALPN, while cleartext accepts prior-knowledge h2c when enableH2c is set. Upgrade: h2c requests are always answered over HTTP/1.1 (RFC 9113 ยง3.1 deprecated that mechanism). See HTTP/2 for the protocol behavior and tuning guidance.
| Setting | Default | Meaning |
|---|---|---|
enable |
true | Global HTTP/2 switch. |
enablePush |
false | Deprecated, no effect: server push is not supported and SETTINGS_ENABLE_PUSH is always advertised as 0. Kept so existing code and configuration files still compile and load. |
enableH2c |
true | Accept prior-knowledge cleartext HTTP/2 (client sends the connection preface directly). |
enableH2cUpgrade |
false | Deprecated, no effect: kept so existing code and configuration files still compile and load. |
enablePriority, maxPriorityTreeDepth |
true, 256 | PRIORITY processing and dependency-tree resource cap. |
mergeUnknownRequestHeaders |
true | Comma-merge unknown duplicate headers for HTTP/2. |
headerTableSize |
4096 | SETTINGS_HEADER_TABLE_SIZE, the limit of both HPACK dynamic tables of a connection: a dynamic table size update of the peer above it is a COMPRESSION_ERROR, and the encoder keeps at most the smaller of it and the value advertised by the peer. A value below 4096 applies to the decoder once the peer acknowledges the SETTINGS. At most 65536. |
maxConcurrentStreams |
100 | SETTINGS_MAX_CONCURRENT_STREAMS. |
initialWindowSize |
1 MiB | Per-stream receive window (SETTINGS_INITIAL_WINDOW_SIZE). The RFC 9113 default of 65,535 bytes caps a transfer on one stream to about 64 KiB per round trip. |
connectionWindowSize |
1 MiB | Connection-level receive window. |
maxFrameSize |
16,384 | Frame payload limit; valid range 16,384 through 16,777,215. |
maxHeaderListSize |
8192 | Decoded header-list ceiling, advertised to the peer and enforced: a larger header block resets its stream with ENHANCE_YOUR_CALM, and its fields beyond the limit are not stored while decoded. |
settingsTimeout |
5 s | Deadline for the acknowledgement of the server SETTINGS, after which the connection is closed with SETTINGS_TIMEOUT; 0 disables it. |
pingInterval, pingTimeout |
disabled, 10 s | Optional PING keepalive: a connection with open streams but no activity for pingInterval is pinged, and closed without acknowledgement within pingTimeout. Idle connections are not pinged. |
maxStreamsPerConnection |
1000000 | Streams of a connection: the last one sends a graceful GOAWAY, and the connection is closed once its streams are answered. Also the stream budget of an HttpClient connection. |
sendContentLengthHeader |
true | Add content-length to responses built with HttpRequestView::makeResponse(). Optional in HTTP/2; disabling it avoids moving the body when its length changes digit count. |
maxStreamPendingBytes |
4 MiB | Per-stream cap for in-memory response body and trailer bytes waiting on peer flow-control credit. Oversized fixed responses receive 503; overflowing streaming responses are reset with ENHANCE_YOUR_CALM. |
maxRecycledRequests |
8 | Server only, at most 255: request objects a connection keeps from its finished streams, reused by the next ones with the capacity of their header maps and buffers. Only connections receiving that many request bodies (or running that many suspended async handlers) at once need more; each one kept costs idle memory. 0 disables the recycling. |
Compression, decompression, and files¶
| Configuration | Defaults and decisions |
|---|---|
CompressionConfig |
minBytes = 1024, maxCompressRatio = 0.6, 32 KiB initial buffer, Vary: Accept-Encoding enabled, and automatic direct-compression mode. Choose preferredFormats, per-codec levels, and a content-type allowlist when CPU use needs controlling. Only compiled codecs can be negotiated. See Compression. |
DecompressionConfig |
Enabled by default when a codec is compiled in; enable = false passes encoded bodies to handlers unchanged. Decodes the compiled codings with a 128 MiB compressed cap, a 4 GiB decoded cap, a 1000x expansion-ratio cap (both caps must be positive), 32 KiB decoder chunks, and a 16 MiB streaming threshold. See Compression. |
StaticFileConfig |
The file handler defaults to ranges and conditional requests enabled, 16 ranges, a 32 MiB assembled multipart-range cap, 1024 header-cache entries, strong ETags and Last-Modified, 128 KiB inline files, and no directory index/dotfiles. It also accepts custom MIME and directory-renderer callbacks. |
The static-file settings are supplied when creating the static handler, not directly on HttpServerConfig. Read Static files for behavior, examples, and every setting.
Probes, logging, telemetry, and router policy¶
| Configuration | Defaults and behavior |
|---|---|
BuiltinProbesConfig (guide) |
Disabled; paths /livez, /readyz, /startupz. dedicatedPort = 0 serves probes on the app listener. A nonzero dedicated port starts an isolated probe loop for MultiHttpServer; livenessStaleThreshold defaults to 10 seconds and must be positive then. |
AccessLogConfig |
Sink None, CLF format, no forwarded-for trust. Select Stdout or File, set filePath for the file sink, and set useForwardedFor only behind a trusted proxy that sanitizes the header. |
TelemetryConfig |
OTLP and DogStatsD both disabled; sample rate 1.0, export interval 10 seconds, timeout 5 seconds. Set endpoint/service name, optional exporter headers, DogStatsD Unix socket/tags/namespace, and explicit finite increasing histogram buckets. OTLP needs the build feature; DogStatsD Unix sockets are Linux-only. |
RouterConfig |
Trailing slash policy is Normalize; set Strict or Redirect as required. A route-level CORS policy wins over withDefaultCorsPolicy(). |
For serializable properties and their JSON/YAML names, build aeronet-config-dump with Glaze and use its emitted output as the baseline. See Kubernetes for a ConfigMap example.