Skip to main content

base_http/body/
mod.rs

1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4//! Native HTTP bodies, following `Protocol::HTTP::Body`.
5//!
6//! [`ReadableBody`] combines synchronous observations and abandonment with asynchronous frame reads using explicit cancellation. I/O-backed implementations retain explicitly constructed scheduler-bound capabilities; asynchronous retirement remains with the resource or driver owner, and [`ReadableBody::close`] signals abandonment without proving retirement. Memory bodies need no scheduler. Readiness, exhaustion, total representation length and replayability have separate meanings.
7//!
8//! Body length describes the whole representation. It does not decrease as chunks are consumed, and a [`HeadBody`] body may report a nonzero length while emitting no data:
9//!
10//! ```rust
11//! use base_http::body::{BufferedBody, HeadBody, ReadableBody};
12//!
13//! let mut body = BufferedBody::from(b"hello".to_vec());
14//! assert!(body.is_ready());
15//! assert_eq!(body.length(), Some(5));
16//! assert_eq!(body.read_chunk(), Some(b"hello".to_vec()));
17//! assert_eq!(body.length(), Some(5));
18//! assert!(body.rewind());
19//!
20//! let head = HeadBody::for_body(&mut body);
21//! assert!(head.is_empty());
22//! assert_eq!(head.length(), Some(5));
23//! ```
24//!
25//! Reads move owned [`BodyFrame`] events: `Data { data, end }`, `Headers { fields, end }` and `End`; [`BodyFrame::is_end`] handles both payload-associated and separate completion. Initial fields stay in the message metadata; a body `Headers` frame contains trailers, and no data or further headers may follow it. An empty data payload is ordinary data unless `end` is true. After completion, reads return `End`. `Request::read` and `Response::read` return these frames without mutating the initial fields; forward a frame with [`WritableBody::write_frame`] or collect its fields with `HttpHeaders::append_trailers`. [`BodyFrame::map_data`] changes the data owner while preserving fields and completion, so forwarding does not require a common allocation, cloning, boxing or copying payload bytes.
26//!
27//! [`BodyReader`] and [`BodyWriter`] are bounded consumer/producer halves implementing [`ReadableBody`] and [`WritableBody`]. [`BodyReader::bounded`] applies backpressure under both a byte and a frame limit. Normal write closure drains accepted frames and marks the last queued payload as final; failure discards queued data and preserves the cause.
28//!
29//! ```rust
30//! use base_http::body::{BodyFrame, BodyReader, BodyError, ReadableBody};
31//! use base_scheduler::Cancellation;
32//! use futures_lite::future::{block_on, zip};
33//!
34//! let (mut reader, mut writer) = BodyReader::bounded(1024, 16);
35//! let producer = async {
36//!     writer.write_frame(
37//!         BodyFrame::Data { data: b"hello".to_vec(), end: true },
38//!         &Cancellation::never(),
39//!     ).await?;
40//!     Ok::<_, BodyError>(())
41//! };
42//! let consumer = async {
43//!     let mut bytes = Vec::new();
44//!     loop {
45//!         let frame = reader.read(&Cancellation::never()).await?;
46//!         let end = frame.is_end();
47//!         match frame {
48//!             BodyFrame::Data { data, .. } => bytes.extend(data),
49//!             BodyFrame::Headers { fields, .. } => {
50//!                 // Forward fields, inspect them, or explicitly collect them.
51//!                 assert!(fields.is_empty());
52//!             }
53//!             BodyFrame::End => {}
54//!         }
55//!         if end { break; }
56//!     }
57//!     Ok::<_, BodyError>(bytes)
58//! };
59//! let (sent, received) = block_on(zip(producer, consumer));
60//! sent?;
61//! assert_eq!(received?, b"hello");
62//! # Ok::<(), BodyError>(())
63//! ```
64//!
65//! There is one pull-body abstraction. A protocol driver reads bodies directly in its existing future, so bodies do not spawn tasks. Custom bodies can read incoming input while producing outgoing chunks. Imperative producers can use the bounded pair with explicitly supervised concurrent work.
66use base_scheduler::Cancellation;
67use std::future::Future;
68
69mod buffered;
70mod empty;
71mod error;
72mod frame;
73mod head;
74mod writable;
75
76pub use buffered::BufferedBody;
77pub use empty::EmptyBody;
78pub use error::BodyError;
79pub use frame::BodyFrame;
80pub use head::HeadBody;
81pub use writable::{BodyReader, BodyWriter};
82
83/// Protocol-independent body observations and lifecycle.
84///
85/// No observation performs I/O or requires a scheduler. Readiness, exhaustion,
86/// known representation length and rewindability are independent properties.
87pub trait ReadableBody: Send {
88    /// Application data, without imposing completion-I/O buffer requirements.
89    type Data: AsRef<[u8]> + Send;
90    type Error: std::error::Error + Send + Sync + 'static;
91
92    /// Whether another read can proceed without waiting for produced data.
93    ///
94    /// This is a conservative batching hint. Terminal completion/error can also
95    /// be ready. It does not promise that an I/O operation will finish in one poll.
96    fn is_ready(&self) -> bool {
97        false
98    }
99
100    /// Whether no unread payload frames remain. False can mean unknown; an open
101    /// queue with no frames is not exhausted. This does not suppress read errors.
102    fn is_empty(&self) -> bool {
103        false
104    }
105
106    /// Total representation length, independent of the current read position.
107    fn length(&self) -> Option<u64> {
108        None
109    }
110
111    /// Whether this body can emit trailing headers. HTTP/1 needs this before
112    /// reading to choose chunked framing, even when the data length is known.
113    /// Returning false promises that no Headers frame will be produced.
114    fn may_have_trailers(&self) -> bool {
115        true
116    }
117
118    fn is_rewindable(&self) -> bool {
119        false
120    }
121
122    /// Restore the read position when supported, reporting success.
123    fn rewind(&mut self) -> bool {
124        false
125    }
126
127    /// Abandon further application reads, optionally reporting their failure.
128    ///
129    /// This synchronous operation does not guarantee asynchronous resource
130    /// retirement. An I/O-backed implementation must leave cleanup with an
131    /// explicit resource/driver owner. Cancelling or dropping a read does not
132    /// prove that backend access ended, nor that a connection can be reused.
133    fn close(&mut self, error: Option<Self::Error>);
134
135    /// Read one owned frame. Pre-cancellation prevents consumption. The caller
136    /// stops after `frame.is_end()`; subsequent reads return `BodyFrame::End`.
137    /// Headers occur at most once, after all data. Dropping a pending read is
138    /// not transport retirement, and must not lose an unreturned queued frame.
139    fn read(
140        &mut self,
141        cancellation: &Cancellation,
142    ) -> impl Future<Output = Result<BodyFrame<Self::Data>, Self::Error>> + Send;
143}
144impl<R: ReadableBody + ?Sized> ReadableBody for &mut R {
145    type Data = R::Data;
146    type Error = R::Error;
147
148    fn is_ready(&self) -> bool {
149        (**self).is_ready()
150    }
151    fn is_empty(&self) -> bool {
152        (**self).is_empty()
153    }
154    fn length(&self) -> Option<u64> {
155        (**self).length()
156    }
157    fn may_have_trailers(&self) -> bool {
158        (**self).may_have_trailers()
159    }
160    fn is_rewindable(&self) -> bool {
161        (**self).is_rewindable()
162    }
163    fn rewind(&mut self) -> bool {
164        (**self).rewind()
165    }
166    fn close(&mut self, error: Option<Self::Error>) {
167        (**self).close(error)
168    }
169
170    async fn read(
171        &mut self,
172        cancellation: &Cancellation,
173    ) -> Result<BodyFrame<Self::Data>, Self::Error> {
174        (**self).read(cancellation).await
175    }
176}
177
178/// A body producer accepting owned frames with backpressure.
179/// Normal closure preserves accepted frames; failure abandons unfinished output.
180/// Flushing exposes accepted frames to the consumer, without promising peer receipt.
181pub trait WritableBody: Send {
182    type Data: AsRef<[u8]> + Send;
183    type Error: std::error::Error + Send + Sync + 'static;
184
185    /// Accept one whole frame. Headers follow all data; a terminal frame closes
186    /// output atomically with acceptance. Later writes fail. Queue capacity
187    /// becoming available does not mean the transport has released its buffers.
188    fn write_frame(
189        &mut self,
190        frame: BodyFrame<Self::Data>,
191        cancellation: &Cancellation,
192    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
193
194    /// Write a nonfinal data frame. Use `write_frame` to carry fields or end.
195    fn write(
196        &mut self,
197        data: Self::Data,
198        cancellation: &Cancellation,
199    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
200        self.write_frame(BodyFrame::Data { data, end: false }, cancellation)
201    }
202    fn flush(
203        &mut self,
204        cancellation: &Cancellation,
205    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
206    fn close_write(&mut self, error: Option<Self::Error>);
207    fn abort(&mut self);
208}
209
210impl<W: WritableBody + ?Sized> WritableBody for &mut W {
211    type Data = W::Data;
212    type Error = W::Error;
213    async fn write_frame(
214        &mut self,
215        frame: BodyFrame<Self::Data>,
216        cancellation: &Cancellation,
217    ) -> Result<(), Self::Error> {
218        (**self).write_frame(frame, cancellation).await
219    }
220    async fn write(
221        &mut self,
222        data: Self::Data,
223        cancellation: &Cancellation,
224    ) -> Result<(), Self::Error> {
225        (**self).write(data, cancellation).await
226    }
227    async fn flush(&mut self, cancellation: &Cancellation) -> Result<(), Self::Error> {
228        (**self).flush(cancellation).await
229    }
230    fn close_write(&mut self, error: Option<Self::Error>) {
231        (**self).close_write(error)
232    }
233    fn abort(&mut self) {
234        (**self).abort()
235    }
236}