Skip to main content

base_http/body/
mod.rs

1// Released under the MIT License.
2// Copyright, 2019-2026, by Samuel Williams.
3// Copyright, 2023, by Bruno Sutic.
4
5//! Native HTTP bodies, following `Protocol::HTTP::Body`.
6//!
7//! [`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.
8//!
9//! 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:
10//!
11//! ```rust
12//! use base_http::body::{BufferedBody, HeadBody, ReadableBody};
13//!
14//! let mut body = BufferedBody::from(b"hello".to_vec());
15//! assert!(body.is_ready());
16//! assert_eq!(body.length(), Some(5));
17//! assert_eq!(body.read_chunk(), Some(b"hello".to_vec()));
18//! assert_eq!(body.length(), Some(5));
19//! assert!(body.rewind());
20//!
21//! let head = HeadBody::for_body(&mut body);
22//! assert!(head.is_empty());
23//! assert_eq!(head.length(), Some(5));
24//! ```
25//!
26//! 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.
27//!
28//! [`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.
29//!
30//! ```rust
31//! use base_http::body::{BodyFrame, BodyReader, BodyError, ReadableBody};
32//! use base_scheduler::Cancellation;
33//! use futures_lite::future::{block_on, zip};
34//!
35//! let (mut reader, mut writer) = BodyReader::bounded(1024, 16);
36//! let producer = async {
37//!     writer.write_frame(
38//!         BodyFrame::Data { data: b"hello".to_vec(), end: true },
39//!         &Cancellation::never(),
40//!     ).await?;
41//!     Ok::<_, BodyError>(())
42//! };
43//! let consumer = async {
44//!     let mut bytes = Vec::new();
45//!     loop {
46//!         let frame = reader.read(&Cancellation::never()).await?;
47//!         let end = frame.is_end();
48//!         match frame {
49//!             BodyFrame::Data { data, .. } => bytes.extend(data),
50//!             BodyFrame::Headers { fields, .. } => {
51//!                 // Forward fields, inspect them, or explicitly collect them.
52//!                 assert!(fields.is_empty());
53//!             }
54//!             BodyFrame::End => {}
55//!         }
56//!         if end { break; }
57//!     }
58//!     Ok::<_, BodyError>(bytes)
59//! };
60//! let (sent, received) = block_on(zip(producer, consumer));
61//! sent?;
62//! assert_eq!(received?, b"hello");
63//! # Ok::<(), BodyError>(())
64//! ```
65//!
66//! # Polling reads alongside other I/O
67//!
68//! A streaming consumer, such as a server-sent events reader, often waits for the next frame and for another source, such as a command channel or a timer, at the same time. The [`ReadableBody::read`] contract requires that dropping a pending read loses no frame, so create a new read for each wait and drop it when the other source wins. The next read continues at the same position. I/O-backed bodies meet this when their underlying reader is cancel-safe, as scheduler sockets and TLS streams are.
69//!
70//! The read future is not `Unpin`, and macros such as `futures_util::select!` require every arm to be `FusedFuture + Unpin`. Pin each read with [`std::pin::pin!`] and fuse it with `FutureExt::fuse`, and do the same for other futures which are not already fused, such as a channel's `recv()`; no custom wrapper type or allocation is needed. Combinators such as `futures_lite::future::or`, whose futures produce one output type, need neither. When the other source only needs to stop reading, a [`Cancellation`] passed to `read` is simpler still.
71//!
72//! ```rust
73//! use base_http::body::{BodyError, BodyFrame, BodyReader, ReadableBody};
74//! use base_scheduler::Cancellation;
75//! use futures_lite::future::block_on;
76//! use futures_util::{FutureExt, StreamExt, select_biased};
77//! use std::pin::pin;
78//!
79//! enum Command {
80//!     Stop,
81//! }
82//!
83//! let (mut body, mut writer) = BodyReader::bounded(1024, 16);
84//! let (sender, mut commands) = futures_channel::mpsc::unbounded();
85//! let never = Cancellation::never();
86//! block_on(async {
87//!     writer.write_frame(BodyFrame::Data { data: b"event".to_vec(), end: false }, &never).await?;
88//!     let mut received = Vec::new();
89//!     loop {
90//!         // Each iteration races a new read, which is dropped while still waiting when the command wins.
91//!         let mut read = pin!(body.read(&never).fuse());
92//!         select_biased! {
93//!             frame = read => {
94//!                 let frame = frame?;
95//!                 // Check completion before moving the frame's contents.
96//!                 let end = frame.is_end();
97//!                 if let BodyFrame::Data { data, .. } = frame {
98//!                     received.extend(data);
99//!                 }
100//!                 if end {
101//!                     break;
102//!                 }
103//!                 sender.unbounded_send(Command::Stop).unwrap();
104//!             }
105//!             command = commands.next() => match command {
106//!                 Some(Command::Stop) | None => break,
107//!             },
108//!         }
109//!     }
110//!     assert_eq!(received, b"event");
111//!
112//!     // The dropped read consumed nothing, so the body is still read in order.
113//!     writer.write_frame(BodyFrame::Data { data: b"more".to_vec(), end: true }, &never).await?;
114//!     let BodyFrame::Data { data, end } = body.read(&never).await? else {
115//!         unreachable!();
116//!     };
117//!     assert_eq!((data.as_slice(), end), (&b"more"[..], true));
118//!     Ok::<_, BodyError>(())
119//! })?;
120//! # Ok::<(), BodyError>(())
121//! ```
122//!
123//! 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.
124use base_scheduler::Cancellation;
125use std::future::Future;
126
127mod buffered;
128mod empty;
129mod error;
130mod frame;
131mod head;
132mod writable;
133
134pub use buffered::BufferedBody;
135pub use empty::EmptyBody;
136pub use error::BodyError;
137pub use frame::BodyFrame;
138pub use head::HeadBody;
139pub use writable::{BodyReader, BodyWriter};
140
141/// Protocol-independent body observations and lifecycle.
142///
143/// No observation performs I/O or requires a scheduler. Readiness, exhaustion,
144/// known representation length and rewindability are independent properties.
145pub trait ReadableBody: Send {
146    /// Application data, without imposing completion-I/O buffer requirements.
147    type Data: AsRef<[u8]> + Send;
148    type Error: std::error::Error + Send + Sync + 'static;
149
150    /// Whether another read can proceed without waiting for produced data.
151    ///
152    /// This is a conservative batching hint. Terminal completion/error can also
153    /// be ready. It does not promise that an I/O operation will finish in one poll.
154    fn is_ready(&self) -> bool {
155        false
156    }
157
158    /// Whether no unread payload frames remain. False can mean unknown; an open
159    /// queue with no frames is not exhausted. This does not suppress read errors.
160    fn is_empty(&self) -> bool {
161        false
162    }
163
164    /// Total representation length, independent of the current read position.
165    fn length(&self) -> Option<u64> {
166        None
167    }
168
169    /// Whether this body can emit trailing headers. An HTTP/1.1 response needs
170    /// this before reading to choose chunked framing, even when the data length
171    /// is known. An HTTP/1 request of known length uses `Content-Length` and
172    /// drops any trailers whatever this reports. Returning false promises that
173    /// no Headers frame will be produced.
174    fn may_have_trailers(&self) -> bool {
175        true
176    }
177
178    fn is_rewindable(&self) -> bool {
179        false
180    }
181
182    /// Restore the read position when supported, reporting success.
183    fn rewind(&mut self) -> bool {
184        false
185    }
186
187    /// Abandon further application reads, optionally reporting their failure.
188    ///
189    /// This synchronous operation does not guarantee asynchronous resource
190    /// retirement. An I/O-backed implementation must leave cleanup with an
191    /// explicit resource/driver owner. Cancelling or dropping a read does not
192    /// prove that backend access ended, nor that a connection can be reused.
193    fn close(&mut self, error: Option<Self::Error>);
194
195    /// Read one owned frame. Pre-cancellation prevents consumption. The caller
196    /// stops after `frame.is_end()`; subsequent reads return `BodyFrame::End`.
197    /// Headers occur at most once, after all data. Dropping a pending read is
198    /// not transport retirement, and must not lose an unreturned queued frame.
199    ///
200    /// Callers can therefore race a read against other futures; see [polling reads alongside other I/O](self#polling-reads-alongside-other-io).
201    fn read(
202        &mut self,
203        cancellation: &Cancellation,
204    ) -> impl Future<Output = Result<BodyFrame<Self::Data>, Self::Error>> + Send;
205}
206impl<R: ReadableBody + ?Sized> ReadableBody for &mut R {
207    type Data = R::Data;
208    type Error = R::Error;
209
210    fn is_ready(&self) -> bool {
211        (**self).is_ready()
212    }
213    fn is_empty(&self) -> bool {
214        (**self).is_empty()
215    }
216    fn length(&self) -> Option<u64> {
217        (**self).length()
218    }
219    fn may_have_trailers(&self) -> bool {
220        (**self).may_have_trailers()
221    }
222    fn is_rewindable(&self) -> bool {
223        (**self).is_rewindable()
224    }
225    fn rewind(&mut self) -> bool {
226        (**self).rewind()
227    }
228    fn close(&mut self, error: Option<Self::Error>) {
229        (**self).close(error)
230    }
231
232    async fn read(
233        &mut self,
234        cancellation: &Cancellation,
235    ) -> Result<BodyFrame<Self::Data>, Self::Error> {
236        (**self).read(cancellation).await
237    }
238}
239
240/// A body producer accepting owned frames with backpressure.
241/// Normal closure preserves accepted frames; failure abandons unfinished output.
242/// Flushing exposes accepted frames to the consumer, without promising peer receipt.
243pub trait WritableBody: Send {
244    type Data: AsRef<[u8]> + Send;
245    type Error: std::error::Error + Send + Sync + 'static;
246
247    /// Accept one whole frame. Headers follow all data; a terminal frame closes
248    /// output atomically with acceptance. Later writes fail. Queue capacity
249    /// becoming available does not mean the transport has released its buffers.
250    fn write_frame(
251        &mut self,
252        frame: BodyFrame<Self::Data>,
253        cancellation: &Cancellation,
254    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
255
256    /// Write a nonfinal data frame. Use `write_frame` to carry fields or end.
257    fn write(
258        &mut self,
259        data: Self::Data,
260        cancellation: &Cancellation,
261    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
262        self.write_frame(BodyFrame::Data { data, end: false }, cancellation)
263    }
264    fn flush(
265        &mut self,
266        cancellation: &Cancellation,
267    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
268    fn close_write(&mut self, error: Option<Self::Error>);
269    fn abort(&mut self);
270}
271
272impl<W: WritableBody + ?Sized> WritableBody for &mut W {
273    type Data = W::Data;
274    type Error = W::Error;
275    async fn write_frame(
276        &mut self,
277        frame: BodyFrame<Self::Data>,
278        cancellation: &Cancellation,
279    ) -> Result<(), Self::Error> {
280        (**self).write_frame(frame, cancellation).await
281    }
282    async fn write(
283        &mut self,
284        data: Self::Data,
285        cancellation: &Cancellation,
286    ) -> Result<(), Self::Error> {
287        (**self).write(data, cancellation).await
288    }
289    async fn flush(&mut self, cancellation: &Cancellation) -> Result<(), Self::Error> {
290        (**self).flush(cancellation).await
291    }
292    fn close_write(&mut self, error: Option<Self::Error>) {
293        (**self).close_write(error)
294    }
295    fn abort(&mut self) {
296        (**self).abort()
297    }
298}