titan_http/
body.rs

1use std::{
2  convert::Infallible,
3  pin::{pin, Pin},
4  task::Poll,
5};
6
7use bytes::Bytes;
8use futures_core::Stream;
9use http_body::{Frame, SizeHint};
10
11/// Represents the body of an HTTP response.
12///
13/// The `Body` enum can either represent a fully-loaded body in memory or a stream of data.
14/// It is used as the response body when implementing the `Respondable` trait, allowing
15/// handlers to return either a complete body or a body that streams data incrementally.
16///
17/// # Variants
18///
19/// - `Full(Box<[u8]>)`:
20///   This variant holds the full body of the response in memory as a boxed byte slice. This is typically used
21///   when the response body is small enough to be loaded entirely into memory at once.
22///   
23/// - `Stream(Pin<Box<dyn Stream<Item = Vec<u8>> + Send>>)`:
24///   This variant represents a streaming body, where the body is returned incrementally in chunks. This is useful
25///   when dealing with large responses (e.g., files or large datasets) that should be sent in multiple parts.
26///   The stream yields `Vec<u8>` chunks, allowing the receiver to process the data incrementally as it arrives.
27///
28/// This enum is not usually used of the library user and is quite low-level.
29pub enum Body {
30  Full(Box<[u8]>),
31  Stream(Pin<Box<dyn Stream<Item = Vec<u8>> + Send>>),
32}
33
34impl http_body::Body for Body {
35  type Data = Bytes;
36  type Error = Infallible;
37
38  fn size_hint(&self) -> http_body::SizeHint {
39    match self {
40      Body::Full(value) => SizeHint::with_exact(value.len() as u64),
41      Body::Stream(body) => {
42        let (lower, higher) = body.size_hint();
43        let mut size_hint = SizeHint::default();
44        size_hint.set_lower(lower as u64);
45        if let Some(higher) = higher {
46          size_hint.set_upper(higher as u64);
47        }
48        size_hint
49      }
50    }
51  }
52
53  fn poll_frame(
54    self: Pin<&mut Self>,
55    cx: &mut std::task::Context<'_>,
56  ) -> std::task::Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>>
57  {
58    match self.get_mut() {
59      Body::Full(body) => {
60        Poll::Ready(Some(Ok(Frame::data(Bytes::from(body.clone())))))
61      }
62      Body::Stream(body) => {
63        let value = pin!(body);
64        let value = match value.poll_next(cx) {
65          Poll::Pending => return Poll::Pending,
66          Poll::Ready(value) => match value {
67            None => return Poll::Ready(None),
68            Some(value) => value,
69          },
70        };
71
72        let frame = Frame::data(Bytes::from(value));
73        Poll::Ready(Some(Ok(frame)))
74      }
75    }
76  }
77
78  fn is_end_stream(&self) -> bool {
79    // TODO
80    true
81  }
82}
83
84impl From<String> for Body {
85  fn from(value: String) -> Self {
86    Self::Full(value.as_bytes().into())
87  }
88}
89
90impl From<()> for Body {
91  fn from(_: ()) -> Self {
92    Self::Full([].into())
93  }
94}
95
96impl<'a> From<&'a str> for Body {
97  fn from(value: &'a str) -> Self {
98    Self::Full(value.as_bytes().into())
99  }
100}
101
102impl From<Box<[u8]>> for Body {
103  fn from(value: Box<[u8]>) -> Self {
104    Self::Full(value)
105  }
106}
107
108impl From<Vec<u8>> for Body {
109  fn from(value: Vec<u8>) -> Self {
110    Self::Full(value.into())
111  }
112}
113
114impl From<&'_ [u8]> for Body {
115  fn from(value: &'_ [u8]) -> Self {
116    Self::Full(value.into())
117  }
118}
119
120//impl From<StatusCode> for Body {
121//  fn from(value: StatusCode) -> Self {
122//    match value {
123//      StatusCode::OK => "Ok",
124//      StatusCode::BAD_REQUEST => "Bad Request",
125//      _ => panic!("no"),
126//    }
127//    .into()
128//  }
129//}
130
131//impl From<Body> for String {
132//  fn from(value: Body) -> Self {
133//    match value {
134//      Body::Full(bytes) => unsafe {
135//        String::from_utf8_unchecked(bytes.to_vec())
136//      },
137//    }
138//  }
139//}
140
141macro_rules! impl_tostring {
142  ($( $type:ident )*) => {
143    $(impl From<$type> for Body {
144          fn from(value: $type) -> Self {
145            let body_str = value.to_string();
146            Self::Full(body_str.as_bytes().into())
147          }
148    })*
149  };
150}
151
152impl_tostring! { usize i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 }