# 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](../../design/duplex.md).
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.