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}