Skip to main content

http_body_util/
lib.rs

1#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
2#![cfg_attr(test, deny(warnings))]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5//! Utilities for [`http_body::Body`].
6//!
7//! [`BodyExt`] adds extensions to the common trait.
8//!
9//! [`Empty`] and [`Full`] provide simple implementations.
10
11mod collected;
12pub mod combinators;
13mod either;
14mod empty;
15mod full;
16mod limited;
17mod stream;
18
19#[cfg(feature = "channel")]
20pub mod channel;
21
22mod util;
23
24use self::combinators::{BoxBody, MapErr, MapFrame, UnsyncBoxBody};
25
26pub use self::collected::Collected;
27pub use self::either::Either;
28pub use self::empty::Empty;
29pub use self::full::Full;
30pub use self::limited::{LengthLimitError, Limited};
31pub use self::stream::{BodyDataStream, BodyStream, StreamBody};
32
33#[cfg(feature = "channel")]
34pub use self::channel::Channel;
35
36/// An extension trait for [`http_body::Body`] adding various combinators and adapters
37pub trait BodyExt: http_body::Body {
38    /// Returns a future that resolves to the next [`Frame`], if any.
39    ///
40    /// [`Frame`]: combinators::Frame
41    fn frame(&mut self) -> combinators::Frame<'_, Self>
42    where
43        Self: Unpin,
44    {
45        combinators::Frame(self)
46    }
47
48    /// Maps this body's frame to a different kind.
49    fn map_frame<F, B>(self, f: F) -> MapFrame<Self, F>
50    where
51        Self: Sized,
52        F: FnMut(http_body::Frame<Self::Data>) -> http_body::Frame<B>,
53        B: bytes::Buf,
54    {
55        MapFrame::new(self, f)
56    }
57
58    /// A body that calls a function with a reference to each frame before yielding it.
59    fn inspect_frame<F>(self, f: F) -> combinators::InspectFrame<Self, F>
60    where
61        Self: Sized,
62        F: FnMut(&http_body::Frame<Self::Data>),
63    {
64        combinators::InspectFrame::new(self, f)
65    }
66
67    /// Maps this body's error value to a different value.
68    fn map_err<F, E>(self, f: F) -> MapErr<Self, F>
69    where
70        Self: Sized,
71        F: FnMut(Self::Error) -> E,
72    {
73        MapErr::new(self, f)
74    }
75
76    /// A body that calls a function with a reference to an error before yielding it.
77    fn inspect_err<F>(self, f: F) -> combinators::InspectErr<Self, F>
78    where
79        Self: Sized,
80        F: FnMut(&Self::Error),
81    {
82        combinators::InspectErr::new(self, f)
83    }
84
85    /// Turn this body into a boxed trait object.
86    fn boxed(self) -> BoxBody<Self::Data, Self::Error>
87    where
88        Self: Sized + Send + Sync + 'static,
89    {
90        BoxBody::new(self)
91    }
92
93    /// Turn this body into a boxed trait object that is !Sync.
94    fn boxed_unsync(self) -> UnsyncBoxBody<Self::Data, Self::Error>
95    where
96        Self: Sized + Send + 'static,
97    {
98        UnsyncBoxBody::new(self)
99    }
100
101    /// Turn this body into [`Collected`] body which will collect all the DATA frames
102    /// and trailers.
103    fn collect(self) -> combinators::Collect<Self>
104    where
105        Self: Sized,
106    {
107        combinators::Collect {
108            body: self,
109            collected: Some(crate::Collected::default()),
110        }
111    }
112
113    /// Add trailers to the body.
114    ///
115    /// The trailers will be sent when all previous frames have been sent and the `trailers` future
116    /// resolves.
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// use http::HeaderMap;
122    /// use http_body_util::{Full, BodyExt};
123    /// use bytes::Bytes;
124    ///
125    /// # #[tokio::main]
126    /// async fn main() {
127    /// let (tx, rx) = tokio::sync::oneshot::channel::<HeaderMap>();
128    ///
129    /// let body = Full::<Bytes>::from("Hello, World!")
130    ///     // add trailers via a future
131    ///     .with_trailers(async move {
132    ///         match rx.await {
133    ///             Ok(trailers) => Some(Ok(trailers)),
134    ///             Err(_err) => None,
135    ///         }
136    ///     });
137    ///
138    /// // compute the trailers in the background
139    /// tokio::spawn(async move {
140    ///     let _ = tx.send(compute_trailers().await);
141    /// });
142    ///
143    /// async fn compute_trailers() -> HeaderMap {
144    ///     // ...
145    ///     # unimplemented!()
146    /// }
147    /// # }
148    /// ```
149    fn with_trailers<F>(self, trailers: F) -> combinators::WithTrailers<Self, F>
150    where
151        Self: Sized,
152        F: std::future::Future<Output = Option<Result<http::HeaderMap, Self::Error>>>,
153    {
154        combinators::WithTrailers::new(self, trailers)
155    }
156
157    /// Turn this body into [`BodyStream`].
158    fn into_stream(self) -> BodyStream<Self>
159    where
160        Self: Sized,
161    {
162        BodyStream::new(self)
163    }
164
165    /// Turn this body into [`BodyDataStream`].
166    fn into_data_stream(self) -> BodyDataStream<Self>
167    where
168        Self: Sized,
169    {
170        BodyDataStream::new(self)
171    }
172
173    /// Creates a "fused" body.
174    ///
175    /// This [`Body`][http_body::Body] yields `Poll::Ready(None)` forever after the underlying
176    /// body yields `Poll::Ready(None)`, or an error `Poll::Ready(Some(Err(_)))`, once.
177    ///
178    /// See [`Fuse<B>`][combinators::Fuse] for more information.
179    fn fuse(self) -> combinators::Fuse<Self>
180    where
181        Self: Sized,
182    {
183        combinators::Fuse::new(self)
184    }
185}
186
187impl<T: ?Sized> BodyExt for T where T: http_body::Body {}