web_static_pack/
body.rs

1//! [http] / [http_body] crate abstractions. Provides [Body], implementing
2//! [HttpBody] for raw bytes slice.
3
4use http_body::{Body as HttpBody, Frame, SizeHint};
5use std::{
6    convert::Infallible,
7    pin::Pin,
8    task::{Context, Poll},
9};
10
11/// [HttpBody] implementation for body consisting of a single in-memory slice.
12/// Implementation is based on `http_body_util::Full`.
13///
14/// Please note that once body is used, eg. polled with [Self::poll_frame], it
15/// will become empty, eg. [Self::data] will return empty slice.
16#[derive(Debug)]
17pub struct Body<'a> {
18    // None(empty slice) is not allowed.
19    data: Option<&'a [u8]>,
20}
21impl<'a> Body<'a> {
22    /// Creates [self] from data.
23    pub fn new(data: &'a [u8]) -> Self {
24        let data = if !data.is_empty() { Some(data) } else { None };
25        Self { data }
26    }
27
28    /// Creates empty [self].
29    pub fn empty() -> Self {
30        let data = None;
31        Self { data }
32    }
33
34    /// Returns remaining data.
35    ///
36    /// This will return original content until polled with [Self::poll_frame],
37    /// then it will return empty slice.
38    pub fn data(&self) -> &'a [u8] {
39        self.data.unwrap_or(b"")
40    }
41}
42impl<'a> HttpBody for Body<'a> {
43    type Data = &'a [u8];
44    type Error = Infallible;
45
46    fn poll_frame(
47        self: Pin<&mut Self>,
48        _cx: &mut Context<'_>,
49    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
50        let self_ = unsafe { self.get_unchecked_mut() };
51        let data = self_.data.take();
52
53        match data {
54            Some(data) => Poll::Ready(Some(Ok(Frame::data(data)))),
55            None => Poll::Ready(None),
56        }
57    }
58
59    fn is_end_stream(&self) -> bool {
60        self.data.is_none()
61    }
62
63    fn size_hint(&self) -> SizeHint {
64        match self.data {
65            Some(data) => SizeHint::with_exact(data.len() as u64),
66            None => SizeHint::with_exact(0),
67        }
68    }
69}
70
71#[cfg(test)]
72mod test_body {
73    use super::Body as Body_;
74    use http_body::Body;
75    use http_body_util::combinators::BoxBody;
76
77    /// we want to keep our body to be compatible with [BoxBody]
78    #[test]
79    fn body_converts_to_box_body() {
80        let body = Body_::new(b"foo");
81        let box_body = BoxBody::new(body);
82
83        assert_eq!(box_body.size_hint().lower(), 3);
84    }
85}