base-http1 0.2.0

Native HTTP/1 framing over Base scheduler stream capabilities
Documentation

Base HTTP/1

A native HTTP/1 codec over published Base scheduler stream capabilities. This crate owns head and trailer syntax, framing, read-ahead, persistence and body lifecycle.

RequestReader<R>::read_request returns a Request<IncomingBody<'_, R>> and an Exchange. The body borrows the input lane. ResponseWriter<W> owns an independent output lane, sends interim responses and writes a generic Response<B> where B: ReadableBody. Returning the incoming body gives streaming echo without an extra task or dynamic dispatch.

Exchange retains immutable framing decisions: version, HEAD/CONNECT classification, persistence, Expect/Continue and, for server requests, any upgrade offers. The application owns RequestMetadata and its ordinary fields; protocol control fields have already been extracted. Its mutations cannot change the captured decisions. Use exchange.version() when constructing the interim writer; the earlier prototype's Exchange::head() snapshot accessor has been removed.

The initial implementation supports HTTP/1.0 and HTTP/1.1 requests, fixed and chunked input, request trailers, fixed/chunked/close-delimited output, response trailers, HEAD/bodyless responses, interim heads, persistence, and managed 101/CONNECT duplex bodies. It rejects conflicting framing and enforces configurable head/field/chunk-line limits. An early final response permits reuse when the active upload subsequently completes; abandoned input disables reuse. Only chunked transfer coding is implemented; upgrade selection currently supports one unversioned token.

RequestWriter<W> and ResponseReader<R> provide independent client directions. Drive request output and response input concurrently, delivering interim heads through InterimResponses. The reader uses the matching request head for HEAD/CONNECT semantics and shares the native input body decoder. Request output supports fixed/chunked framing and trailers; response input also supports close-delimited bodies. HTTP/1.0 request bodies require a length. These primitives hide no dialing, pooling, task creation or Expect/Continue wait policy. Switch responses expose raw input and disable HTTP reuse; managed bidirectional client switch coordination remains future work.

A writer's successful persistence result applies to its output lane only. Finish both directions, check input reuse, and join all exchange work before admitting another request. A complete early response must not automatically cancel a still-active upload. An error can cancel its sibling direction, but both must still be awaited. See the duplex experiment.

Bodies use base-http semantics. Request/response/body reads return owned BodyFrame events: Data { data, end }, Headers { fields, end }, or End. Stop after frame.is_end(). Incoming fixed-length data carries final completion; chunked trailers arrive in an owned final header frame. Initial message fields remain untouched; callers can forward frames directly or explicitly collect fields with HttpHeaders::append_trailers. Outgoing writers consume the end flag without another read and serialize header frames directly as trailers. ReadableBody::may_have_trailers() defaults to true and selects HTTP/1.1 chunked framing before reads, even with a known data length. Static bodies return false. Known lengths are validated with either fixed or chunked output. HTTP/1.0 cannot represent actual trailer frames.

The caller owns the socket: flush successful output, await both lane closures, drop the handles, then close the socket exclusively. A failed or dropped started operation prevents reuse, except for incoming body reads: a dropped or cancelled pending body read loses no input, and a later read resumes at the same position. This requires the ReadableStream to transfer bytes only in a read_into poll that returns them, as scheduler socket readers and the TLS stream do. A body read that fails for any other reason fails every later read. Cancellation is distinct from backend retirement. base-http-server implements this ownership sequence and bounded listener orchestration.

The codecs consume transport-neutral base_endpoint::stream::{ReadableStream, WritableStream} capabilities. Scheduler socket handles forward directly into these contracts. TLS handshakes and ALPN selection belong to endpoint and application orchestration; this crate processes plaintext only. The shared base-http-protocol layer constructs matching codec pairs after selection.

Run cargo test --locked and cargo clippy --locked --all-targets -- -D warnings. Deterministic tests cover fragmentation, partial writes and framing failures. Real HTTP and HTTPS integration tests live in the sibling base-http-server and base-http-client packages. Rust 1.87 is the declared minimum. Managed client tunnels, fuzzing and other runtime-backend validation remain follow-up work.

Content-Length is available as metadata.content_length, including on HEAD/304 responses. Host becomes request authority. Transfer-Encoding, Connection, standard connection controls, Connection-nominated fields and Upgrade are consumed by framing/exchange processing and excluded from application Fields. Single-token upgrade intent is represented by protocol; richer upgrade offers remain private exchange state. Outgoing metadata.persistent can request closure or HTTP/1.0 keep-alive. Raw outgoing control fields remain validated, including conflicts with typed values; generated wire controls do not mutate metadata.