Overview / Roadmap
Feature roadmap
These items describe the direction we want to take with thwip, not currently available behavior.
TLS
Thwip currently serves plaintext HTTP and connects to plaintext http:// upstreams. TLS needs to cover two distinct roles: terminating HTTPS from clients and establishing HTTPS connections to upstream services.
Client TLS termination
A server will need certificate and private-key configuration, protocol and cipher policy, and a way to select certificates during SNI. The listener still owns the accepted socket, while each connection owns its TLS session and handshake state. HTTP parsing begins only after the handshake has produced decrypted application bytes.
Runtime integration
Epoll and kqueue will drive nonblocking handshakes whenever the TLS engine requests more readable or writable socket progress. io_uring will submit network receives and sends around the same runtime-neutral TLS session; cryptographic state remains connection-owned rather than ring-owned. This keeps certificates, routing, and HTTP behavior consistent across drivers.
HTTPS upstreams
Upstream TLS also requires hostname verification, trust-store configuration, SNI, handshake deadlines, and clear separation between DNS, TCP-connect, TLS-handshake, request-write, and response-read failures. Connection pooling should follow TLS so established secure sessions can be reused safely.
Likely configuration areas include certificate/key paths per server, minimum TLS version, trust roots for upstreams, verification controls, and a dedicated handshake timeout. Secure defaults should be mandatory; disabling verification should be explicit and difficult to do accidentally.
Benchmarking
Benchmarking is a primary roadmap stepping stone because Thwip’s platform-specific runtimes exist to pursue practical performance. We are really looking forward to see how our tech does in comparison to other alternatives and how different runtimes affect our performance benchmarks
Core workloads
- Small fixed responses at high request rates.
- Small and large static files, including cached, ranged, and streaming responses.
- Streaming proxy responses with fast and slow clients.
- Short-lived connections and persistent keep-alive workloads when keep-alive lands.
- Balanced upstream traffic, upstream latency, and failure scenarios.
- Connection-limit, queue-saturation, and buffer-starvation behavior under overload.
Measurements
Every published run should report throughput, median and tail latency, CPU time, context switches, memory high-water mark, open connections, errors, and runtime-specific pressure such as submission occupancy or event-batch saturation. Warm-up, run duration, concurrency, response sizes, worker count, kernel, hardware, compiler profile, and configuration must be recorded alongside results.
Fair comparisons
Runtime comparisons need the same machine, client generator, worker count, socket settings, request mix, and output validation. The load generator should run on separate capacity when possible so it does not compete with Thwip. Multiple runs and variance are required; a single best result is not representative.
The intended deliverable is a reproducible benchmark suite plus checked-in configurations and published result snapshots. Optional io_uring operations should be adopted only when those measurements show a meaningful improvement without weakening correctness or portability.
OpenBenchmark benchmarks
Is a benchmark we are looking forward to prepare for, once we achieve a benchmark ready state with sufficient features, we will be conducting this benchmark in the hardware we currently have at our disposal and publish a findings report to this documentation page.
Documenting different user alternatives for benchmarking
Is what our end goal is to observe adoption and adoption success.
Hot reload & thwip_ctl (systemctl/services support)
After TLS, the primary operational feature is a local control plane that can apply configuration changes without dropping active traffic. A dedicated thwip_ctl terminal application will communicate with the master process over a permission-restricted Unix domain socket.
Transactional hot reload
The master should read and fully validate a candidate configuration before changing live state. It then starts a new worker generation with the new listeners, routes, certificates, and upstream definitions. Only after the new generation reports readiness does the master stop sending work to the old generation and ask it to drain within the configured deadline.
If parsing, binding, certificate loading, capability probing, or worker startup fails, the candidate generation is discarded and the existing workers continue serving the previous configuration. Reload must therefore be atomic from an operator’s perspective: either the complete configuration becomes ready or none of it is activated.
thwip_ctl commands
thwip_ctl validate /etc/thwip/thwip.toml
thwip_ctl reload
thwip_ctl status
thwip_ctl workers
thwip_ctl metrics
thwip_ctl drain
thwip_ctl stop
| Command | Purpose |
|---|---|
validate
|
Parse and validate a file without changing the running server. |
reload
|
Request a transactional worker-generation replacement. |
status
|
Show master identity, uptime, active generation, selected runtimes, and reload state. |
workers
|
List worker PIDs, generation, health, restart count, and drain state. |
metrics
|
Read a concise snapshot of per-worker and aggregate counters. |
drain
|
Stop accepting new traffic and gracefully finish active work. |
stop
|
Request graceful server shutdown through the master. |
Control safety
The control socket should default to a runtime directory with restrictive filesystem permissions. Requests need a versioned protocol, bounded message sizes, explicit timeouts, structured error responses, and no ability for workers to mutate master state directly. Signals can remain as an emergency compatibility path, while thwip_ctl becomes the observable and scriptable interface.
HTTP lifecycle
- Expose validated request bodies to actions rather than only forwarding them internally.
- Decode chunked request bodies with bounded memory and correct trailer/framing rules.
- Add HTTP/1.1 keep-alive and complete request/response framing semantics.
- Define WebSocket upgrade behavior and other currently unsupported protocol features.
- Separate header and body limits so large legitimate bodies do not permit oversized request heads.
Regex routing
Thwip will add an explicit regular-expression matcher for routes that cannot be expressed as an exact path or a stable prefix. Exact and prefix matching will remain the default because they are easier to understand, faster to evaluate, and sufficient for most proxy layouts.
Regex routes should be opt-in and evaluated only after exact and prefix matches. Configuration will need an obvious matcher type, a documented ordering rule for multiple matching expressions, and compile-time validation so an invalid pattern prevents startup rather than failing during a request.
Regular expressions can be expensive or surprising when used broadly. The implementation should enforce practical pattern and evaluation limits, avoid unbounded backtracking behavior, and keep captures or rewrite-style transformations out of the first version.
Secondary protocols
WebSockets
WebSocket support begins as an HTTP/1.1 upgrade. Thwip must validate the upgrade handshake, switch the connection out of HTTP request/response mode, and relay frames bidirectionally with bounded buffers, backpressure, idle timeouts, close-frame handling, and half-close/error propagation. The initial goal should be transparent WebSocket proxying rather than interpreting application messages.
UDP proxying
UDP is connectionless and should use a separate datagram listener and action model instead of being forced through the TCP connection abstraction. Workers will need flow tracking keyed by client and destination addresses, bounded per-flow state, idle expiry, datagram-size limits, and a clear policy for upstream selection and response association.
QUIC and HTTP/3
QUIC runs over UDP but adds encrypted connections, streams, congestion control, and transport-level state. HTTP/3 should therefore be treated as a later protocol implementation built on a QUIC library—not as a side effect of adding raw UDP forwarding. Its TLS requirements and lifecycle are substantially different from TCP HTTP.
Likely configuration should separate http, stream, and datagram services so protocol-specific limits and routing remain explicit.
Proxy resilience
- Validate upstream response framing before relaying it to clients.
- Pool and reuse upstream connections with bounded per-worker limits.
- Add retry policy that distinguishes safe retryable failures from requests that may already have reached an upstream.
- Track passive and active health so unavailable endpoints can leave rotation temporarily.
- Add HTTPS upstreams through the TLS work described above.
Pooling, retries, and health checks must remain compatible with streaming and backpressure; they should not require buffering entire requests or responses.
Runtime control and observability
- Extend the runtime-neutral interface with general-purpose timers and control messages.
- Expose the selected runtime and automatic-fallback reason as worker metrics.
- Add more detailed runtime metrics for queue pressure, active operations, buffer use, and wakeups.
- Add configurable logging controls while retaining structured lifecycle and shutdown reports.
Configuration hardening
- Add an explicit configuration schema version so future changes can fail or migrate predictably.
- Validate worker count, server names, route paths, response status codes, static directories, and full upstream URL syntax.
- Add separate request-header and request-body limits.
- Add TLS and upstream connection-pool settings.
- Expose logging level and destination controls.
Optional io_uring acceleration
After baseline behavior is stable, Thwip can capability-probe optional operations such as recvmsg, zero-copy sends, fixed files, and splice. None should become a baseline requirement: unsupported kernels must retain the current paths, and auto mode must continue to choose a dependable runtime.
Adoption should be driven by measured CPU, latency, throughput, queue pressure, and memory behavior rather than the availability of an opcode alone.
Home