core.net.weft.connection
Per-connection HTTP/1.1 pipeline. One spawned task drives a single TCP / TLS / vsock / UDS stream through the keep-alive loop until the client closes, the listener drains, or the cancellation token fires.
Source: core/net/weft/connection.vr.
ConnectionConfig
public type ConnectionConfig is {
max_request_size: Int, // default 1 MiB
read_buffer_capacity: Int, // default 16 KiB
keep_alive: Bool, // default true
};
implement ConnectionConfig {
public fn default() -> ConnectionConfig {
ConnectionConfig {
max_request_size: 1 * 1024 * 1024,
read_buffer_capacity: 16 * 1024,
keep_alive: true,
}
}
}
Listener-level overrides flow through ListenerConfig.connection.
serve_http1 — the pipeline entry point
public async fn serve_http1<T: WeftTransport, S: Handler>(
mut stream: T,
peer_addr: SocketAddr,
app: Heap<S>,
token: CancellationToken,
draining: Shared<AtomicBool>,
config: ConnectionConfig,
) -> Result<(), ConnError>
Generic over the transport (WeftTransport protocol) and the
handler (Handler protocol) — zero-cost monomorphisation.
Behaviour:
- Token check at the top of every loop iteration. If cancelled,
return
Err(Cancelled). - Drain check — if the listener-level
drainingflag is set, the upcoming response is stampedConnection: closeand the loop exits after the message. - Read request — accumulate bytes until the parser declares
Done { consumed, body_len, body_start }orError. - Sojourn-time stamp — record
x-weft-submitted-at(ms) so the CoDel admission layer can compute queue-time downstream. Any client-supplied value is preserved asx-weft-submitted-at-clientto detect injection attempts. - Body read — content-length or chunked. Hard cap at
max_request_size. Truncation returns 413. - Dispatch to
app.handle(request). Success orIntoResponse-rendered error. - Stamp
Connection: keep-aliveorConnection: closeon the response based on HTTP version, requestConnectionheader, server config, and drain state. - Write response — partial-write tolerant; retries until the
buffer is fully drained. I/O errors return
Err(Io). - Loop or return —
KeepAlive.Continuekeeps the connection;KeepAlive.Closeexits.
ConnError — typed connection-loop errors
public type ConnError is
| ClientClosed
| Cancelled
| Io(Text)
| Parse(ParseError)
| TooLarge
| UnsupportedTransferEncoding(Text)
| HandlerError(WeftError);
These never reach the client as wire bytes — they're internal classifications for logging and metrics. The wire response was already written (or could not be written) by the time the error surfaces.
Sojourn-time stamping
The stamp is a millisecond timestamp captured at the moment the
listener begins reading the request. The CoDel layer reads it and
computes now - stamp to get queue-time. If the p95 sojourn time
exceeds target (default 100 ms) for 5 seconds straight, the layer
narrows the concurrency limit — application-layer FQ-CoDel.
The stamp is also useful for tracing: the duration from accept to handler-start gives you the queue depth without instrumenting every layer between.
Chunked transfer encoding
Input chunked decoding goes through ChunkedDecoder from
core.net.http_parser. The decoder is fed accumulated buffer
slices and emits:
ChunkOutput { consumed, data_start, data_len }for each chunk's payload (the connection appendsdata_lenbytes to the body output buffer);ChunkEnd { .. }when the terminating zero-length chunk arrives;ChunkNeedMorewhen more bytes are needed (the connection reads more from the stream and re-feeds);ChunkErr(error)on protocol violation — the connection writes a 400 response and returns the error.
The hard cap on total decoded body size is max_request_size. A
malicious or buggy client cannot blow memory by streaming chunks
indefinitely.
Output chunking (server-side response streaming) is currently a fixed-content-length flow; chunked output is a Phase 2 follow-up.
Keep-alive decision
fn decide_keep_alive(
version: &Version,
headers: &Headers,
server_allows: Bool,
) -> Bool
Rules:
- If the server config disables keep-alive or the listener is
draining, return
false. - For HTTP/1.1: keep-alive unless the client sent
Connection: close. - For HTTP/1.0: keep-alive only if the client sent
Connection: keep-alive. - For other versions: no keep-alive (HTTP/2 / HTTP/3 use their own multiplexing; this path should not be reached).
Comparison is ASCII-fast eq_ignore_case on the borrowed value —
no fresh Text allocation per request.
Cancellation propagation
Every read_cancellable and write_response checks the
cancellation token. The flow:
accept_loopclones the listener-level token into the spawned task.serve_http1polls the token at the top of every loop iteration.- Inside
read_cancellable, the runtime registers a cancel handler that cancels the available kernelrecvsyscall. - The handler's
awaitpoints may also see the same token propagated throughprovide CancellationToken = ....
Result: when shutdown is requested, every available connection
sees cancellation within idle_timeout of the next read or
sooner if a write is in progress. There is no deadline by which a
slow client can keep a connection alive past shutdown.
Status
- Implementation: complete (keep-alive, chunked input, drain-aware, 413 enforcement, cancellation throughout).
- Conformance: covered by
hello_world,slow_loris_pool_exhaustion, andgraceful_shutdowntests. - Phase: 1 + 2 closed.
- Out of scope for current release: chunked output (server-side
streaming), HTTP/1.1 pipelining (multiple requests in flight on a
single connection), HTTP upgrade sideband mechanism (e.g. WebSocket
upgrade is handled by a separate
WsRunner).