1use std::{
2 any::Any,
3 convert::Infallible,
4 fmt,
5 panic::{AssertUnwindSafe, catch_unwind},
6 pin::Pin,
7 task::{Context, Poll},
8};
9
10use bytes::Bytes;
11use http_body::{Frame, SizeHint};
12use http_body_util::{
13 BodyExt, BodyStream, Empty, Full, LengthLimitError, Limited, combinators::UnsyncBoxBody,
14};
15use topcoat_core::error::Result;
16
17use crate::error::{bad_request, content_too_large};
18
19pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
21
22#[must_use]
27pub struct Body(UnsyncBoxBody<Bytes, BoxError>);
28
29impl Body {
30 pub fn empty() -> Self {
32 Self::from(Bytes::new())
33 }
34
35 pub fn new<B>(body: B) -> Self
37 where
38 B: http_body::Body<Data = Bytes> + Send + 'static,
39 B::Error: Into<BoxError>,
40 {
41 Self(body.map_err(Into::into).boxed_unsync())
42 }
43
44 pub fn into_data_stream(self) -> BodyDataStream {
47 BodyDataStream(BodyStream::new(self))
48 }
49}
50
51impl Default for Body {
52 fn default() -> Self {
53 Self::empty()
54 }
55}
56
57impl From<Bytes> for Body {
58 fn from(bytes: Bytes) -> Self {
59 Self(
60 Full::new(bytes)
61 .map_err(|never: Infallible| match never {})
62 .boxed_unsync(),
63 )
64 }
65}
66
67impl From<()> for Body {
68 fn from((): ()) -> Self {
69 Self(
70 Empty::new()
71 .map_err(|never: Infallible| match never {})
72 .boxed_unsync(),
73 )
74 }
75}
76
77impl From<Vec<u8>> for Body {
78 fn from(value: Vec<u8>) -> Self {
79 Self::from(Bytes::from(value))
80 }
81}
82
83impl From<&'static [u8]> for Body {
84 fn from(value: &'static [u8]) -> Self {
85 Self::from(Bytes::from_static(value))
86 }
87}
88
89impl From<String> for Body {
90 fn from(value: String) -> Self {
91 Self::from(Bytes::from(value))
92 }
93}
94
95impl From<&'static str> for Body {
96 fn from(value: &'static str) -> Self {
97 Self::from(Bytes::from_static(value.as_bytes()))
98 }
99}
100
101impl http_body::Body for Body {
102 type Data = Bytes;
103 type Error = BoxError;
104
105 fn poll_frame(
106 mut self: Pin<&mut Self>,
107 cx: &mut Context<'_>,
108 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
109 match catch_unwind(AssertUnwindSafe(|| Pin::new(&mut self.0).poll_frame(cx))) {
113 Ok(poll) => poll,
114 Err(payload) => {
115 self.0 = Self::from(()).0;
119 Poll::Ready(Some(Err(BodyPanicError::new(payload).into())))
120 }
121 }
122 }
123
124 fn size_hint(&self) -> SizeHint {
125 self.0.size_hint()
126 }
127
128 fn is_end_stream(&self) -> bool {
129 self.0.is_end_stream()
130 }
131}
132
133impl fmt::Debug for Body {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("Body").finish_non_exhaustive()
136 }
137}
138
139#[derive(Debug)]
145pub struct BodyPanicError {
146 message: Option<Box<str>>,
147}
148
149impl BodyPanicError {
150 fn new(payload: Box<dyn Any + Send>) -> Self {
152 let message = match payload.downcast::<String>() {
153 Ok(message) => Some(message.into_boxed_str()),
154 Err(payload) => payload
155 .downcast::<&'static str>()
156 .ok()
157 .map(|message| Box::from(*message)),
158 };
159 Self { message }
160 }
161
162 #[must_use]
164 pub fn message(&self) -> Option<&str> {
165 self.message.as_deref()
166 }
167}
168
169impl fmt::Display for BodyPanicError {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match &self.message {
172 Some(message) => write!(f, "the body panicked: {message}"),
173 None => f.write_str("the body panicked"),
174 }
175 }
176}
177
178impl std::error::Error for BodyPanicError {}
179
180#[must_use]
183pub struct BodyDataStream(BodyStream<Body>);
184
185impl futures_core::Stream for BodyDataStream {
186 type Item = Result<Bytes, BoxError>;
187
188 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
189 loop {
190 return match Pin::new(&mut self.0).poll_next(cx) {
191 Poll::Ready(Some(Ok(frame))) => match frame.into_data() {
192 Ok(data) => Poll::Ready(Some(Ok(data))),
193 Err(_frame) => continue,
194 },
195 Poll::Ready(Some(Err(error))) => Poll::Ready(Some(Err(error))),
196 Poll::Ready(None) => Poll::Ready(None),
197 Poll::Pending => Poll::Pending,
198 };
199 }
200 }
201}
202
203pub async fn to_bytes(body: Body, limit: usize) -> Result<Bytes> {
218 let collected = if limit == usize::MAX {
219 body.collect().await
220 } else {
221 Limited::new(body, limit).collect().await
222 };
223
224 match collected {
225 Ok(collected) => Ok(collected.to_bytes()),
226 Err(error) => Err(match error.downcast::<LengthLimitError>() {
227 Ok(_) => content_too_large().into(),
228 Err(error) => bad_request(format!("failed to read the body: {error}")).into(),
229 }),
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use http_body::Body as _;
236
237 use super::*;
238 use crate::error::{BadRequestError, ContentTooLargeError};
239
240 struct FailingBody;
243
244 impl http_body::Body for FailingBody {
245 type Data = Bytes;
246 type Error = BoxError;
247
248 fn poll_frame(
249 self: Pin<&mut Self>,
250 _cx: &mut Context<'_>,
251 ) -> Poll<Option<Result<Frame<Bytes>, BoxError>>> {
252 Poll::Ready(Some(Err("body stream broke".into())))
253 }
254 }
255
256 struct PanickingBody;
258
259 impl http_body::Body for PanickingBody {
260 type Data = Bytes;
261 type Error = BoxError;
262
263 fn poll_frame(
264 self: Pin<&mut Self>,
265 _cx: &mut Context<'_>,
266 ) -> Poll<Option<Result<Frame<Bytes>, BoxError>>> {
267 panic!("frame {}", 1);
268 }
269 }
270
271 #[tokio::test]
272 async fn a_body_within_the_limit_reads_in_full() {
273 let bytes = to_bytes(Body::from("hello"), 1024).await.unwrap();
274 assert_eq!(bytes, Bytes::from_static(b"hello"));
275 }
276
277 #[tokio::test]
278 async fn usize_max_reads_without_enforcing_a_limit() {
279 let bytes = to_bytes(Body::from("hello"), usize::MAX).await.unwrap();
280 assert_eq!(bytes, Bytes::from_static(b"hello"));
281 }
282
283 #[tokio::test]
284 async fn a_body_over_the_limit_is_content_too_large() {
285 let error = to_bytes(Body::from("hello"), 4).await.unwrap_err();
286 assert!(error.is::<ContentTooLargeError>());
287 }
288
289 #[tokio::test]
290 async fn a_panicking_body_ends_its_stream_with_an_error() {
291 let mut body = Body::new(PanickingBody);
292 assert!(!body.is_end_stream());
293
294 let error = body.frame().await.unwrap().unwrap_err();
295 let error = error
296 .downcast::<BodyPanicError>()
297 .expect("a panic ends the stream with its own error");
298 assert_eq!(error.message(), Some("frame 1"));
299 assert_eq!(error.to_string(), "the body panicked: frame 1");
300
301 assert!(body.is_end_stream());
304 assert!(body.frame().await.is_none());
305 }
306
307 #[tokio::test]
308 async fn a_read_failure_is_a_bad_request_carrying_the_cause() {
309 for limit in [usize::MAX, 1024] {
312 let error = to_bytes(Body::new(FailingBody), limit).await.unwrap_err();
313 let error = error
314 .downcast::<BadRequestError>()
315 .expect("a read failure is a bad request");
316 assert_eq!(
317 error.description(),
318 "failed to read the body: body stream broke"
319 );
320 }
321 }
322}