1use crate::error::{BoxError, Error};
12use bytes::Bytes;
13use std::fmt;
14
15#[doc(hidden)]
17pub use http_body::Body as HttpBody;
18
19pub type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Error>;
28
29pub type BoxBodySync = http_body_util::combinators::BoxBody<Bytes, Error>;
34
35pub fn boxed<B>(body: B) -> BoxBody
42where
43 B: http_body::Body<Data = Bytes> + Send + 'static,
44 B::Error: Into<BoxError>,
45{
46 use http_body_util::BodyExt;
47
48 try_downcast(body).unwrap_or_else(|body| body.map_err(Error::new).boxed_unsync())
49}
50
51pub fn boxed_sync<B>(body: B) -> BoxBodySync
53where
54 B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
55 B::Error: Into<BoxError>,
56{
57 use http_body_util::BodyExt;
58 body.map_err(Error::new).boxed()
59}
60
61#[doc(hidden)]
62pub(crate) fn try_downcast<T, K>(k: K) -> Result<T, K>
63where
64 T: 'static,
65 K: Send + 'static,
66{
67 let mut k = Some(k);
68 if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k) {
69 Ok(k.take().unwrap())
70 } else {
71 Err(k.unwrap())
72 }
73}
74
75pub fn empty() -> BoxBody {
77 boxed(http_body_util::Empty::<Bytes>::new())
78}
79
80pub fn empty_sync() -> BoxBodySync {
82 boxed_sync(http_body_util::Empty::<Bytes>::new())
83}
84
85#[doc(hidden)]
89pub fn to_boxed<B>(body: B) -> BoxBody
90where
91 B: Into<Bytes>,
92{
93 boxed(http_body_util::Full::new(body.into()))
94}
95
96#[doc(hidden)]
100pub fn to_boxed_sync<B>(body: B) -> BoxBodySync
101where
102 B: Into<Bytes>,
103{
104 boxed_sync(http_body_util::Full::new(body.into()))
105}
106
107pub fn from_bytes(bytes: Bytes) -> BoxBody {
109 boxed(http_body_util::Full::new(bytes))
110}
111
112#[doc(hidden)]
121#[derive(Debug, Clone, Copy)]
122pub struct BodyLimitExceeded {
123 pub limit: usize,
125}
126
127impl fmt::Display for BodyLimitExceeded {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(
130 f,
131 "request body exceeded the configured maximum of {} bytes",
132 self.limit
133 )
134 }
135}
136
137impl std::error::Error for BodyLimitExceeded {}
138
139#[doc(hidden)]
146#[derive(Debug)]
147pub enum CollectBodyError<E> {
148 Body(E),
150 TooLarge(BodyLimitExceeded),
152}
153
154impl<E: fmt::Display> fmt::Display for CollectBodyError<E> {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 Self::Body(e) => write!(f, "error reading request body: {e}"),
158 Self::TooLarge(e) => e.fmt(f),
159 }
160 }
161}
162
163impl<E: std::error::Error + 'static> std::error::Error for CollectBodyError<E> {
164 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
165 match self {
166 Self::Body(e) => Some(e),
167 Self::TooLarge(e) => Some(e),
168 }
169 }
170}
171
172#[doc(hidden)]
182pub async fn collect_body_limited<B>(body: B, limit: usize) -> Result<Bytes, CollectBodyError<B::Error>>
183where
184 B: HttpBody,
185{
186 use http_body_util::BodyExt;
187
188 if limit == 0 {
189 return body
190 .collect()
191 .await
192 .map(|c| c.to_bytes())
193 .map_err(CollectBodyError::Body);
194 }
195
196 let lower = body.size_hint().lower() as usize;
200 if lower > limit {
201 return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
202 }
203
204 let mut body = std::pin::pin!(body);
205 let mut collected = bytes::BytesMut::with_capacity(lower);
206 while let Some(frame) = std::future::poll_fn(|cx| body.as_mut().poll_frame(cx))
207 .await
208 .transpose()
209 .map_err(CollectBodyError::Body)?
210 {
211 if let Ok(data) = frame.into_data() {
212 use bytes::{Buf, BufMut};
213 let data_len = data.remaining();
214 if collected.len().saturating_add(data_len) > limit {
215 return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
216 }
217 collected.put(data);
218 }
219 }
221
222 Ok(collected.freeze())
223}
224
225pub fn wrap_stream<S, O, E>(stream: S) -> BoxBody
241where
242 S: futures_util::Stream<Item = Result<O, E>> + Send + 'static,
243 O: Into<Bytes> + 'static,
244 E: Into<BoxError> + 'static,
245{
246 use futures_util::TryStreamExt;
247 use http_body_util::StreamBody;
248
249 let frame_stream = stream
251 .map_ok(|chunk| http_body::Frame::data(chunk.into()))
252 .map_err(|e| Error::new(e.into()));
253
254 boxed(StreamBody::new(frame_stream))
255}
256
257pub fn wrap_stream_sync<S, O, E>(stream: S) -> BoxBodySync
265where
266 S: futures_util::Stream<Item = Result<O, E>> + Send + Sync + 'static,
267 O: Into<Bytes> + 'static,
268 E: Into<BoxError> + 'static,
269{
270 use futures_util::TryStreamExt;
271 use http_body_util::StreamBody;
272
273 let frame_stream = stream
275 .map_ok(|chunk| http_body::Frame::data(chunk.into()))
276 .map_err(|e| Error::new(e.into()));
277
278 boxed_sync(StreamBody::new(frame_stream))
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 async fn collect_bytes<B>(body: B) -> Result<Bytes, Error>
290 where
291 B: HttpBody,
292 B::Error: Into<BoxError>,
293 {
294 use http_body_util::BodyExt;
295
296 let collected = body.collect().await.map_err(Error::new)?;
297 Ok(collected.to_bytes())
298 }
299
300 #[tokio::test]
301 async fn test_empty_body() {
302 let body = empty();
303 let bytes = collect_bytes(body).await.unwrap();
304 assert_eq!(bytes.len(), 0);
305 }
306
307 #[tokio::test]
308 async fn test_from_bytes() {
309 let data = Bytes::from("hello world");
310 let body = from_bytes(data.clone());
311 let collected = collect_bytes(body).await.unwrap();
312 assert_eq!(collected, data);
313 }
314
315 #[tokio::test]
316 async fn test_to_boxed_string() {
317 let s = "hello world";
318 let body = to_boxed(s);
319 let collected = collect_bytes(body).await.unwrap();
320 assert_eq!(collected, Bytes::from(s));
321 }
322
323 #[tokio::test]
324 async fn test_to_boxed_vec() {
325 let vec = vec![1u8, 2, 3, 4, 5];
326 let body = to_boxed(vec.clone());
327 let collected = collect_bytes(body).await.unwrap();
328 assert_eq!(collected.as_ref(), vec.as_slice());
329 }
330
331 #[tokio::test]
332 async fn test_boxed() {
333 use http_body_util::Full;
334 let full_body = Full::new(Bytes::from("test data"));
335 let boxed_body: BoxBody = boxed(full_body);
336 let collected = collect_bytes(boxed_body).await.unwrap();
337 assert_eq!(collected, Bytes::from("test data"));
338 }
339
340 #[tokio::test]
341 async fn test_boxed_sync() {
342 use http_body_util::Full;
343 let full_body = Full::new(Bytes::from("sync test"));
344 let boxed_body: BoxBodySync = boxed_sync(full_body);
345 let collected = collect_bytes(boxed_body).await.unwrap();
346 assert_eq!(collected, Bytes::from("sync test"));
347 }
348
349 #[tokio::test]
350 async fn test_wrap_stream_single_chunk() {
351 use futures_util::stream;
352
353 let data = Bytes::from("single chunk");
354 let stream = stream::iter(vec![Ok::<_, std::io::Error>(data.clone())]);
355
356 let body = wrap_stream(stream);
357 let collected = collect_bytes(body).await.unwrap();
358 assert_eq!(collected, data);
359 }
360
361 #[tokio::test]
362 async fn test_wrap_stream_multiple_chunks() {
363 use futures_util::stream;
364
365 let chunks = vec![
366 Ok::<_, std::io::Error>(Bytes::from("chunk1")),
367 Ok(Bytes::from("chunk2")),
368 Ok(Bytes::from("chunk3")),
369 ];
370 let expected = Bytes::from("chunk1chunk2chunk3");
371
372 let stream = stream::iter(chunks);
373 let body = wrap_stream(stream);
374 let collected = collect_bytes(body).await.unwrap();
375 assert_eq!(collected, expected);
376 }
377
378 #[tokio::test]
379 async fn test_wrap_stream_empty() {
380 use futures_util::stream;
381
382 let stream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::new())]);
383
384 let body = wrap_stream(stream);
385 let collected = collect_bytes(body).await.unwrap();
386 assert_eq!(collected.len(), 0);
387 }
388
389 #[tokio::test]
390 async fn test_wrap_stream_error() {
391 use futures_util::stream;
392
393 let chunks = vec![
394 Ok::<_, std::io::Error>(Bytes::from("chunk1")),
395 Err(std::io::Error::other("test error")),
396 ];
397
398 let stream = stream::iter(chunks);
399 let body = wrap_stream(stream);
400 let result = collect_bytes(body).await;
401 assert!(result.is_err());
402 }
403
404 #[tokio::test]
405 async fn test_wrap_stream_various_types() {
406 use futures_util::stream;
407
408 let chunks = vec![Ok::<_, std::io::Error>("string slice"), Ok("another string")];
412 let stream = stream::iter(chunks);
413 let body = wrap_stream(stream);
414 let collected = collect_bytes(body).await.unwrap();
415 assert_eq!(collected, Bytes::from("string sliceanother string"));
416
417 let chunks = vec![
419 Ok::<_, std::io::Error>(String::from("owned ")),
420 Ok(String::from("strings")),
421 ];
422 let stream = stream::iter(chunks);
423 let body = wrap_stream(stream);
424 let collected = collect_bytes(body).await.unwrap();
425 assert_eq!(collected, Bytes::from("owned strings"));
426
427 let chunks = vec![
429 Ok::<_, std::io::Error>(vec![72u8, 101, 108, 108, 111]), Ok(vec![32u8, 87, 111, 114, 108, 100]), ];
432 let stream = stream::iter(chunks);
433 let body = wrap_stream(stream);
434 let collected = collect_bytes(body).await.unwrap();
435 assert_eq!(collected, Bytes::from("Hello World"));
436
437 let chunks = vec![
439 Ok::<_, std::io::Error>(&[98u8, 121, 116, 101] as &[u8]), Ok(&[115u8, 33] as &[u8]), ];
442 let stream = stream::iter(chunks);
443 let body = wrap_stream(stream);
444 let collected = collect_bytes(body).await.unwrap();
445 assert_eq!(collected, Bytes::from("bytes!"));
446
447 struct CustomChunk {
449 data: String,
450 }
451
452 impl From<CustomChunk> for Bytes {
453 fn from(chunk: CustomChunk) -> Bytes {
454 Bytes::from(chunk.data)
455 }
456 }
457
458 let chunks = vec![
459 Ok::<_, std::io::Error>(CustomChunk { data: "custom ".into() }),
460 Ok(CustomChunk { data: "struct".into() }),
461 ];
462 let stream = stream::iter(chunks);
463 let body = wrap_stream(stream);
464 let collected = collect_bytes(body).await.unwrap();
465 assert_eq!(collected, Bytes::from("custom struct"));
466 }
467
468 #[tokio::test]
469 async fn test_wrap_stream_custom_stream_type() {
470 use bytes::Bytes;
471 use std::pin::Pin;
472 use std::task::{Context, Poll};
473
474 struct CustomStream {
476 chunks: Vec<Result<Bytes, std::io::Error>>,
477 }
478
479 impl CustomStream {
480 fn new(chunks: Vec<Result<Bytes, std::io::Error>>) -> Self {
481 Self { chunks }
482 }
483 }
484
485 impl futures_util::Stream for CustomStream {
486 type Item = Result<Bytes, std::io::Error>;
487
488 fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
489 if self.chunks.is_empty() {
490 Poll::Ready(None)
491 } else {
492 Poll::Ready(Some(self.chunks.remove(0)))
493 }
494 }
495 }
496
497 let stream = CustomStream::new(vec![Ok(Bytes::from("custom ")), Ok(Bytes::from("stream"))]);
498
499 let body = wrap_stream(stream);
500 let collected = collect_bytes(body).await.unwrap();
501 assert_eq!(collected, Bytes::from("custom stream"));
502 }
503
504 #[tokio::test]
505 async fn test_wrap_stream_custom_error_type() {
506 use bytes::Bytes;
507 use futures_util::stream;
508
509 #[derive(Debug, Clone)]
511 struct CustomError {
512 message: String,
513 }
514
515 impl std::fmt::Display for CustomError {
516 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
517 write!(f, "CustomError: {}", self.message)
518 }
519 }
520
521 impl std::error::Error for CustomError {}
522
523 let chunks = vec![
525 Ok::<_, CustomError>(Bytes::from("custom ")),
526 Ok(Bytes::from("error type")),
527 ];
528 let stream = stream::iter(chunks);
529 let body = wrap_stream(stream);
530 let collected = collect_bytes(body).await.unwrap();
531 assert_eq!(collected, Bytes::from("custom error type"));
532
533 let chunks = vec![
535 Ok::<_, CustomError>(Bytes::from("data")),
536 Err(CustomError {
537 message: "custom error".into(),
538 }),
539 ];
540 let stream = stream::iter(chunks);
541 let body = wrap_stream(stream);
542 let result = collect_bytes(body).await;
543 assert!(result.is_err());
544 }
545
546 #[tokio::test]
547 async fn test_wrap_stream_incremental_consumption() {
548 use bytes::Bytes;
549 use http_body_util::BodyExt;
550 use std::pin::Pin;
551 use std::task::{Context, Poll};
552
553 struct IncrementalStream {
554 chunks: Vec<Result<Bytes, std::io::Error>>,
555 }
556
557 impl IncrementalStream {
558 fn new(chunks: Vec<Result<Bytes, std::io::Error>>) -> Self {
559 Self { chunks }
560 }
561 }
562
563 impl futures_util::Stream for IncrementalStream {
564 type Item = Result<Bytes, std::io::Error>;
565
566 fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
567 if self.chunks.is_empty() {
568 Poll::Ready(None)
569 } else {
570 Poll::Ready(Some(self.chunks.remove(0)))
571 }
572 }
573 }
574
575 let stream = IncrementalStream::new(vec![
576 Ok(Bytes::from("chunk1")),
577 Ok(Bytes::from("chunk2")),
578 Ok(Bytes::from("chunk3")),
579 ]);
580
581 let mut body = wrap_stream(stream);
582
583 let frame1 = body.frame().await.unwrap().unwrap();
584 assert!(frame1.is_data());
585 assert_eq!(frame1.into_data().unwrap(), Bytes::from("chunk1"));
586
587 let frame2 = body.frame().await.unwrap().unwrap();
588 assert!(frame2.is_data());
589 assert_eq!(frame2.into_data().unwrap(), Bytes::from("chunk2"));
590
591 let frame3 = body.frame().await.unwrap().unwrap();
592 assert!(frame3.is_data());
593 assert_eq!(frame3.into_data().unwrap(), Bytes::from("chunk3"));
594
595 let frame4 = body.frame().await;
596 assert!(frame4.is_none());
597 }
598
599 #[tokio::test]
600 async fn test_wrap_stream_sync_single_chunk() {
601 use futures_util::stream;
602
603 let data = Bytes::from("sync single chunk");
604 let stream = stream::iter(vec![Ok::<_, std::io::Error>(data.clone())]);
605
606 let body = wrap_stream_sync(stream);
607 let collected = collect_bytes(body).await.unwrap();
608 assert_eq!(collected, data);
609 }
610
611 #[tokio::test]
612 async fn test_wrap_stream_sync_multiple_chunks() {
613 use futures_util::stream;
614
615 let chunks = vec![
616 Ok::<_, std::io::Error>(Bytes::from("sync1")),
617 Ok(Bytes::from("sync2")),
618 Ok(Bytes::from("sync3")),
619 ];
620 let expected = Bytes::from("sync1sync2sync3");
621
622 let stream = stream::iter(chunks);
623 let body = wrap_stream_sync(stream);
624 let collected = collect_bytes(body).await.unwrap();
625 assert_eq!(collected, expected);
626 }
627
628 #[tokio::test]
629 async fn test_empty_sync_body() {
630 let body = empty_sync();
631 let bytes = collect_bytes(body).await.unwrap();
632 assert_eq!(bytes.len(), 0);
633 }
634
635 #[tokio::test]
636 async fn test_to_boxed_sync() {
637 let data = Bytes::from("sync boxed data");
638 let body = to_boxed_sync(data.clone());
639 let collected = collect_bytes(body).await.unwrap();
640 assert_eq!(collected, data);
641 }
642
643 fn _assert_send<T: Send>() {}
646 fn _assert_sync<T: Sync>() {}
647
648 fn _assert_send_sync_bounds() {
649 _assert_send::<BoxBodySync>();
651 _assert_sync::<BoxBodySync>();
652
653 _assert_send::<BoxBody>();
655 }
656
657 #[tokio::test]
658 async fn test_wrap_stream_sync_produces_sync_body() {
659 use futures_util::stream;
660
661 let data = Bytes::from("test sync");
662 let stream = stream::iter(vec![Ok::<_, std::io::Error>(data.clone())]);
663
664 let body = wrap_stream_sync(stream);
665
666 fn check_sync<T: Sync>(_: &T) {}
668 check_sync(&body);
669
670 let collected = collect_bytes(body).await.unwrap();
671 assert_eq!(collected, data);
672 }
673
674 #[test]
675 fn test_empty_sync_is_sync() {
676 let body = empty_sync();
677 fn check_sync<T: Sync>(_: &T) {}
678 check_sync(&body);
679 }
680
681 #[test]
682 fn test_boxed_sync_is_sync() {
683 use http_body_util::Full;
684 let body = boxed_sync(Full::new(Bytes::from("test")));
685 fn check_sync<T: Sync>(_: &T) {}
686 check_sync(&body);
687 }
688
689 #[tokio::test]
690 async fn test_collect_body_limited_under_limit() {
691 let body = from_bytes(Bytes::from("hello"));
692 let result = collect_body_limited(body, 1024).await;
693 assert_eq!(result.unwrap(), Bytes::from("hello"));
694 }
695
696 #[tokio::test]
697 async fn test_collect_body_limited_over_limit() {
698 let body = from_bytes(Bytes::from("this is way too long"));
699 let result = collect_body_limited(body, 5).await;
700 assert!(matches!(
701 result,
702 Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit: 5 }))
703 ));
704 }
705
706 #[tokio::test]
707 async fn test_collect_body_limited_chunked_exceeds_mid_stream() {
708 use futures_util::stream;
709
710 let chunks = vec![
712 Ok::<_, std::io::Error>(http_body::Frame::data(Bytes::from("aaaa"))), Ok(http_body::Frame::data(Bytes::from("bbbb"))), ];
715 let body = http_body_util::StreamBody::new(stream::iter(chunks));
716 let result = collect_body_limited(body, 6).await;
717 assert!(matches!(result, Err(CollectBodyError::TooLarge(_))));
718 }
719
720 #[tokio::test]
721 async fn test_collect_body_limited_empty_body() {
722 let body = empty();
723 let result = collect_body_limited(body, 100).await;
724 assert_eq!(result.unwrap(), Bytes::from(""));
725 }
726}