What is HTTP Body?
masterhttp-body crate provides a trait that represents asynchronous operations on an HTTP body. It is designed to handle the streaming nature of HTTP bodies in an asynchronous environment.repository·master·Indexed 41 days ago
https://github.com/hyperium/http-bodyA Rust crate providing a trait for asynchronous operations on HTTP bodies, designed to handle streaming data in asynchronous environments. Version 1.1.0 includes the http-body-util crate, which offers utility extension traits (BodyExt), concrete implementations like Channel, Empty, and ErrorBody, and combinators such as Fuse, Either, and BoxBody for body manipulation, transformation, and composition.
http-body crate provides a trait that represents asynchronous operations on an HTTP body. It is designed to handle the streaming nature of HTTP bodies in an asynchronous environment.The Body trait represents an asynchronous streaming body for an HTTP Request or Response. It yields individual Frame<Self::Data> instances via the poll_frame method.
Self::Data or an optional set of trailers (common in HTTP/2.0).poll_frame. The stream ends when poll_frame returns Poll::Ready(None).poll_frame returns Poll::Ready(Some(Err(error))), the body should be considered broken and discarded.is_end_stream(): Returns true if the end of the stream has been reached. This allows consumers to avoid unnecessary calls to poll_frame.size_hint(): Provides an estimate of the remaining bytes in the stream. If the exact length is known, the upper and lower bounds in the SizeHint will be equal.pub trait Body {
type Data: Buf;
type Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>;
fn is_end_stream(&self) -> bool;
fn size_hint(&self) -> SizeHint;
}The Either<L, R> enum is a sum type used when a value (specifically an HTTP body) can be one of two distinct types. It allows you to wrap two different types into a single type that implements the Body trait, provided both types share the same Data type and their errors can be converted into Box<dyn Error + Send + Sync>.
Either::Left(L): Holds a value of type L.Either::Right(R): Holds a value of type R.BodyWhen both L and R implement Body with the same Data type, Either<L, R> also implements Body. This is useful for middleware or combinators that need to return a body that might originate from different sources.
// Example: Wrapping two different body types into one
let body: Either<Full<Bytes>, Empty<Bytes>> = Either::Left(Full::new(bytes));The SizeHint struct provides a way to represent the estimated size of data in an HTTP body using a lower bound and an optional upper bound.
SizeHint (via new() or Default::default()) has a lower bound of 0 and an upper bound of None.with_exact(value) or set_exact(value).lower() returns the minimum guaranteed bytes.upper() returns the maximum possible bytes as an Option<u64> (None if unknown).SizeHint instances together using the + operator. The resulting SizeHint will have a lower bound equal to the sum of the two lower bounds, and an upper bound that is the sum of the two upper bounds (if both are Some). If either upper bound is None, the resulting upper bound is None.use http_body::SizeHint;
// Create a hint with an exact size
let hint = SizeHint::with_exact(1024);
assert_eq!(hint.lower(), 1024);
assert_eq!(hint.upper(), Some(1024));
assert_eq!(hint.exact(), Some(1024));
// Combine hints
let hint1 = SizeHint::with_exact(100);
let hint2 = SizeHint::with_exact(200);
let combined = hint1 + hint2;
assert_eq!(combined.exact(), Some(300));The Fuse<B> struct is a combinator that wraps an underlying Body to ensure it is not polled again after it has finished.
This is useful for implementations that do not guarantee they will continue to return Poll::Ready(None) indefinitely after the end of the stream is reached. Once the underlying body yields Poll::Ready(None) or a single error Poll::Ready(Some(Err(_))), Fuse<B> will yield Poll::Ready(None) forever and will stop polling the inner body.
This behavior is analogous to std::iter::Iterator::fuse() for iterators.
// Example conceptual usage
let fused_body = Fuse::new(original_body);The String type implements Body for convenience. It treats the entire string as a single data frame and returns Infallible as the error type.
bytes::Bytesstd::convert::Infalliblepoll_frame returns the string content as one frame and then returns None on subsequent calls.// String implements Body
// Data = Bytes, Error = InfallibleThe Collected<B> struct is produced by calling BodyExt::collect on an HTTP body. It buffers all DATA frames and any trailers sent by the body. This is useful when you need to consume the entire body at once or inspect the HTTP trailers.
Key methods:
to_bytes(): Consumes the Collected body and returns the aggregated data as a single Bytes object.aggregate(): Consumes the Collected body and returns the underlying buffers as an object implementing the Buf trait.trailers(): Returns a reference to the HeaderMap containing the body's trailers, if any.take_trailers(): Removes and returns the HeaderMap containing the body's trailers.// Example: Collecting a body and converting to Bytes
let buffered = body.collect().await.unwrap();
let mut buf = buffered.to_bytes();The BodyExt trait provides a suite of extension methods for any type implementing http_body::Body. It allows you to transform, inspect, and combine bodies using functional combinators. Common operations include mapping errors, mapping frames, boxing the body for type erasure, and limiting the body size.
use http_body_util::{BodyExt, Full};
use bytes::Bytes;
// Example: Chaining two bodies together
let first = Full::new(Bytes::from("hello "));
let second = Full::new(Bytes::from("world!"));
let chained = first.chain(second);
// Example: Limiting body size
let limited_body = first.limited(1024);Use BoxBody<D, E> to erase the specific type of an HTTP body while preserving its Data (D) and Error (E) types. This is useful when you need to return different body implementations from a single function or store them in a collection. BoxBody requires the underlying body to be Send + Sync + 'static.
To create a new BoxBody, use BoxBody::new(body) where body implements Body and Data implements Buf.
use http_body_util::BoxBody;
use http_body::Body;
// Assuming 'my_body' is some type implementing Body<Data = Bytes, Error = MyError>
let boxed_body = BoxBody::new(my_body);The ErrorBody<D, E> struct implements the Body trait and is designed to yield an error of type E immediately when polled. It is primarily used for testing error-handling logic in asynchronous code.
To use it, provide the error value via ErrorBody::new(error). The generic parameter D represents the type of data the body would have yielded if it hadn't failed, allowing it to satisfy type requirements for functions expecting specific body data types.
use bytes::Bytes;
use http_body_util::{BodyExt, ErrorBody};
#[tokio::main]
async fn main() {
let mut body = ErrorBody::<Bytes, &str>::new("problem");
let frame = body.frame().await;
assert_eq!(
frame.unwrap().unwrap_err(),
"problem",
);
}Body trait is automatically implemented for http::Request<B> and http::Response<B> where B: Body. This allows you to treat a request or response itself as a stream of frames by delegating calls to the underlying body.To create a fused body, use Fuse::new(body).
If the provided body already reports that the stream has ended via is_end_stream(), the resulting Fuse<B> will immediately report is_end_stream() == true and will not poll the inner body.
impl<B> Fuse<B>
where
B: Body,
{
/// Returns a fused body.
pub fn new(body: B) -> Self {
Self {
inner: if body.is_end_stream() {
None
} else {
Some(body)
},
}
}
}