Skip to main content

spire_core/request/
body.rs

1//! HTTP request and response body handling.
2//!
3//! This module provides the [`Body`] type, a type-erased HTTP body implementation
4//! that supports various data sources (bytes, strings, streams, etc.).
5
6use std::any::Any;
7use std::fmt;
8use std::pin::Pin;
9use std::task::{Context, Poll};
10
11use bytes::Bytes;
12use http_body::{Body as HttpBody, Frame, SizeHint};
13use http_body_util::combinators::BoxBody;
14use http_body_util::{BodyExt, Empty, Full};
15
16use crate::BoxError;
17
18/// Forked from [`axum_core::body::Body`].
19///
20/// Attempts to downcast a type `K` to type `T` using runtime type information.
21///
22/// [`axum_core`]: https://github.com/tokio-rs/axum
23fn try_downcast<T, K>(k: K) -> Result<T, K>
24where
25    T: 'static,
26    K: Send + 'static,
27{
28    let mut k = Some(k);
29    match <dyn Any>::downcast_mut::<Option<T>>(&mut k) {
30        Some(x) => Ok(x.take().unwrap()),
31        _ => Err(k.unwrap()),
32    }
33}
34
35/// The `http::`[`Body`] type used in [`Request`]s and [`Response`]s.
36///
37/// [`Body`]: http_body::Body
38/// [`Request`]: http::Request
39/// [`Response`]: http::Response
40pub struct Body(BoxBody<Bytes, BoxError>);
41
42impl Body {
43    /// Creates a new [`Body`].
44    pub fn new<B>(body: B) -> Self
45    where
46        B: HttpBody<Data = Bytes> + Send + Sync + 'static,
47        B::Error: Into<BoxError> + Send + Sync + 'static,
48    {
49        try_downcast(body).unwrap_or_else(|x| {
50            let boxed = x.map_err(Into::into).boxed();
51            Self(boxed)
52        })
53    }
54}
55
56impl Default for Body {
57    #[inline]
58    fn default() -> Self {
59        Self::new(Empty::new())
60    }
61}
62
63impl From<()> for Body {
64    #[inline]
65    fn from((): ()) -> Self {
66        Self::default()
67    }
68}
69
70impl From<Bytes> for Body {
71    #[inline]
72    fn from(bytes: Bytes) -> Self {
73        Self::new(Full::new(bytes))
74    }
75}
76
77impl From<Vec<u8>> for Body {
78    #[inline]
79    fn from(vec: Vec<u8>) -> Self {
80        Bytes::from(vec).into()
81    }
82}
83
84impl From<&'static [u8]> for Body {
85    #[inline]
86    fn from(slice: &'static [u8]) -> Self {
87        Bytes::from(slice).into()
88    }
89}
90
91impl From<String> for Body {
92    #[inline]
93    fn from(string: String) -> Self {
94        string.into_bytes().into()
95    }
96}
97
98impl From<&'static str> for Body {
99    #[inline]
100    fn from(slice: &'static str) -> Self {
101        slice.as_bytes().into()
102    }
103}
104
105impl fmt::Debug for Body {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        f.debug_struct("Body").finish_non_exhaustive()
108    }
109}
110
111impl HttpBody for Body {
112    type Data = Bytes;
113    type Error = BoxError;
114
115    #[inline]
116    fn poll_frame(
117        mut self: Pin<&mut Self>,
118        cx: &mut Context<'_>,
119    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
120        Pin::new(&mut self.0).poll_frame(cx)
121    }
122
123    #[inline]
124    fn is_end_stream(&self) -> bool {
125        self.0.is_end_stream()
126    }
127
128    #[inline]
129    fn size_hint(&self) -> SizeHint {
130        self.0.size_hint()
131    }
132}
133
134/// Type alias for `http::`[`Request`] whose body type defaults to [`Body`].
135///
136/// [`Request`]: http::Request
137#[cfg(test)]
138mod test {
139    use bytes::Bytes;
140    use http_body_util::{Empty, Full};
141
142    use super::Body;
143
144    #[test]
145    pub fn empty() {
146        let _ = Body::default();
147        let _ = Body::new(Empty::new());
148    }
149
150    #[test]
151    pub fn full() {
152        let body = Full::new(Bytes::new());
153        let _ = Body::new(body);
154    }
155}