Releases: sjanel/aeronet
Releases · sjanel/aeronet
aeronet v1.0.0
Release Overview
aeronet v1.0.0 marks the production-ready release of a modern, high-performance HTTP/WebSocket server library for Linux. The library has undergone extensive development and testing, achieving enterprise-grade stability and performance.
aeronet is being used in production in good network conditions.
Key Milestones
- ✅ HTTP/1.1 Compliance - 91.6% feature-complete (87/95 features), fully RFC-compliant
- ✅ HTTP/2 Support - RFC 9113 implementation with HPACK, multiplexing, and flow control
- ✅ Enterprise TLS - OpenSSL integration with ALPN, mTLS, session tickets, kernel TLS offload
- ✅ Production Performance - Benchmarked in HTTP/1.1 against major frameworks (Drogon, Pistache, Go, Rust); consistently top or second in RPS and latency depending on scenario
- ✅ Comprehensive Testing - ~94% code coverage at this time, tracked in CI
- ✅ Minimal Technical Debt - Only 7 minor TODOs (optimizations, not functional gaps)
- ✅ Security Hardened - Comprehensive buffer overflow protection, path traversal mitigation, proper input validation
Features
HTTP/1.1 Core
- Request/response parsing with strict RFC 7230 compliance
- Persistent connections with keep-alive and pipelining
- Chunked Transfer-Encoding with trailer header support (RFC 7230)
- Content-Length body handling with configurable size limits
- Streaming responses with backpressure management
- Full HEAD method semantics
- CONNECT tunneling for HTTP proxying
- Expect header handling with 100-continue support
- TRACE method with configurable security policy
- Request header duplicate handling with proper merge strategies
HTTP/2 (RFC 9113)
- HPACK header compression with Huffman encoding
- Stream multiplexing with per-stream and connection-level flow control
- ALPN "h2" negotiation over TLS
- h2c (cleartext prior knowledge) mode
- HTTP/1.1 → HTTP/2 upgrade via Upgrade header
- Unified handler API (same HttpRequest type for both protocols)
- Static file responses with zero-copy sendfile awareness
- CORS and middleware support
- PRIORITY frames (optional, configurable)
⚠️ Note: HTTP/2 CONNECT tunneling not implemented (use HTTP/1.1 instead)
Compression & Content Negotiation
- Accept-Encoding negotiation with q-values and server preference
- gzip, deflate (zlib), zstd, and brotli support (feature-flag gated)
- Inbound request body decompression (symmetric with outbound)
- Per-response compression opt-out
- Automatic Vary header management
- Safe decompression limits with proper 413/415 responses
TLS/HTTPS
- File-based and in-memory PEM certificate loading
- SNI-based multi-certificate routing
- Optional/required mTLS with validation
- Strict ALPN enforcement with negotiation
- Min/Max protocol version constraints (TLS 1.2+)
- Handshake timeout for Slowloris mitigation
- TLS session tickets with automatic key rotation
- Kernel TLS (kTLS) with sendfile - Linux zero-copy offload (opportunistic/required modes)
- Per-server statistics (no global state)
- Hot reload of certificates and trust store
- Comprehensive metrics (handshake duration, failure reasons, ALPN/cipher distributions)
Static File Serving (RFC 7233 / RFC 7232)
- Zero-copy sendfile on plaintext, buffered fallback for TLS
- Single-range request support (RFC 7233)
- Conditional request handling (If-None-Match, If-Match, If-Modified-Since, If-Unmodified-Since, If-Range)
- Strong ETag generation
- Directory listing with optional custom CSS
- HTML escaping and URL encoding for safety
- Path traversal protection (.. segments rejected)
- Configurable default index fallback
WebSocket (RFC 6455)
- Upgrade negotiation and handshake
- Bidirectional communication with frame handling
- Per-message compression support
- Proper closure sequences
Streaming & Chunked Responses
- Incremental body writing via HttpResponseWriter
- Automatic or explicit Content-Length
- Transfer-Encoding: chunked with proper termination
- Trailing headers support
- Backpressure-aware buffering
Performance & Architecture
- Single-thread event loop with epoll
- Horizontal scaling via SO_REUSEPORT
- Multi-instance orchestration (HttpServer wrapper)
- writev scatter-gather for efficient I/O
- Zero-allocation hot paths where possible
- Smart shrink_to_fit for reused connections
- Automated CI benchmarks against major frameworks
Cloud Native & Observability
- Kubernetes-style health probes (/livez, /readyz, /startupz)
- OpenTelemetry integration (metrics, tracing)
- Dogstatsd metrics export
- JSON stats endpoint
- Per-server (no global) telemetry state
- Graceful drain lifecycle (beginDrain/stop)
Routing & Middleware
- Simple exact-match path routing
- Path pattern parameters (e.g., /users/{id}/posts/{post})
- Wildcard terminal segments (e.g., /files/*)
- Global and per-path request/response middleware
- Mixed-mode dispatch (simultaneous fixed and streaming)
- Configurable trailing slash handling (Strict/Normalize/Redirect)
Security Enhancements
Input Validation & Bounds Checking
- ✅ Buffer Overflow Protection - All buffer operations guarded by overflow checks with
std::overflow_error - ✅ Integer Overflow Detection - SafeCast utility and compile-time assertions prevent wrapping
- ✅ Header Size Limits - Configurable
maxHeaderByteswith 431 response - ✅ Body Size Limits - Configurable
maxBodyByteswith 413 response - ✅ Chunk Size Validation - Prevents chunk explosion attacks
- ✅ Outbound Buffer Limits -
maxOutboundBufferBytesprevents unbounded memory growth - ✅ Multipart Form Safety - Limits on part count, headers per part, and individual part size
Path Traversal Protection
- ✅ Canonical Path Resolution - Uses
std::filesystem::weakly_canonicalwith fallback to absolute paths - ✅ Segment Filtering - ".." segments explicitly rejected in static file handler
- ✅ Symlink Status Checks - Uses
symlink_status()to detect and prevent symlink attacks - ✅ Root Confinement - All resolved paths guaranteed under configured root directory
Protocol Security
- ✅ Forbidden Trailer Headers - Authorization, Set-Cookie, Content-* and other unsafe headers rejected
- ✅ Malformed Input Handling - Invalid HTTP parsed as 400 Bad Request
- ✅ Slowloris Mitigation - Header read timeout (configurable, disabled by default)
- ✅ Decompression Limits - Expansion guards and per-stream/connection limits
- ✅ Content-Encoding Validation - Unknown encodings return 415 Unsupported Media Type
- ✅ HTTP/2 Frame Validation - Frame size checks, stream state validation, flow control enforcement
Code Quality & Testing
- ✅ ASAN/UBSAN in CI - Address and undefined behavior sanitizers catch memory issues
- ✅ Comprehensive Testing - 113 tests covering all critical paths
- ✅ Code Coverage - ~94% coverage tracked in CI
- ✅ clang-tidy Enforcement - Static analysis prevents common pitfalls
- ✅ Modern C++23 - RAII-based resource management, no raw pointers in public API
No Known Vulnerabilities
- ✓ No buffer overflows detected
- ✓ No integer overflows possible (checked or prevented at compile time)
- ✓ No path traversal vulnerabilities
- ✓ No decompression bombs possible (expansion limits enforced)
- ✓ No unvalidated state transitions
- ✓ No global mutable state (thread-safety by design)
Known Limitations
The following features are NOT implemented in v1.0.0 (planned for future releases):
- ❌ HTTP/2 CONNECT Tunneling - Use HTTP/1.1 for proxy tunneling; full implementation requires per-stream tunnel state tracking
- ❌ Multi-range Responses - Single ranges only (RFC 7233 multi-range /
multipart/byteranges) - ❌ Server Push - Intentionally disabled (rarely used by modern clients)
- ❌ Pluggable Logging Sinks - Basic logging functional; advanced hooks TBD
- ❌ Deterministic Network Fault Tests - Planned for post-v1.0 robustness improvements
- ❌ OCSP Stapling - Passive stapling and revocation checking planned
Breaking Changes from 0.7.x
None. The public API has remained stable throughout development. Code built against 0.8.x should compile and run unchanged with v1.0.0.
Minor refinements include:
v0.7.0
What's Changed
- Open version 0.7.0 by @sjanel in #181
- chore: clean-up benchmark code by @sjanel in #182
- chore: add some unit tests - tls, dogstats by @sjanel in #183
- feature: make servers copy assignable - toggle reuse port instead of throw for multi threaded MultiHttpServer (if false) by @sjanel in #184
- feat: clarify and change reusePort config bool for MultiHttpServer by @sjanel in #185
- feat: accept threadCount of 0 for MultiHttpServer constructor - hardware concurrency in this case by @sjanel in #186
- chore: raise default max requests per connection to 100k by @sjanel in #187
- feat: multipart from data by @sjanel in #188
- chore: improve config unit tests by @sjanel in #189
- chore: add HttpRequest / HttpResponse fuzz tests by @sjanel in #190
- feat: TLS phase 2 implementation by @sjanel in #191
- chore: factorize trim spaces functions by @sjanel in #193
- feat: websocket protocol support by @sjanel in #194
- feat: add HttpResponse constructor from body only for convenience by @sjanel in #197
- chore: better coverage timestring by @sjanel in #198
- chore: switch to llvm coverage - much better for templated code by @sjanel in #199
- chore: HttpResponse better code coverage by @sjanel in #200
- chore: remove unused flat_hash_map code by @sjanel in #201
- chore: clean-up unused code (ipow, other minor accessors) by @sjanel in #203
- fix: basic exception guarantee fix for ObjectPool when a new allocate… by @sjanel in #202
- chore: better logs for HttpResponseWriter errors by @sjanel in #204
- chore: code clean-up and minor coverage by @sjanel in #205
- chore: tests some allocation failures in internal modules by @sjanel in #206
- chore: minor clean-up in ConnectionState by @sjanel in #207
- feat: wrk benchmarks by @sjanel in #209
- chore: match mime-mappings case insensitive by @sjanel in #212
- chore: tls-info.hpp coverage by @sjanel in #213
- feat: clearer content-type picker algorithm for StaticFileHandler by @sjanel in #214
- feature: init GitHub pages by @sjanel in #215
- chore: fix github pages sync by @sjanel in #216
- chore: fix benchmark renderings pushes (without artifacts) by @sjanel in #217
- chore: fix ci give write permissions to git push by @sjanel in #218
- chore: add benchmarks data to gh pages by @sjanel in #219
- feat: HttpResponse::appendBody by @sjanel in #220
- chore: benchmarks clean-up includes by @sjanel in #221
- chore: fix manual schedule of benchmarks by @sjanel in #222
- chore: do not keep gh-pages branch history, keep only last snapshot by @sjanel in #223
- feat: Rename MultiHttpServer in HttpServer, read nbThreads from config by @sjanel in #224
- feat: add nice error 404 template by @sjanel in #226
- chore: add code coverage to transport by @sjanel in #227
- chore: improve code coverage raw bytes by @sjanel in #228
- chore: remove useless assert by @sjanel in #230
- chore: move some tls tests from tls components to tls config test by @sjanel in #229
- perf: optimize ndigits by @sjanel in #231
- feat: display memory results in the benchmarks by @sjanel in #232
- chore: attempt to clean-up cmake dependencies by @sjanel in #233
- cors: deny preflight when Access-Control-Request-Headers present but empty and no allowed headers by @sjanel in #234
- chore: zlib-decoder: make sure maxDecompressedBytes is respected and simplify code by @sjanel in #235
- chore: remove useless variable in encoders by @sjanel in #236
- fix: max uncompressed bytes for zstd and brotli by @sjanel in #237
- chore: encoder chunk size must be non-zero by @sjanel in #238
- chore: split telemetry config test, add lifecycle test by @sjanel in #239
- chore: refactor around otel-tracer by @sjanel in #240
- chore: clean-up Router set path by @sjanel in #241
- chore: open-telemetry support for gauges by @sjanel in #242
- chore: add more tests event loop failures by @sjanel in #243
- feature: support otel histogram buckets by @sjanel in #244
- chore: rename SmallRawChars into RawChars32, take size uint64_t as parameter by @sjanel in #246
- feat: HttpResponse headers and trailers iterable view by @sjanel in #247
- feature: validate at configuration time the preferred encodings by @sjanel in #248
- feat: readiness probe returns 503 when draining with keep-alive connections by @sjanel in #249
- chore: move Router to http module instead of main by @sjanel in #251
- feature: make HttpResponse::headers() a range by @sjanel in #252
- feature: add epoll errors registration by @sjanel in #253
- feature: tls phase 2.5 by @sjanel in #254
- feature: consider empty content-length and transfer-encoding as Bad Request by @sjanel in #255
- fix: fix kTLS activation and remove AERONET_ENABLE_KTLS compile time flag by @sjanel in #256
- chore: enrich tls-ticket-key-store tests by @sjanel in #257
- fix: PathHandlerEntry async and normal handler construct instead of a… by @sjanel in #258
- chore: factorize encoders tests by @sjanel in #259
- chore: clean-up and optimize routing tests by @sjanel in #260
- chore: ensure TlsContext not moveable for callbacks safety by @sjanel in #261
- benchs: add connection close to mixed scenarios by @sjanel in #263
- feature: connection state pool by @sjanel in #262
- fix: zstd encode chunk for difficult to compress payloads by @sjanel in #264
- chore: remove vector upgrade-handler by @sjanel in #265
- perf: use writev for plain transport of dual buffers by @sjanel in #267
- feature: WebSocket is now a compile time option by @sjanel in #266
- feat: http2 first version by @sjanel in #245
- chore: full testing of multipart-form-data by @sjanel in #268
- chore: test HttpRequest Http2 fields by @sjanel in #269
- chore: move bytes-string to dedicated tech file by @sjanel in #270
Full Changelog: v0.6.0...v0.7.0
v0.6.0
What's Changed
- Open version 0.6.0 by @sjanel in #124
- chore: warnings in top level project only by @sjanel in #125
- chore: enforce crlf in headers parsing by @sjanel in #126
- feat: place all aeronet headers into aeronet dir for easier consume by @sjanel in #127
- feat: possibility to set a global signal handler by @sjanel in #129
- feat: add convenient async start methods to HttpServer, and blocking run methods to MultiHttpServer by @sjanel in #132
- feat: reject HTTP 408 when header time out by @sjanel in #133
- bugfix: do not erase original content type for automatic response compression by @sjanel in #135
- perf: optimize tolower, string equal ignore case, measured around -30 % CPU by @sjanel in #136
- chore: do not export AERONET version str with cmake PROJECT_VERSION by @sjanel in #137
- chore: improve compilation by splitting RawChars implementation by @sjanel in #138
- perf: optimize HttpResponse finalize by @sjanel in #139
- feat: do not emit Connection header when its value is the default by @sjanel in #140
- feat: add option TCP no delay by @sjanel in #141
- Feat/update flat hash map by @sjanel in #142
- feat: DogStatsD support even without OpenTelemetry by @sjanel in #145
- chore: Router::setPath now takes paths as std::string_view instead of std::string by @sjanel in #148
- feat: improve HttpResponse compression performance by @sjanel in #151
- chore: log method and path for router path overwrite warning by @sjanel in #152
- feat: parse HTTP methods from requests as case insensitive by @sjanel in #154
- chore: refactor http methods function names by @sjanel in #155
- chore: do not emit info logs per server in MultiHttpServer by @sjanel in #156
- feat: support HttpResponse append header value with separator by @sjanel in #157
- feat: add coverage by @sjanel in #158
- chore: update logo by @sjanel in #159
- chore: better coverage HttpPayload by @sjanel in #160
- chore: coverage RawChars, EventLoop by @sjanel in #161
- chore: prefix all custom cmake functions with Aeronet by @sjanel in #162
- chore: export brotli and zstd interfaces for simpler client usage by @sjanel in #163
- chore: send standard reason phrases for HTTP errors by @sjanel in #164
- feat: initiate otel e2e tests by @sjanel in #165
- bugfix: correctly return HTTP 400 for invalid header key (with spaces) by @sjanel in #166
- chore: better tests for static file handler by @sjanel in #167
- chore: add examples test in ci by @sjanel in #168
- chore: more tests for sys by @sjanel in #169
- feat: streaming decompression phase 1 by @sjanel in #170
- feat: async streaming inbound request by @sjanel in #173
- feat: simplify MultiHttpServer lifecycle by @sjanel in #175
- chore: enhance RawBytes and flat_hash_map tests by @sjanel in #176
- feat: HttpServer becomes now copy-constructible from idle instance by @sjanel in #177
- feat: enforce docs code chunks up to date thanks to CI job compilation check by @sjanel in #178
- fix: when automatic decompression occurs, replace Content-Length value to decompressed size in HttpRequest by @sjanel in #179
- fix: preserve HttpRequest trailers when two (or more) steps body inflate occur by @sjanel in #180
Full Changelog: v0.5.0...v0.6.0
v0.5.0
What's Changed
- feat: static file directory index by @sjanel in #99
- chore: remove aeronet::invalid_argument, use std::invalid_argument in… by @sjanel in #102
- chore: remove aeronet::exception in favor of standard ones by @sjanel in #103
- chore: move EventLoop in sys module by @sjanel in #104
- feat: cors policy phase 1 by @sjanel in #105
- chore: add header method from int in HttpResponse by @sjanel in #107
- chore: simplify routing & url decoding tests by @sjanel in #108
- chore: simplify tests 2 by @sjanel in #109
- chore: replace underscores with - in test names by @sjanel in #110
- feat: cors phase 2 - possibility to configure via per router path by @sjanel in #106
- chore: simplify encoders code by @sjanel in #112
- chore: rename statusCode method set in status by @sjanel in #113
- feat: simplify setting of content type header in response by @sjanel in #115
- feat: allow HttpServer hot config update by @sjanel in #116
- chore: upgrade spdlog to 1.16.0 by @sjanel in #118
- feat: KTLS support implementation by @sjanel in #117
- feat: middleware pre / post hooks by @sjanel in #119
- feat: middleware metrics by @sjanel in #121
- feat: safe in-flight router updates by @sjanel in #123
Full Changelog: v0.4.0...v0.5.0
v0.4.0
What's Changed
- Open version 0.4.0 by @sjanel in #44
- feat: make codec chunk size configurable by @sjanel in #45
- feat: transport chunk size configurable for precise headers / body si… by @sjanel in #46
- Enhance benchmarks by @sjanel in #47
- feat: possibility to define global response headers by @sjanel in #48
- fix: make servers stop() method noexcept as called from destructor by @sjanel in #49
- feat: make socket & connect non blocking at creation by @sjanel in #53
- chore: factorize test net code by @sjanel in #54
- chore: remove unneeded fields in ConnectionState by @sjanel in #55
- chore: clean-up test utilities by @sjanel in #56
- feat: externalize path handlers, make all servers restartable by @sjanel in #57
- chore: suffix all global tests with _test by @sjanel in #58
- fix: large payloads are now handled correctly for TLS & plain by @sjanel in #60
- feat: opentelemetry phase 1 by @sjanel in #59
- feat: optimization for large body sizes by @sjanel in #63
- feat: AsyncHttpServer new method startWithStopToken by @sjanel in #72
- feat: OPTIONS and TRACE support by @sjanel in #73
- feat: CONNECT request method support by @sjanel in #74
- feat: graceful draining implementation by @sjanel in #76
- feat: builtin probes by @sjanel in #78
- optim: shrink config object size by @sjanel in #80
- chore: merge many http tests and update test coverage matrix by @sjanel in #81
- chore: refactor benchframework by @sjanel in #82
- feat: trailers by @sjanel in #83
- chore: simplify some ifdef macros by @sjanel in #84
- chore: try to fix MultiHttpServer.BeginDrainClosesKeepAliveConnection… by @sjanel in #85
- feat: zero-copy, sendfile implementation by @sjanel in #86
- chore: replace BaseFd::isOpened with operator bool by @sjanel in #87
- Feature/static file handler by @sjanel in #88
- chore: lower severity of pre-handshake TLS error log by @sjanel in #89
- chore: fix some flaky tests by @sjanel in #90
- feat: support HTTP Expect header processing by @sjanel in #91
- chore: attempt to fix flaky TLS test by @sjanel in #92
- chore: make HttpRequest server internal to reuse memory and simplify code by @sjanel in #93
- feat: path pattern support by @sjanel in #95
- chore: better request tracing support even in case of errors by @sjanel in #96
- chore: simplify HttpResponseWriter by @sjanel in #97
Full Changelog: v0.3.0...v0.4.0
v0.3.0
What's Changed
- Open version 0.3.0 by @sjanel in #16
- feat: compression phase 1 with zlib by @sjanel in #20
- bugfix: compare header keys as case insensitive by @sjanel in #21
- bugfix: avoid random freezes at MultiHttpServer stop with threads joins by @sjanel in #22
- feat: reject 406 if no acceptable encoding found by @sjanel in #23
- feat: url decode query params by @sjanel in #24
- Factorize test utilities by @sjanel in #27
- feat: allow empty value headers by @sjanel in #28
- feat: add aeronet/aeronet.hpp umbrella header by @sjanel in #30
- chore: fix more warnings by @sjanel in #31
- feat: handle duplicate headers by @sjanel in #32
- feat: support zstd compression by @sjanel in #33
- feat: automatic request decompression (configurable) by @sjanel in #36
- feat: support brotli compression / decompression by @sjanel in #39
- feat: make MultiHttpServer move while running possible by @sjanel in #40
- feat: MultiHttpServer is now restartable by @sjanel in #41
- chore: reorganize a bit the documentation by @sjanel in #43
Full Changelog: v0.2.0...v0.3.0