Skip to main content

volo_http/
body.rs

1//! HTTP Body implementation for [`http_body::Body`]
2//!
3//! See [`Body`] for more details.
4
5use std::{
6    convert::Infallible,
7    error::Error,
8    fmt,
9    future::Future,
10    pin::Pin,
11    task::{Context, Poll},
12};
13
14use bytes::Bytes;
15use faststr::FastStr;
16use futures_util::stream::Stream;
17use http_body::{Frame, SizeHint};
18use http_body_util::{BodyExt, Full, StreamBody, combinators::BoxBody};
19use hyper::body::Incoming;
20use linkedbytes::{LinkedBytes, Node};
21use pin_project::pin_project;
22#[cfg(feature = "json")]
23use serde::de::DeserializeOwned;
24
25use crate::error::BoxError;
26
27// The `futures_util::stream::BoxStream` does not have `Sync`
28type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + Send + Sync + 'a>>;
29
30/// An implementation for [`http_body::Body`].
31#[pin_project]
32pub struct Body {
33    #[pin]
34    repr: BodyRepr,
35}
36
37#[pin_project(project = BodyProj)]
38enum BodyRepr {
39    /// Complete [`Bytes`], with a certain size and content
40    Full(#[pin] Full<Bytes>),
41    /// Wrapper of [`Incoming`], it usually appears in request of server or response of client.
42    ///
43    /// Althrough [`Incoming`] implements [`http_body::Body`], the type is so commonly used, we
44    /// wrap it here as [`BodyRepr::Hyper`] to avoid cost of [`Box`] with dynamic dispatch.
45    Hyper(#[pin] Incoming),
46    /// Boxed stream with `Item = Result<Frame<Bytes>, BoxError>`
47    Stream(#[pin] StreamBody<BoxStream<'static, Result<Frame<Bytes>, BoxError>>>),
48    /// Boxed [`http_body::Body`]
49    Body(#[pin] BoxBody<Bytes, BoxError>),
50}
51
52impl Default for Body {
53    fn default() -> Self {
54        Body::empty()
55    }
56}
57
58impl Body {
59    /// Create an empty body.
60    pub fn empty() -> Self {
61        Self {
62            repr: BodyRepr::Full(Full::new(Bytes::new())),
63        }
64    }
65
66    /// Create a body by [`Incoming`].
67    ///
68    /// Compared to [`Body::from_body`], this function avoids overhead of allocating by [`Box`]
69    /// and dynamic dispatch by [`dyn http_body::Body`][http_body::Body].
70    pub fn from_incoming(incoming: Incoming) -> Self {
71        Self {
72            repr: BodyRepr::Hyper(incoming),
73        }
74    }
75
76    /// Create a body by a [`Stream`] with `Item = Result<Frame<Bytes>, BoxError>`.
77    pub fn from_stream<S>(stream: S) -> Self
78    where
79        S: Stream<Item = Result<Frame<Bytes>, BoxError>> + Send + Sync + 'static,
80    {
81        Self {
82            repr: BodyRepr::Stream(StreamBody::new(Box::pin(stream))),
83        }
84    }
85
86    /// Create a body by another [`http_body::Body`] instance.
87    pub fn from_body<B>(body: B) -> Self
88    where
89        B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
90        B::Error: Into<BoxError>,
91    {
92        Self {
93            repr: BodyRepr::Body(BoxBody::new(body.map_err(Into::into))),
94        }
95    }
96
97    /// Try to clone the body.
98    ///
99    /// Only in-memory bodies (created from bytes, string, [`Body::empty`], etc.) can be cloned,
100    /// and the clone is cheap since the underlying [`Bytes`] is reference-counted (a shallow
101    /// clone, the payload is not copied).
102    ///
103    /// Streaming bodies ([`Body::from_stream`], [`Body::from_incoming`], [`Body::from_body`]) are
104    /// one-shot and cannot be replayed, so this method returns [`None`] for them. This is mainly
105    /// used by redirect-following, where a request may need to be re-sent: a request with a
106    /// non-cloneable body will not be followed across redirects.
107    pub fn try_clone(&self) -> Option<Self> {
108        match &self.repr {
109            BodyRepr::Full(full) => Some(Self {
110                repr: BodyRepr::Full(full.clone()),
111            }),
112            BodyRepr::Hyper(_) | BodyRepr::Stream(_) | BodyRepr::Body(_) => None,
113        }
114    }
115}
116
117impl http_body::Body for Body {
118    type Data = Bytes;
119    type Error = BoxError;
120
121    fn poll_frame(
122        self: Pin<&mut Self>,
123        cx: &mut Context<'_>,
124    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
125        match self.project().repr.project() {
126            BodyProj::Full(full) => http_body::Body::poll_frame(full, cx).map_err(BoxError::from),
127            BodyProj::Hyper(incoming) => {
128                http_body::Body::poll_frame(incoming, cx).map_err(BoxError::from)
129            }
130            BodyProj::Stream(stream) => http_body::Body::poll_frame(stream, cx),
131            BodyProj::Body(body) => http_body::Body::poll_frame(body, cx),
132        }
133    }
134
135    fn is_end_stream(&self) -> bool {
136        match &self.repr {
137            BodyRepr::Full(full) => http_body::Body::is_end_stream(full),
138            BodyRepr::Hyper(incoming) => http_body::Body::is_end_stream(incoming),
139            BodyRepr::Stream(stream) => http_body::Body::is_end_stream(stream),
140            BodyRepr::Body(body) => http_body::Body::is_end_stream(body),
141        }
142    }
143
144    fn size_hint(&self) -> SizeHint {
145        match &self.repr {
146            BodyRepr::Full(full) => http_body::Body::size_hint(full),
147            BodyRepr::Hyper(incoming) => http_body::Body::size_hint(incoming),
148            BodyRepr::Stream(stream) => http_body::Body::size_hint(stream),
149            BodyRepr::Body(body) => http_body::Body::size_hint(body),
150        }
151    }
152}
153
154impl fmt::Debug for Body {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match &self.repr {
157            BodyRepr::Full(_) => f.write_str("Body::Full"),
158            BodyRepr::Hyper(_) => f.write_str("Body::Hyper"),
159            BodyRepr::Stream(_) => f.write_str("Body::Stream"),
160            BodyRepr::Body(_) => f.write_str("Body::Body"),
161        }
162    }
163}
164
165mod sealed {
166    pub trait SealedBody
167    where
168        Self: http_body::Body + Sized + Send,
169        Self::Data: Send,
170    {
171    }
172
173    impl<T> SealedBody for T
174    where
175        T: http_body::Body + Send,
176        T::Data: Send,
177    {
178    }
179}
180
181/// An extend trait for [`http_body::Body`] that can converting a body to other types
182pub trait BodyConversion: sealed::SealedBody
183where
184    <Self as http_body::Body>::Data: Send,
185{
186    /// Consume a body and convert it into [`Bytes`].
187    fn into_bytes(self) -> impl Future<Output = Result<Bytes, BodyConvertError>> + Send {
188        async {
189            Ok(self
190                .collect()
191                .await
192                .map_err(|_| BodyConvertError::BodyCollectionError)?
193                .to_bytes())
194        }
195    }
196
197    /// Consume a body and convert it into [`Vec<u8>`].
198    fn into_vec(self) -> impl Future<Output = Result<Vec<u8>, BodyConvertError>> + Send {
199        async { Ok(self.into_bytes().await?.into()) }
200    }
201
202    /// Consume a body and convert it into [`String`].
203    fn into_string(self) -> impl Future<Output = Result<String, BodyConvertError>> + Send {
204        async {
205            let vec = self.into_vec().await?;
206
207            // SAFETY: The `Vec<u8>` is checked by `simdutf8` and it is a valid `String`
208            let _ =
209                simdutf8::basic::from_utf8(&vec).map_err(|_| BodyConvertError::StringUtf8Error)?;
210            Ok(unsafe { String::from_utf8_unchecked(vec) })
211        }
212    }
213
214    /// Consume a body and convert it into [`String`].
215    ///
216    /// # Safety
217    ///
218    /// It is up to the caller to guarantee that the value really is valid. Using this when the
219    /// content is invalid causes immediate undefined behavior.
220    unsafe fn into_string_unchecked(
221        self,
222    ) -> impl Future<Output = Result<String, BodyConvertError>> + Send {
223        async {
224            let vec = self.into_vec().await?;
225
226            Ok(unsafe { String::from_utf8_unchecked(vec) })
227        }
228    }
229
230    /// Consume a body and convert it into [`FastStr`].
231    fn into_faststr(self) -> impl Future<Output = Result<FastStr, BodyConvertError>> + Send {
232        async {
233            let bytes = self.into_bytes().await?;
234
235            // SAFETY: The `Vec<u8>` is checked by `simdutf8` and it is a valid `String`
236            let _ = simdutf8::basic::from_utf8(&bytes)
237                .map_err(|_| BodyConvertError::StringUtf8Error)?;
238            Ok(unsafe { FastStr::from_bytes_unchecked(bytes) })
239        }
240    }
241
242    /// Consume a body and convert it into [`FastStr`].
243    ///
244    /// # Safety
245    ///
246    /// It is up to the caller to guarantee that the value really is valid. Using this when the
247    /// content is invalid causes immediate undefined behavior.
248    unsafe fn into_faststr_unchecked(
249        self,
250    ) -> impl Future<Output = Result<FastStr, BodyConvertError>> + Send {
251        async {
252            let bytes = self.into_bytes().await?;
253
254            Ok(unsafe { FastStr::from_bytes_unchecked(bytes) })
255        }
256    }
257
258    /// Consume a body and convert it into an instance with [`DeserializeOwned`].
259    #[cfg(feature = "json")]
260    fn into_json<T>(self) -> impl Future<Output = Result<T, BodyConvertError>> + Send
261    where
262        T: DeserializeOwned,
263    {
264        async {
265            let bytes = self.into_bytes().await?;
266            crate::utils::json::deserialize(&bytes).map_err(BodyConvertError::JsonDeserializeError)
267        }
268    }
269}
270
271impl<T> BodyConversion for T
272where
273    T: sealed::SealedBody,
274    <T as http_body::Body>::Data: Send,
275{
276}
277
278/// General error for polling [`http_body::Body`] or converting the [`Bytes`] just polled.
279#[derive(Debug)]
280pub enum BodyConvertError {
281    /// Failed to collect the body
282    BodyCollectionError,
283    /// The body is not a valid utf-8 string
284    StringUtf8Error,
285    /// Failed to deserialize the json
286    #[cfg(feature = "json")]
287    JsonDeserializeError(crate::utils::json::Error),
288}
289
290impl fmt::Display for BodyConvertError {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            Self::BodyCollectionError => f.write_str("failed to collect body"),
294            Self::StringUtf8Error => f.write_str("body is not a valid string"),
295            #[cfg(feature = "json")]
296            Self::JsonDeserializeError(e) => write!(f, "failed to deserialize body: {e}"),
297        }
298    }
299}
300
301impl Error for BodyConvertError {
302    fn source(&self) -> Option<&(dyn Error + 'static)> {
303        match self {
304            #[cfg(feature = "json")]
305            Self::JsonDeserializeError(e) => Some(e),
306            _ => None,
307        }
308    }
309}
310
311impl From<()> for Body {
312    fn from(_: ()) -> Self {
313        Self::empty()
314    }
315}
316
317impl From<&'static str> for Body {
318    fn from(value: &'static str) -> Self {
319        Self {
320            repr: BodyRepr::Full(Full::new(Bytes::from_static(value.as_bytes()))),
321        }
322    }
323}
324
325impl From<Vec<u8>> for Body {
326    fn from(value: Vec<u8>) -> Self {
327        Self {
328            repr: BodyRepr::Full(Full::new(Bytes::from(value))),
329        }
330    }
331}
332
333impl From<Bytes> for Body {
334    fn from(value: Bytes) -> Self {
335        Self {
336            repr: BodyRepr::Full(Full::new(value)),
337        }
338    }
339}
340
341impl From<FastStr> for Body {
342    fn from(value: FastStr) -> Self {
343        Self {
344            repr: BodyRepr::Full(Full::new(value.into_bytes())),
345        }
346    }
347}
348
349impl From<String> for Body {
350    fn from(value: String) -> Self {
351        Self {
352            repr: BodyRepr::Full(Full::new(Bytes::from(value))),
353        }
354    }
355}
356
357struct LinkedBytesBody<I> {
358    inner: I,
359}
360
361impl<I> http_body::Body for LinkedBytesBody<I>
362where
363    I: Iterator<Item = Node> + Unpin,
364{
365    type Data = Bytes;
366    type Error = Infallible;
367
368    fn poll_frame(
369        self: Pin<&mut Self>,
370        _: &mut Context<'_>,
371    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
372        let this = self.get_mut();
373        let Some(node) = this.inner.next() else {
374            return Poll::Ready(None);
375        };
376        let bytes = match node {
377            Node::Bytes(bytes) => bytes,
378            Node::BytesMut(bytesmut) => bytesmut.freeze(),
379            Node::FastStr(faststr) => faststr.into_bytes(),
380        };
381        Poll::Ready(Some(Ok(Frame::data(bytes))))
382    }
383
384    fn is_end_stream(&self) -> bool {
385        false
386    }
387
388    fn size_hint(&self) -> SizeHint {
389        let (lower, upper) = self.inner.size_hint();
390        let mut size_hint = SizeHint::new();
391        size_hint.set_lower(lower as u64);
392        if let Some(upper) = upper {
393            size_hint.set_upper(upper as u64);
394        }
395        size_hint
396    }
397}
398
399impl From<LinkedBytes> for Body {
400    fn from(value: LinkedBytes) -> Self {
401        Body::from_body(LinkedBytesBody {
402            inner: value.into_iter_list(),
403        })
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use bytes::Bytes;
410    use faststr::FastStr;
411    use linkedbytes::LinkedBytes;
412
413    use super::Body;
414    use crate::body::BodyConversion;
415
416    #[tokio::test]
417    async fn test_from_linked_bytes() {
418        let mut bytes = LinkedBytes::new();
419        bytes.insert(Bytes::from_static(b"Hello, "));
420        bytes.insert_faststr(FastStr::new("world!"));
421        let body = Body::from(bytes);
422        assert_eq!(body.into_string().await.unwrap(), "Hello, world!");
423    }
424
425    #[tokio::test]
426    async fn test_try_clone_in_memory() {
427        // In-memory bodies can be cloned, and the clone carries the same content.
428        let body = Body::from("hello");
429        let cloned = body.try_clone().expect("full body should be cloneable");
430        assert_eq!(cloned.into_string().await.unwrap(), "hello");
431        assert_eq!(body.into_string().await.unwrap(), "hello");
432
433        // Empty body is also cloneable.
434        assert!(Body::empty().try_clone().is_some());
435    }
436
437    #[test]
438    fn test_try_clone_streaming_is_none() {
439        // Streaming bodies are one-shot and cannot be cloned.
440        let stream = futures_util::stream::empty::<
441            Result<http_body::Frame<Bytes>, crate::error::BoxError>,
442        >();
443        let body = Body::from_stream(stream);
444        assert!(body.try_clone().is_none());
445    }
446}