1mod framed;
2#[cfg(feature = "unstable")]
3use crate::h3::H3Body;
4use crate::{Headers, HttpConfig, h2::H2Body};
5use BodyType::{Empty, Static, Streaming};
6pub use framed::BodyFraming;
7use futures_lite::{AsyncRead, io::Cursor, ready};
8use pin_project_lite::pin_project;
9use std::{
10 borrow::Cow,
11 fmt::{self, Debug, Formatter},
12 io::{Error, Result},
13 pin::Pin,
14 sync::Arc,
15 task::{Context, Poll},
16};
17use sync_wrapper::SyncWrapper;
18
19pub trait BodySource: AsyncRead + Send + 'static {
26 fn trailers(self: Pin<&mut Self>) -> Option<Headers>;
31}
32
33pin_project! {
34 struct PlainBody<T> {
35 #[pin]
36 async_read: T,
37 }
38}
39
40impl<T: AsyncRead> AsyncRead for PlainBody<T> {
41 fn poll_read(
42 self: Pin<&mut Self>,
43 cx: &mut Context<'_>,
44 buf: &mut [u8],
45 ) -> Poll<Result<usize>> {
46 self.project().async_read.poll_read(cx, buf)
47 }
48}
49
50impl<T: AsyncRead + Send + 'static> BodySource for PlainBody<T> {
51 fn trailers(self: Pin<&mut Self>) -> Option<Headers> {
52 None
53 }
54}
55
56#[derive(Debug, Default)]
60pub struct Body(pub(crate) BodyType);
61
62impl Body {
63 pub fn new_streaming(async_read: impl AsyncRead + Send + 'static, len: Option<u64>) -> Self {
67 Self::new_with_trailers(PlainBody { async_read }, len)
68 }
69
70 pub fn new_with_trailers(body: impl BodySource, len: Option<u64>) -> Self {
76 Self(Streaming {
77 async_read: SyncWrapper::new(Box::pin(body)),
78 len,
79 done: false,
80 progress: 0,
81 chunked_framing: true,
82 keep_open: false,
83 })
84 }
85
86 #[doc(hidden)]
94 #[cfg(feature = "unstable")]
95 #[must_use]
96 pub fn without_chunked_framing(mut self) -> Self {
97 if let Streaming {
98 ref mut chunked_framing,
99 ..
100 } = self.0
101 {
102 *chunked_framing = false;
103 }
104 self
105 }
106
107 #[doc(hidden)]
125 #[cfg(feature = "unstable")]
126 #[must_use]
127 pub fn keep_open(mut self) -> Self {
128 if matches!(self.0, Static { .. }) {
132 let reader = std::mem::take(&mut self).into_reader();
133 self = Self::new_streaming(reader, None);
134 }
135
136 if let Streaming {
137 ref mut len,
138 ref mut chunked_framing,
139 ref mut keep_open,
140 ..
141 } = self.0
142 {
143 *len = None;
144 *chunked_framing = true;
145 *keep_open = true;
146 }
147 self
148 }
149
150 #[doc(hidden)]
156 pub fn trailers(&mut self) -> Option<Headers> {
157 match &mut self.0 {
158 Streaming {
159 async_read, done, ..
160 } if *done => async_read.get_mut().as_mut().trailers(),
161 _ => None,
162 }
163 }
164
165 pub fn new_static(content: impl Into<Cow<'static, [u8]>>) -> Self {
168 Self(Static {
169 content: StaticContent::Cow(content.into()),
170 cursor: 0,
171 })
172 }
173
174 pub fn static_bytes(&self) -> Option<&[u8]> {
178 match &self.0 {
179 Static { content, .. } => Some(content.as_ref()),
180 _ => None,
181 }
182 }
183
184 pub fn into_reader(self) -> Pin<Box<dyn AsyncRead + Send + Sync + 'static>> {
188 match self.0 {
189 Streaming { async_read, .. } => Box::pin(SyncAsyncReader(async_read)),
190 Static { content, .. } => Box::pin(Cursor::new(content)),
191 Empty => Box::pin(Cursor::new("")),
192 }
193 }
194
195 pub async fn into_bytes(self) -> Result<Cow<'static, [u8]>> {
204 match self.0 {
205 Static { content, .. } => Ok(content.into_cow()),
206
207 Streaming {
208 async_read,
209 len,
210 progress: 0,
211 done: false,
212 ..
213 } => {
214 let mut async_read = async_read.into_inner();
215 let mut buf = len
218 .and_then(|len| usize::try_from(len).ok())
219 .map_or_default(|len| {
220 Vec::with_capacity(
221 len.min(HttpConfig::DEFAULT.received_body_max_preallocate),
222 )
223 });
224
225 crate::util::read_to_end(&mut async_read, &mut buf).await?;
226
227 Ok(Cow::Owned(buf))
228 }
229
230 Empty => Ok(Cow::Borrowed(b"")),
231
232 Streaming { .. } => Err(Error::other("body already read to completion")),
233 }
234 }
235
236 pub fn bytes_read(&self) -> u64 {
239 self.0.bytes_read()
240 }
241
242 pub fn len(&self) -> Option<u64> {
245 self.0.len()
246 }
247
248 pub fn is_empty(&self) -> bool {
250 self.0.is_empty()
251 }
252
253 pub fn is_static(&self) -> bool {
255 matches!(self.0, Static { .. })
256 }
257
258 pub fn is_streaming(&self) -> bool {
260 matches!(self.0, Streaming { .. })
261 }
262
263 #[cfg(feature = "unstable")]
270 #[doc(hidden)]
271 pub fn into_body_source(self) -> Option<Pin<Box<dyn BodySource>>> {
272 match self.0 {
273 Streaming { async_read, .. } => Some(async_read.into_inner()),
274 _ => None,
275 }
276 }
277
278 #[doc(hidden)]
285 #[cfg(feature = "unstable")]
286 pub fn try_clone(&self) -> Option<Self> {
287 match &self.0 {
288 Empty => Some(Self::default()),
289 Static { content, .. } => Some(Self(Static {
290 content: content.clone(),
291 cursor: 0,
292 })),
293 Streaming { .. } => None,
294 }
295 }
296
297 #[cfg(feature = "unstable")]
304 pub fn into_h3(self) -> H3Body {
305 H3Body::new(self)
306 }
307
308 pub(crate) fn into_h2(self) -> H2Body {
315 H2Body::new(self)
316 }
317}
318
319#[allow(
320 clippy::cast_sign_loss,
321 clippy::cast_possible_truncation,
322 clippy::cast_precision_loss,
323 reason = "buffers are well below petabyte scale; log2/4 of a usize stays in f64 range, and \
324 the subtraction always yields a non-negative usize-representable value"
325)]
326fn max_bytes_to_read(buf_len: usize) -> usize {
327 assert!(
328 buf_len >= 6,
329 "buffers of length {buf_len} are too small for this implementation.
330 if this is a problem for you, please open an issue"
331 );
332
333 let bytes_remaining_after_two_cr_lns = (buf_len - 4) as f64;
334 let max_bytes_of_hex_framing = (bytes_remaining_after_two_cr_lns).log2() / 4f64;
336 (bytes_remaining_after_two_cr_lns - max_bytes_of_hex_framing.ceil()) as usize
337}
338
339impl AsyncRead for Body {
340 fn poll_read(
341 mut self: Pin<&mut Self>,
342 cx: &mut Context<'_>,
343 buf: &mut [u8],
344 ) -> Poll<Result<usize>> {
345 match &mut self.0 {
346 Empty => Poll::Ready(Ok(0)),
347 Static { content, cursor } => {
348 let length = content.len();
349 if length == *cursor {
350 return Poll::Ready(Ok(0));
351 }
352 let bytes = (length - *cursor).min(buf.len());
353 buf[0..bytes].copy_from_slice(&content[*cursor..*cursor + bytes]);
354 *cursor += bytes;
355 Poll::Ready(Ok(bytes))
356 }
357
358 Streaming {
359 async_read,
360 len: Some(len),
361 done,
362 progress,
363 ..
364 } => {
365 if *done {
366 return Poll::Ready(Ok(0));
367 }
368
369 let max_bytes_to_read = (*len - *progress)
370 .try_into()
371 .unwrap_or(buf.len())
372 .min(buf.len());
373
374 let bytes = ready!(
375 async_read
376 .get_mut()
377 .as_mut()
378 .poll_read(cx, &mut buf[..max_bytes_to_read])
379 )?;
380
381 if bytes == 0 {
382 *done = true;
383 } else {
384 *progress += bytes as u64;
385 }
386
387 Poll::Ready(Ok(bytes))
388 }
389
390 Streaming {
391 async_read,
392 len: None,
393 done,
394 progress,
395 chunked_framing,
396 keep_open,
397 } => {
398 if *done {
399 return Poll::Ready(Ok(0));
400 }
401
402 if !*chunked_framing {
403 let bytes = ready!(async_read.get_mut().as_mut().poll_read(cx, buf))?;
404 if bytes == 0 {
405 *done = true;
406 } else {
407 *progress += bytes as u64;
408 }
409 return Poll::Ready(Ok(bytes));
410 }
411
412 let max_bytes_to_read = max_bytes_to_read(buf.len());
413
414 let bytes = ready!(
415 async_read
416 .get_mut()
417 .as_mut()
418 .poll_read(cx, &mut buf[..max_bytes_to_read])
419 )?;
420
421 if bytes == 0 {
422 *done = true;
423 if *keep_open {
424 return Poll::Ready(Ok(0));
427 }
428 buf[..3].copy_from_slice(b"0\r\n");
434 return Poll::Ready(Ok(3));
435 }
436
437 *progress += bytes as u64;
438
439 let start = format!("{bytes:X}\r\n");
440 let start_length = start.len();
441 let total = bytes + start_length + 2;
442 buf.copy_within(..bytes, start_length);
443 buf[..start_length].copy_from_slice(start.as_bytes());
444 buf[total - 2..total].copy_from_slice(b"\r\n");
445 Poll::Ready(Ok(total))
446 }
447 }
448 }
449}
450
451struct SyncAsyncReader(SyncWrapper<Pin<Box<dyn BodySource>>>);
452impl Debug for SyncAsyncReader {
453 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
454 f.debug_struct("SyncAsyncReader").finish()
455 }
456}
457impl AsyncRead for SyncAsyncReader {
458 fn poll_read(
459 self: Pin<&mut Self>,
460 cx: &mut Context<'_>,
461 buf: &mut [u8],
462 ) -> Poll<Result<usize>> {
463 self.get_mut().0.get_mut().as_mut().poll_read(cx, buf)
464 }
465}
466
467#[derive(Clone)]
470pub(crate) enum StaticContent {
471 Cow(Cow<'static, [u8]>),
472 Bytes(Arc<[u8]>),
473 Str(Arc<str>),
474}
475
476impl std::ops::Deref for StaticContent {
477 type Target = [u8];
478
479 fn deref(&self) -> &[u8] {
480 match self {
481 StaticContent::Cow(content) => content,
482 StaticContent::Bytes(content) => content,
483 StaticContent::Str(content) => content.as_bytes(),
484 }
485 }
486}
487
488impl AsRef<[u8]> for StaticContent {
489 fn as_ref(&self) -> &[u8] {
490 self
491 }
492}
493
494impl StaticContent {
495 fn into_cow(self) -> Cow<'static, [u8]> {
498 match self {
499 StaticContent::Cow(content) => content,
500 other => Cow::Owned(other.to_vec()),
501 }
502 }
503}
504
505#[derive(Default)]
506pub(crate) enum BodyType {
507 #[default]
508 Empty,
509
510 Static {
511 content: StaticContent,
512 cursor: usize,
513 },
514
515 Streaming {
516 async_read: SyncWrapper<Pin<Box<dyn BodySource>>>,
517 progress: u64,
518 len: Option<u64>,
519 done: bool,
520 chunked_framing: bool,
524 keep_open: bool,
528 },
529}
530
531impl Debug for BodyType {
532 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
533 match self {
534 Empty => f.debug_tuple("BodyType::Empty").finish(),
535 Static { content, cursor } => f
536 .debug_struct("BodyType::Static")
537 .field("content", &String::from_utf8_lossy(content))
538 .field("cursor", cursor)
539 .finish(),
540 Streaming {
541 len,
542 done,
543 progress,
544 ..
545 } => f
546 .debug_struct("BodyType::Streaming")
547 .field("async_read", &format_args!(".."))
548 .field("len", &len)
549 .field("done", &done)
550 .field("progress", &progress)
551 .finish(),
552 }
553 }
554}
555
556impl BodyType {
557 fn is_empty(&self) -> bool {
558 match *self {
559 Empty => true,
560 Static { ref content, .. } => content.is_empty(),
561 Streaming { len, .. } => len == Some(0),
562 }
563 }
564
565 fn len(&self) -> Option<u64> {
566 match *self {
567 Empty => Some(0),
568 Static { ref content, .. } => Some(content.len() as u64),
569 Streaming { len, .. } => len,
570 }
571 }
572
573 fn bytes_read(&self) -> u64 {
574 match *self {
575 Empty => 0,
576 Static { cursor, .. } => cursor as u64,
577 Streaming { progress, .. } => progress,
578 }
579 }
580}
581
582impl From<String> for Body {
583 fn from(s: String) -> Self {
584 s.into_bytes().into()
585 }
586}
587
588impl From<&'static str> for Body {
589 fn from(s: &'static str) -> Self {
590 s.as_bytes().into()
591 }
592}
593
594impl From<&'static [u8]> for Body {
595 fn from(content: &'static [u8]) -> Self {
596 Self::new_static(content)
597 }
598}
599
600impl From<Vec<u8>> for Body {
601 fn from(content: Vec<u8>) -> Self {
602 Self::new_static(content)
603 }
604}
605
606impl From<Cow<'static, [u8]>> for Body {
607 fn from(value: Cow<'static, [u8]>) -> Self {
608 Self::new_static(value)
609 }
610}
611
612impl From<Cow<'static, str>> for Body {
613 fn from(value: Cow<'static, str>) -> Self {
614 match value {
615 Cow::Borrowed(b) => b.into(),
616 Cow::Owned(o) => o.into(),
617 }
618 }
619}
620
621impl From<Arc<[u8]>> for Body {
622 fn from(content: Arc<[u8]>) -> Self {
623 Self(Static {
624 content: StaticContent::Bytes(content),
625 cursor: 0,
626 })
627 }
628}
629
630impl From<Arc<str>> for Body {
631 fn from(content: Arc<str>) -> Self {
632 Self(Static {
633 content: StaticContent::Str(content),
634 cursor: 0,
635 })
636 }
637}
638
639#[cfg(test)]
640mod test_shared_content {
641 use super::Body;
642 use futures_lite::future::block_on;
643 use std::sync::Arc;
644
645 #[test]
646 fn arc_bytes_roundtrips() {
647 let arc: Arc<[u8]> = Arc::from(&b"shared bytes"[..]);
648 let body = Body::from(Arc::clone(&arc));
649 assert_eq!(body.len(), Some(12));
650 assert_eq!(body.static_bytes(), Some(&b"shared bytes"[..]));
651 assert_eq!(
652 block_on(body.into_bytes()).unwrap().as_ref(),
653 b"shared bytes"
654 );
655 assert_eq!(&*arc, b"shared bytes");
657 }
658
659 #[test]
660 fn arc_str_roundtrips() {
661 let arc: Arc<str> = Arc::from("shared str");
662 let body = Body::from(arc);
663 assert_eq!(body.len(), Some(10));
664 assert_eq!(body.static_bytes(), Some(&b"shared str"[..]));
665 assert_eq!(block_on(body.into_bytes()).unwrap().as_ref(), b"shared str");
666 }
667
668 #[cfg(feature = "unstable")]
669 #[test]
670 fn shared_body_clones_without_copying_the_arc() {
671 let arc: Arc<[u8]> = Arc::from(&b"abc"[..]);
672 let body = Body::from(Arc::clone(&arc));
673 let clone = body.try_clone().expect("static bodies clone");
674 assert_eq!(clone.static_bytes(), Some(&b"abc"[..]));
675 assert_eq!(Arc::strong_count(&arc), 3);
677 }
678}
679
680#[cfg(test)]
681mod test_bytes_to_read {
682 #[test]
683 fn simple_check_of_known_values() {
684 let values = vec![
693 (6, 1), (7, 2), (20, 15), (21, 15), (22, 16), (23, 17), (260, 254), (261, 254), (262, 255), (263, 256), (4100, 4093), (4101, 4093), (4102, 4094), (4103, 4095), (4104, 4096), ];
709
710 for (input, expected) in values {
711 let actual = super::max_bytes_to_read(input);
712 assert_eq!(
713 actual, expected,
714 "\n\nexpected max_bytes_to_read({input}) to be {expected}, but it was {actual}"
715 );
716
717 let used_bytes = expected + 4 + format!("{expected:X}").len();
719 assert!(
720 used_bytes == input || used_bytes == input - 1,
721 "\n\nfor an input of {}, expected used bytes to be {} or {}, but was {}",
722 input,
723 input,
724 input - 1,
725 used_bytes
726 );
727 }
728 }
729}
730
731#[cfg(test)]
732mod test_into_bytes {
733 use super::Body;
734 use futures_lite::{future::block_on, io::Cursor};
735
736 #[test]
737 fn a_streaming_body_fills_its_known_length_exactly() {
738 let body = Body::new_streaming(Cursor::new(vec![b'x'; 5000]), Some(5000));
739 let bytes = block_on(body.into_bytes()).unwrap().into_owned();
740 assert_eq!(bytes.len(), 5000);
741 assert_eq!(
742 bytes.capacity(),
743 5000,
744 "the body was reallocated after it was complete"
745 );
746 }
747
748 #[test]
749 fn a_declared_length_does_not_dictate_the_allocation() {
750 let body = Body::new_streaming(Cursor::new(b"hello".to_vec()), Some(u64::MAX));
751 assert_eq!(block_on(body.into_bytes()).unwrap().as_ref(), b"hello");
752 }
753}