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