#![deny(missing_debug_implementations, missing_docs, unreachable_pub)] #![cfg_attr(test, deny(warnings))] //! Utilities for [`http_body::Body`]. //! //! [`BodyExt`] adds extensions to the common trait. //! //! [`Empty`] and [`Full`] provide simple implementations. mod collected; pub mod combinators; mod either; mod empty; mod full; mod limited; mod stream; #[cfg(feature = "channel")] pub mod channel; mod util; use self::combinators::{BoxBody, MapErr, MapFrame, UnsyncBoxBody}; pub use self::collected::Collected; pub use self::either::Either; pub use self::empty::Empty; pub use self::full::Full; pub use self::limited::{LengthLimitError, Limited}; pub use self::stream::{BodyDataStream, BodyStream, StreamBody}; #[cfg(feature = "channel")] pub use self::channel::Channel; /// An extension trait for [`http_body::Body`] adding various combinators and adapters pub trait BodyExt: http_body::Body { /// Returns a future that resolves to the next [`Frame`], if any. /// /// [`Frame`]: combinators::Frame fn frame(&mut self) -> combinators::Frame<'_, Self> where Self: Unpin, { combinators::Frame(self) } /// Maps this body's frame to a different kind. fn map_frame(self, f: F) -> MapFrame where Self: Sized, F: FnMut(http_body::Frame) -> http_body::Frame, B: bytes::Buf, { MapFrame::new(self, f) } /// Maps this body's error value to a different value. fn map_err(self, f: F) -> MapErr where Self: Sized, F: FnMut(Self::Error) -> E, { MapErr::new(self, f) } /// Turn this body into a boxed trait object. fn boxed(self) -> BoxBody where Self: Sized + Send + Sync + 'static, { BoxBody::new(self) } /// Turn this body into a boxed trait object that is !Sync. fn boxed_unsync(self) -> UnsyncBoxBody where Self: Sized + Send + 'static, { UnsyncBoxBody::new(self) } /// Turn this body into [`Collected`] body which will collect all the DATA frames /// and trailers. fn collect(self) -> combinators::Collect where Self: Sized, { combinators::Collect { body: self, collected: Some(crate::Collected::default()), } } /// Add trailers to the body. /// /// The trailers will be sent when all previous frames have been sent and the `trailers` future /// resolves. /// /// # Example /// /// ``` /// use http::HeaderMap; /// use http_body_util::{Full, BodyExt}; /// use bytes::Bytes; /// /// # #[tokio::main] /// async fn main() { /// let (tx, rx) = tokio::sync::oneshot::channel::(); /// /// let body = Full::::from("Hello, World!") /// // add trailers via a future /// .with_trailers(async move { /// match rx.await { /// Ok(trailers) => Some(Ok(trailers)), /// Err(_err) => None, /// } /// }); /// /// // compute the trailers in the background /// tokio::spawn(async move { /// let _ = tx.send(compute_trailers().await); /// }); /// /// async fn compute_trailers() -> HeaderMap { /// // ... /// # unimplemented!() /// } /// # } /// ``` fn with_trailers(self, trailers: F) -> combinators::WithTrailers where Self: Sized, F: std::future::Future>>, { combinators::WithTrailers::new(self, trailers) } /// Turn this body into [`BodyDataStream`]. fn into_data_stream(self) -> BodyDataStream where Self: Sized, { BodyDataStream::new(self) } } impl BodyExt for T where T: http_body::Body {}