Skip to main content

xitca_http/
body.rs

1//! HTTP body types.
2//!
3//! body types are generic over [Body] trait and mutation of body type must also implement said
4//! trait for being accepted as body type that xitca-http know of.
5
6pub use http_body_alt::{Body, BodyExt, Frame, SizeHint, util::*};
7
8use core::{
9    pin::Pin,
10    task::{Context, Poll},
11};
12
13use std::{borrow::Cow, error};
14
15use pin_project_lite::pin_project;
16
17use super::{
18    bytes::{Buf, Bytes, BytesMut},
19    error::BodyError,
20};
21
22/// A unified request body type for different http protocols.
23/// This enables one service type to handle multiple http protocols.
24#[derive(Default)]
25pub enum RequestBody {
26    #[cfg(feature = "http2")]
27    H2(super::h2::RequestBody),
28    #[cfg(feature = "http3")]
29    H3(super::h3::RequestBody),
30    Boxed(BoxBody),
31    #[default]
32    None,
33}
34
35impl RequestBody {
36    pub fn into_boxed(self) -> BoxBody {
37        match self {
38            #[cfg(feature = "http2")]
39            Self::H2(body) => BoxBody::new(body),
40            #[cfg(feature = "http3")]
41            Self::H3(body) => BoxBody::new(body),
42            Self::Boxed(body) => body,
43            Self::None => BoxBody::new(Empty::<Bytes>::new()),
44        }
45    }
46}
47
48impl Body for RequestBody {
49    type Data = Bytes;
50    type Error = BodyError;
51
52    #[inline]
53    fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
54        match self.get_mut() {
55            #[cfg(feature = "http2")]
56            Self::H2(body) => Pin::new(body).poll_frame(cx),
57            #[cfg(feature = "http3")]
58            Self::H3(body) => Pin::new(body).poll_frame(cx),
59            Self::Boxed(body) => Pin::new(body).poll_frame(cx),
60            Self::None => Poll::Ready(None),
61        }
62    }
63
64    #[inline]
65    fn is_end_stream(&self) -> bool {
66        match self {
67            #[cfg(feature = "http2")]
68            Self::H2(body) => body.is_end_stream(),
69            #[cfg(feature = "http3")]
70            Self::H3(body) => body.is_end_stream(),
71            Self::Boxed(body) => body.is_end_stream(),
72            Self::None => true,
73        }
74    }
75
76    #[inline]
77    fn size_hint(&self) -> SizeHint {
78        match self {
79            #[cfg(feature = "http2")]
80            Self::H2(body) => body.size_hint(),
81            #[cfg(feature = "http3")]
82            Self::H3(body) => body.size_hint(),
83            Self::Boxed(body) => body.size_hint(),
84            Self::None => SizeHint::None,
85        }
86    }
87}
88
89impl From<Bytes> for RequestBody {
90    fn from(bytes: Bytes) -> Self {
91        Self::from(BoxBody::new(Full::new(bytes)))
92    }
93}
94
95impl From<BoxBody> for RequestBody {
96    fn from(body: BoxBody) -> Self {
97        Self::Boxed(body)
98    }
99}
100
101macro_rules! req_bytes_impl {
102    ($ty: ty) => {
103        impl From<$ty> for RequestBody {
104            fn from(item: $ty) -> Self {
105                Self::from(Bytes::from(item))
106            }
107        }
108    };
109}
110
111req_bytes_impl!(&'static [u8]);
112req_bytes_impl!(Box<[u8]>);
113req_bytes_impl!(Vec<u8>);
114req_bytes_impl!(String);
115
116/// type erased body. This is a `!Send` box, unlike `http_body_util::UnsyncBoxBody`.
117pub struct BoxBody(Pin<Box<dyn Body<Data = Bytes, Error = BodyError>>>);
118
119impl Default for BoxBody {
120    fn default() -> Self {
121        Self::new(Empty::<Bytes>::new())
122    }
123}
124
125impl BoxBody {
126    #[inline]
127    pub fn new<B>(body: B) -> Self
128    where
129        B: Body + 'static,
130        B::Data: Into<Bytes> + 'static,
131        B::Error: Into<BodyError> + 'static,
132    {
133        pin_project! {
134            struct MapBody<B> {
135                #[pin]
136                body: B
137            }
138        }
139
140        impl<B, T, E> Body for MapBody<B>
141        where
142            B: Body<Data = T, Error = E>,
143            T: Into<Bytes>,
144            E: Into<BodyError>,
145        {
146            type Data = Bytes;
147            type Error = BodyError;
148
149            #[inline]
150            fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, BodyError>>> {
151                self.project()
152                    .body
153                    .poll_frame(cx)
154                    .map_ok(|frame| match frame {
155                        Frame::Data(data) => Frame::Data(data.into()),
156                        Frame::Trailers(trailers) => Frame::Trailers(trailers),
157                    })
158                    .map_err(Into::into)
159            }
160
161            #[inline]
162            fn is_end_stream(&self) -> bool {
163                self.body.is_end_stream()
164            }
165
166            #[inline]
167            fn size_hint(&self) -> SizeHint {
168                self.body.size_hint()
169            }
170        }
171
172        Self(Box::pin(MapBody { body }))
173    }
174}
175
176impl Body for BoxBody {
177    type Data = Bytes;
178    type Error = BodyError;
179
180    #[inline]
181    fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, BodyError>>> {
182        self.get_mut().0.as_mut().poll_frame(cx)
183    }
184
185    #[inline]
186    fn is_end_stream(&self) -> bool {
187        self.0.is_end_stream()
188    }
189
190    #[inline]
191    fn size_hint(&self) -> SizeHint {
192        self.0.size_hint()
193    }
194}
195
196pin_project! {
197    /// A unified response body type.
198    /// Generic type is for custom pinned response body(type implement [Body](http_body::Body)).
199    pub struct ResponseBody<B = BoxBody> {
200        #[pin]
201        inner: ResponseBodyInner<B>
202    }
203}
204
205pin_project! {
206    #[project = ResponseBodyProj]
207    #[project_replace = ResponseBodyProjReplace]
208    enum ResponseBodyInner<B> {
209        None,
210        Bytes {
211            bytes: Bytes,
212        },
213        Body {
214            #[pin]
215            stream: B,
216        },
217    }
218}
219
220impl<B> Default for ResponseBody<B> {
221    fn default() -> Self {
222        Self::empty()
223    }
224}
225
226impl ResponseBody {
227    /// Construct a new Body variant of ResponseBody with default type as [BoxBody]
228    #[inline]
229    pub fn boxed<B, T, E>(body: B) -> Self
230    where
231        B: Body<Data = T, Error = E> + 'static,
232        T: Into<Bytes> + 'static,
233        E: Into<BodyError> + 'static,
234    {
235        Self::body(BoxBody::new(body))
236    }
237}
238
239impl<B> ResponseBody<B> {
240    /// indicate empty body is attached to response.
241    /// `content-length: 0` header would be added to response when [BodySize] is
242    /// used for inferring response body type.
243    #[inline]
244    pub const fn empty() -> Self {
245        Self {
246            inner: ResponseBodyInner::None,
247        }
248    }
249
250    /// Construct a new Body variant of ResponseBody
251    #[inline]
252    pub const fn body(stream: B) -> Self {
253        Self {
254            inner: ResponseBodyInner::Body { stream },
255        }
256    }
257
258    /// Construct a new Bytes variant of ResponseBody
259    #[inline]
260    pub fn bytes<B2>(bytes: B2) -> Self
261    where
262        Bytes: From<B2>,
263    {
264        Self {
265            inner: ResponseBodyInner::Bytes {
266                bytes: Bytes::from(bytes),
267            },
268        }
269    }
270
271    /// erase generic body type by boxing the variant.
272    #[inline]
273    pub fn into_boxed<T, E>(self) -> ResponseBody
274    where
275        B: Body<Data = T, Error = E> + 'static,
276        T: Into<Bytes> + Buf + 'static,
277        E: error::Error + Send + Sync + 'static,
278    {
279        match self.inner {
280            ResponseBodyInner::None => ResponseBody::empty(),
281            ResponseBodyInner::Bytes { bytes } => ResponseBody::bytes(bytes),
282            ResponseBodyInner::Body { stream } => ResponseBody::boxed(stream),
283        }
284    }
285}
286
287impl<B, E> Body for ResponseBody<B>
288where
289    B: Body<Data = Bytes, Error = E>,
290{
291    type Data = Bytes;
292    type Error = E;
293
294    fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, E>>> {
295        let mut inner = self.project().inner;
296        match inner.as_mut().project() {
297            ResponseBodyProj::None => Poll::Ready(None),
298            ResponseBodyProj::Bytes { .. } => match inner.project_replace(ResponseBodyInner::None) {
299                ResponseBodyProjReplace::Bytes { bytes } => Poll::Ready(Some(Ok(Frame::Data(bytes)))),
300                _ => unreachable!(),
301            },
302            ResponseBodyProj::Body { stream } => stream.poll_frame(cx),
303        }
304    }
305
306    fn is_end_stream(&self) -> bool {
307        match self.inner {
308            ResponseBodyInner::None => true,
309            // see poll_frame method for reason. bytes variant always yield once on poll
310            ResponseBodyInner::Bytes { .. } => false,
311            ResponseBodyInner::Body { ref stream } => stream.is_end_stream(),
312        }
313    }
314
315    fn size_hint(&self) -> SizeHint {
316        match self.inner {
317            ResponseBodyInner::None => SizeHint::None,
318            ResponseBodyInner::Bytes { ref bytes } => SizeHint::Exact(bytes.len() as u64),
319            ResponseBodyInner::Body { ref stream } => stream.size_hint(),
320        }
321    }
322}
323
324impl From<BoxBody> for ResponseBody {
325    fn from(body: BoxBody) -> Self {
326        Self::boxed(body)
327    }
328}
329
330macro_rules! res_bytes_impl {
331    ($ty: ty) => {
332        impl<B> From<$ty> for ResponseBody<B> {
333            fn from(item: $ty) -> Self {
334                Self::bytes(item)
335            }
336        }
337    };
338}
339
340res_bytes_impl!(Bytes);
341res_bytes_impl!(BytesMut);
342res_bytes_impl!(&'static [u8]);
343res_bytes_impl!(&'static str);
344res_bytes_impl!(Box<[u8]>);
345res_bytes_impl!(Vec<u8>);
346res_bytes_impl!(String);
347
348impl<B> From<Box<str>> for ResponseBody<B> {
349    fn from(str: Box<str>) -> Self {
350        Self::from(Box::<[u8]>::from(str))
351    }
352}
353
354impl<B> From<Cow<'static, str>> for ResponseBody<B> {
355    fn from(str: Cow<'static, str>) -> Self {
356        match str {
357            Cow::Owned(str) => Self::from(str),
358            Cow::Borrowed(str) => Self::from(str),
359        }
360    }
361}