http_types/
body.rs

1use futures_lite::{io, prelude::*, ready};
2use serde::{de::DeserializeOwned, Serialize};
3
4use std::fmt::{self, Debug};
5use std::pin::Pin;
6use std::task::{Context, Poll};
7
8use crate::{mime, Mime};
9use crate::{Status, StatusCode};
10
11pin_project_lite::pin_project! {
12    /// A streaming HTTP body.
13    ///
14    /// `Body` represents the HTTP body of both `Request` and `Response`. It's completely
15    /// streaming, and implements `AsyncBufRead` to make reading from it both convenient and
16    /// performant.
17    ///
18    /// Both `Request` and `Response` take `Body` by `Into<Body>`, which means that passing string
19    /// literals, byte vectors, but also concrete `Body` instances are all valid. This makes it
20    /// easy to create both quick HTTP requests, but also have fine grained control over how bodies
21    /// are streamed out.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// use http_types::{Body, Response, StatusCode};
27    /// use async_std::io::Cursor;
28    ///
29    /// let mut req = Response::new(StatusCode::Ok);
30    /// req.set_body("Hello Chashu");
31    ///
32    /// let mut req = Response::new(StatusCode::Ok);
33    /// let cursor = Cursor::new("Hello Nori");
34    /// let body = Body::from_reader(cursor, Some(10)); // set the body length
35    /// req.set_body(body);
36    /// ```
37    ///
38    /// # Length
39    ///
40    /// One of the details of `Body` to be aware of is the `length` parameter. The value of
41    /// `length` is used by HTTP implementations to determine how to treat the stream. If a length
42    /// is known ahead of time, it's _strongly_ recommended to pass it.
43    ///
44    /// Casting from `Vec<u8>`, `String`, or similar to `Body` will automatically set the value of
45    /// `length`.
46    ///
47    /// # Content Encoding
48    ///
49    /// By default `Body` will come with a fallback Mime type that is used by `Request` and
50    /// `Response` if no other type has been set, and no other Mime type can be inferred.
51    ///
52    /// It's _strongly_ recommended to always set a mime type on both the `Request` and `Response`,
53    /// and not rely on the fallback mechanisms. However, they're still there if you need them.
54    pub struct Body {
55        #[pin]
56        reader: Box<dyn AsyncBufRead + Unpin + Send + Sync + 'static>,
57        mime: Mime,
58        length: Option<usize>,
59        bytes_read: usize
60    }
61}
62
63impl Body {
64    /// Create a new empty `Body`.
65    ///
66    /// The body will have a length of `0`, and the Mime type set to `application/octet-stream` if
67    /// no other mime type has been set or can be sniffed.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use http_types::{Body, Response, StatusCode};
73    ///
74    /// let mut req = Response::new(StatusCode::Ok);
75    /// req.set_body(Body::empty());
76    /// ```
77    pub fn empty() -> Self {
78        Self {
79            reader: Box::new(io::empty()),
80            mime: mime::BYTE_STREAM,
81            length: Some(0),
82            bytes_read: 0,
83        }
84    }
85
86    /// Create a `Body` from a reader with an optional length.
87    ///
88    /// The Mime type is set to `application/octet-stream` if no other mime type has been set or can
89    /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to
90    /// framed messages such as [Chunked
91    /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding).
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use http_types::{Body, Response, StatusCode};
97    /// use async_std::io::Cursor;
98    ///
99    /// let mut req = Response::new(StatusCode::Ok);
100    ///
101    /// let cursor = Cursor::new("Hello Nori");
102    /// let len = 10;
103    /// req.set_body(Body::from_reader(cursor, Some(len)));
104    /// ```
105    pub fn from_reader(
106        reader: impl AsyncBufRead + Unpin + Send + Sync + 'static,
107        len: Option<usize>,
108    ) -> Self {
109        Self {
110            reader: Box::new(reader),
111            mime: mime::BYTE_STREAM,
112            length: len,
113            bytes_read: 0,
114        }
115    }
116
117    /// Get the inner reader from the `Body`
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// # use std::io::prelude::*;
123    /// use http_types::Body;
124    /// use async_std::io::Cursor;
125    ///
126    /// let cursor = Cursor::new("Hello Nori");
127    /// let body = Body::from_reader(cursor, None);
128    /// let _ = body.into_reader();
129    /// ```
130    pub fn into_reader(self) -> Box<dyn AsyncBufRead + Unpin + Send + Sync + 'static> {
131        self.reader
132    }
133
134    /// Create a `Body` from a Vec of bytes.
135    ///
136    /// The Mime type is set to `application/octet-stream` if no other mime type has been set or can
137    /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to
138    /// framed messages such as [Chunked
139    /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding).
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use http_types::{Body, Response, StatusCode};
145    /// use async_std::io::Cursor;
146    ///
147    /// let mut req = Response::new(StatusCode::Ok);
148    ///
149    /// let input = vec![1, 2, 3];
150    /// req.set_body(Body::from_bytes(input));
151    /// ```
152    pub fn from_bytes(bytes: Vec<u8>) -> Self {
153        Self {
154            mime: mime::BYTE_STREAM,
155            length: Some(bytes.len()),
156            reader: Box::new(io::Cursor::new(bytes)),
157            bytes_read: 0,
158        }
159    }
160
161    /// Parse the body into a `Vec<u8>`.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
167    /// use http_types::Body;
168    ///
169    /// let bytes = vec![1, 2, 3];
170    /// let body = Body::from_bytes(bytes);
171    ///
172    /// let bytes: Vec<u8> = body.into_bytes().await?;
173    /// assert_eq!(bytes, vec![1, 2, 3]);
174    /// # Ok(()) }) }
175    /// ```
176    pub async fn into_bytes(mut self) -> crate::Result<Vec<u8>> {
177        let mut buf = Vec::with_capacity(1024);
178        self.read_to_end(&mut buf)
179            .await
180            .status(StatusCode::UnprocessableEntity)?;
181        Ok(buf)
182    }
183
184    /// Create a `Body` from a String
185    ///
186    /// The Mime type is set to `text/plain` if no other mime type has been set or can
187    /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to
188    /// framed messages such as [Chunked
189    /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding).
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use http_types::{Body, Response, StatusCode};
195    /// use async_std::io::Cursor;
196    ///
197    /// let mut req = Response::new(StatusCode::Ok);
198    ///
199    /// let input = String::from("hello Nori!");
200    /// req.set_body(Body::from_string(input));
201    /// ```
202    pub fn from_string(s: String) -> Self {
203        Self {
204            mime: mime::PLAIN,
205            length: Some(s.len()),
206            reader: Box::new(io::Cursor::new(s.into_bytes())),
207            bytes_read: 0,
208        }
209    }
210
211    /// Read the body as a string
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
217    /// use http_types::Body;
218    /// use async_std::io::Cursor;
219    ///
220    /// let cursor = Cursor::new("Hello Nori");
221    /// let body = Body::from_reader(cursor, None);
222    /// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori");
223    /// # Ok(()) }) }
224    /// ```
225    pub async fn into_string(mut self) -> crate::Result<String> {
226        let mut result = String::with_capacity(self.len().unwrap_or(0));
227        self.read_to_string(&mut result)
228            .await
229            .status(StatusCode::UnprocessableEntity)?;
230        Ok(result)
231    }
232
233    /// Creates a `Body` from a type, serializing it as JSON.
234    ///
235    /// # Mime
236    ///
237    /// The encoding is set to `application/json`.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use http_types::{Body, convert::json};
243    ///
244    /// let body = Body::from_json(&json!({ "name": "Chashu" }));
245    /// # drop(body);
246    /// ```
247    pub fn from_json(json: &impl Serialize) -> crate::Result<Self> {
248        let bytes = serde_json::to_vec(&json)?;
249        let body = Self {
250            length: Some(bytes.len()),
251            reader: Box::new(io::Cursor::new(bytes)),
252            mime: mime::JSON,
253            bytes_read: 0,
254        };
255        Ok(body)
256    }
257
258    /// Parse the body as JSON, serializing it to a struct.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
264    /// use http_types::Body;
265    /// use http_types::convert::{Serialize, Deserialize};
266    ///
267    /// #[derive(Debug, Serialize, Deserialize)]
268    /// struct Cat { name: String }
269    ///
270    /// let cat = Cat { name: String::from("chashu") };
271    /// let body = Body::from_json(&cat)?;
272    ///
273    /// let cat: Cat = body.into_json().await?;
274    /// assert_eq!(&cat.name, "chashu");
275    /// # Ok(()) }) }
276    /// ```
277    pub async fn into_json<T: DeserializeOwned>(mut self) -> crate::Result<T> {
278        let mut buf = Vec::with_capacity(1024);
279        self.read_to_end(&mut buf).await?;
280        Ok(serde_json::from_slice(&buf).status(StatusCode::UnprocessableEntity)?)
281    }
282
283    /// Creates a `Body` from a type, serializing it using form encoding.
284    ///
285    /// # Mime
286    ///
287    /// The encoding is set to `application/x-www-form-urlencoded`.
288    ///
289    /// # Errors
290    ///
291    /// An error will be returned if the encoding failed.
292    ///
293    /// # Examples
294    ///
295    /// ```
296    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
297    /// use http_types::Body;
298    /// use http_types::convert::{Serialize, Deserialize};
299    ///
300    /// #[derive(Debug, Serialize, Deserialize)]
301    /// struct Cat { name: String }
302    ///
303    /// let cat = Cat { name: String::from("chashu") };
304    /// let body = Body::from_form(&cat)?;
305    ///
306    /// let cat: Cat = body.into_form().await?;
307    /// assert_eq!(&cat.name, "chashu");
308    /// # Ok(()) }) }
309    /// ```
310    pub fn from_form(form: &impl Serialize) -> crate::Result<Self> {
311        let query = serde_urlencoded::to_string(form)?;
312        let bytes = query.into_bytes();
313
314        let body = Self {
315            length: Some(bytes.len()),
316            reader: Box::new(io::Cursor::new(bytes)),
317            mime: mime::FORM,
318            bytes_read: 0,
319        };
320        Ok(body)
321    }
322
323    /// Parse the body from form encoding into a type.
324    ///
325    /// # Errors
326    ///
327    /// An error is returned if the underlying IO stream errors, or if the body
328    /// could not be deserialized into the type.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
334    /// use http_types::Body;
335    /// use http_types::convert::{Serialize, Deserialize};
336    ///
337    /// #[derive(Debug, Serialize, Deserialize)]
338    /// struct Cat { name: String }
339    ///
340    /// let cat = Cat { name: String::from("chashu") };
341    /// let body = Body::from_form(&cat)?;
342    ///
343    /// let cat: Cat = body.into_form().await?;
344    /// assert_eq!(&cat.name, "chashu");
345    /// # Ok(()) }) }
346    /// ```
347    pub async fn into_form<T: DeserializeOwned>(self) -> crate::Result<T> {
348        let s = self.into_string().await?;
349        Ok(serde_urlencoded::from_str(&s).status(StatusCode::UnprocessableEntity)?)
350    }
351
352    /// Create a `Body` from a file.
353    ///
354    /// The Mime type set to `application/octet-stream` if no other mime type has
355    /// been set or can be sniffed.
356    ///
357    /// # Examples
358    ///
359    /// ```no_run
360    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
361    /// use http_types::{Body, Response, StatusCode};
362    ///
363    /// let mut res = Response::new(StatusCode::Ok);
364    /// res.set_body(Body::from_file("/path/to/file").await?);
365    /// # Ok(()) }) }
366    /// ```
367    #[cfg(all(feature = "fs", not(target_os = "unknown")))]
368    pub async fn from_file<P>(path: P) -> io::Result<Self>
369    where
370        P: AsRef<std::path::Path>,
371    {
372        let path = path.as_ref();
373        let mut file = async_std::fs::File::open(path).await?;
374        let len = file.metadata().await?.len();
375
376        // Look at magic bytes first, look at extension second, fall back to
377        // octet stream.
378        let mime = peek_mime(&mut file)
379            .await?
380            .or_else(|| guess_ext(path))
381            .unwrap_or(mime::BYTE_STREAM);
382
383        Ok(Self {
384            mime,
385            length: Some(len as usize),
386            reader: Box::new(io::BufReader::new(file)),
387            bytes_read: 0,
388        })
389    }
390
391    /// Get the length of the body in bytes.
392    ///
393    /// # Examples
394    ///
395    /// ```
396    /// use http_types::Body;
397    /// use async_std::io::Cursor;
398    ///
399    /// let cursor = Cursor::new("Hello Nori");
400    /// let len = 10;
401    /// let body = Body::from_reader(cursor, Some(len));
402    /// assert_eq!(body.len(), Some(10));
403    /// ```
404    pub fn len(&self) -> Option<usize> {
405        self.length
406    }
407
408    /// Returns `true` if the body has a length of zero, and `false` otherwise.
409    pub fn is_empty(&self) -> Option<bool> {
410        self.length.map(|length| length == 0)
411    }
412
413    /// Returns the mime type of this Body.
414    pub fn mime(&self) -> &Mime {
415        &self.mime
416    }
417
418    /// Sets the mime type of this Body.
419    pub fn set_mime(&mut self, mime: impl Into<Mime>) {
420        self.mime = mime.into();
421    }
422
423    /// Create a Body by chaining another Body after this one, consuming both.
424    ///
425    /// If both Body instances have a length, and their sum does not overflow,
426    /// the resulting Body will have a length.
427    ///
428    /// If both Body instances have the same fallback MIME type, the resulting
429    /// Body will have the same fallback MIME type; otherwise, the resulting
430    /// Body will have the fallback MIME type `application/octet-stream`.
431    ///
432    /// # Examples
433    ///
434    /// ```
435    /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
436    /// use http_types::Body;
437    /// use async_std::io::Cursor;
438    ///
439    /// let cursor = Cursor::new("Hello ");
440    /// let body = Body::from_reader(cursor, None).chain(Body::from("Nori"));
441    /// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori");
442    /// # Ok(()) }) }
443    /// ```
444    pub fn chain(self, other: Body) -> Self {
445        let mime = if self.mime == other.mime {
446            self.mime.clone()
447        } else {
448            mime::BYTE_STREAM
449        };
450        let length = match (self.length, other.length) {
451            (Some(l1), Some(l2)) => (l1 - self.bytes_read).checked_add(l2 - other.bytes_read),
452            _ => None,
453        };
454        Self {
455            mime,
456            length,
457            reader: Box::new(futures_lite::io::AsyncReadExt::chain(self, other)),
458            bytes_read: 0,
459        }
460    }
461}
462
463impl Debug for Body {
464    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465        f.debug_struct("Body")
466            .field("reader", &"<hidden>")
467            .field("length", &self.length)
468            .field("bytes_read", &self.bytes_read)
469            .finish()
470    }
471}
472
473impl From<serde_json::Value> for Body {
474    fn from(json_value: serde_json::Value) -> Self {
475        Self::from_json(&json_value).unwrap()
476    }
477}
478
479impl From<String> for Body {
480    fn from(s: String) -> Self {
481        Self::from_string(s)
482    }
483}
484
485impl<'a> From<&'a str> for Body {
486    fn from(s: &'a str) -> Self {
487        Self::from_string(s.to_owned())
488    }
489}
490
491impl From<Vec<u8>> for Body {
492    fn from(b: Vec<u8>) -> Self {
493        Self::from_bytes(b)
494    }
495}
496
497impl<'a> From<&'a [u8]> for Body {
498    fn from(b: &'a [u8]) -> Self {
499        Self::from_bytes(b.to_owned())
500    }
501}
502
503impl AsyncRead for Body {
504    #[allow(missing_doc_code_examples)]
505    fn poll_read(
506        mut self: Pin<&mut Self>,
507        cx: &mut Context<'_>,
508        buf: &mut [u8],
509    ) -> Poll<io::Result<usize>> {
510        let mut buf = match self.length {
511            None => buf,
512            Some(length) if length == self.bytes_read => return Poll::Ready(Ok(0)),
513            Some(length) => {
514                let max_len = (length - self.bytes_read).min(buf.len());
515                &mut buf[0..max_len]
516            }
517        };
518
519        let bytes = ready!(Pin::new(&mut self.reader).poll_read(cx, &mut buf))?;
520        self.bytes_read += bytes;
521        Poll::Ready(Ok(bytes))
522    }
523}
524
525impl AsyncBufRead for Body {
526    #[allow(missing_doc_code_examples)]
527    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
528        self.project().reader.poll_fill_buf(cx)
529    }
530
531    fn consume(mut self: Pin<&mut Self>, amt: usize) {
532        Pin::new(&mut self.reader).consume(amt)
533    }
534}
535
536/// Look at first few bytes of a file to determine the mime type.
537/// This is used for various binary formats such as images and videos.
538#[cfg(all(feature = "fs", not(target_os = "unknown")))]
539async fn peek_mime(file: &mut async_std::fs::File) -> io::Result<Option<Mime>> {
540    // We need to read the first 300 bytes to correctly infer formats such as tar.
541    let mut buf = [0_u8; 300];
542    file.read(&mut buf).await?;
543    let mime = Mime::sniff(&buf).ok();
544
545    // Reset the file cursor back to the start.
546    file.seek(io::SeekFrom::Start(0)).await?;
547    Ok(mime)
548}
549
550/// Look at the extension of a file to determine the mime type.
551/// This is useful for plain-text formats such as HTML and CSS.
552#[cfg(all(feature = "fs", not(target_os = "unknown")))]
553fn guess_ext(path: &std::path::Path) -> Option<Mime> {
554    let ext = path.extension().map(|p| p.to_str()).flatten();
555    ext.and_then(Mime::from_extension)
556}
557
558#[cfg(test)]
559mod test {
560    use super::*;
561    use async_std::io::Cursor;
562    use serde::Deserialize;
563
564    #[async_std::test]
565    async fn json_status() {
566        #[derive(Debug, Deserialize)]
567        struct Foo {
568            inner: String,
569        }
570        let body = Body::empty();
571        let res = body.into_json::<Foo>().await;
572        assert_eq!(res.unwrap_err().status(), 422);
573    }
574
575    #[async_std::test]
576    async fn form_status() {
577        #[derive(Debug, Deserialize)]
578        struct Foo {
579            inner: String,
580        }
581        let body = Body::empty();
582        let res = body.into_form::<Foo>().await;
583        assert_eq!(res.unwrap_err().status(), 422);
584    }
585
586    async fn read_with_buffers_of_size<R>(reader: &mut R, size: usize) -> crate::Result<String>
587    where
588        R: AsyncRead + Unpin,
589    {
590        let mut return_buffer = vec![];
591        loop {
592            let mut buf = vec![0; size];
593            match reader.read(&mut buf).await? {
594                0 => break Ok(String::from_utf8(return_buffer)?),
595                bytes_read => return_buffer.extend_from_slice(&buf[..bytes_read]),
596            }
597        }
598    }
599
600    #[async_std::test]
601    async fn attempting_to_read_past_length() -> crate::Result<()> {
602        for buf_len in 1..13 {
603            let mut body = Body::from_reader(Cursor::new("hello world"), Some(5));
604            assert_eq!(
605                read_with_buffers_of_size(&mut body, buf_len).await?,
606                "hello"
607            );
608            assert_eq!(body.bytes_read, 5);
609        }
610
611        Ok(())
612    }
613
614    #[async_std::test]
615    async fn attempting_to_read_when_length_is_greater_than_content() -> crate::Result<()> {
616        for buf_len in 1..13 {
617            let mut body = Body::from_reader(Cursor::new("hello world"), Some(15));
618            assert_eq!(
619                read_with_buffers_of_size(&mut body, buf_len).await?,
620                "hello world"
621            );
622            assert_eq!(body.bytes_read, 11);
623        }
624
625        Ok(())
626    }
627
628    #[async_std::test]
629    async fn attempting_to_read_when_length_is_exactly_right() -> crate::Result<()> {
630        for buf_len in 1..13 {
631            let mut body = Body::from_reader(Cursor::new("hello world"), Some(11));
632            assert_eq!(
633                read_with_buffers_of_size(&mut body, buf_len).await?,
634                "hello world"
635            );
636            assert_eq!(body.bytes_read, 11);
637        }
638
639        Ok(())
640    }
641
642    #[async_std::test]
643    async fn reading_in_various_buffer_lengths_when_there_is_no_length() -> crate::Result<()> {
644        for buf_len in 1..13 {
645            let mut body = Body::from_reader(Cursor::new("hello world"), None);
646            assert_eq!(
647                read_with_buffers_of_size(&mut body, buf_len).await?,
648                "hello world"
649            );
650            assert_eq!(body.bytes_read, 11);
651        }
652
653        Ok(())
654    }
655
656    #[async_std::test]
657    async fn chain_strings() -> crate::Result<()> {
658        for buf_len in 1..13 {
659            let mut body = Body::from("hello ").chain(Body::from("world"));
660            assert_eq!(body.len(), Some(11));
661            assert_eq!(body.mime(), &mime::PLAIN);
662            assert_eq!(
663                read_with_buffers_of_size(&mut body, buf_len).await?,
664                "hello world"
665            );
666            assert_eq!(body.bytes_read, 11);
667        }
668
669        Ok(())
670    }
671
672    #[async_std::test]
673    async fn chain_mixed_bytes_string() -> crate::Result<()> {
674        for buf_len in 1..13 {
675            let mut body = Body::from(&b"hello "[..]).chain(Body::from("world"));
676            assert_eq!(body.len(), Some(11));
677            assert_eq!(body.mime(), &mime::BYTE_STREAM);
678            assert_eq!(
679                read_with_buffers_of_size(&mut body, buf_len).await?,
680                "hello world"
681            );
682            assert_eq!(body.bytes_read, 11);
683        }
684
685        Ok(())
686    }
687
688    #[async_std::test]
689    async fn chain_mixed_reader_string() -> crate::Result<()> {
690        for buf_len in 1..13 {
691            let mut body =
692                Body::from_reader(Cursor::new("hello "), Some(6)).chain(Body::from("world"));
693            assert_eq!(body.len(), Some(11));
694            assert_eq!(body.mime(), &mime::BYTE_STREAM);
695            assert_eq!(
696                read_with_buffers_of_size(&mut body, buf_len).await?,
697                "hello world"
698            );
699            assert_eq!(body.bytes_read, 11);
700        }
701
702        Ok(())
703    }
704
705    #[async_std::test]
706    async fn chain_mixed_nolen_len() -> crate::Result<()> {
707        for buf_len in 1..13 {
708            let mut body =
709                Body::from_reader(Cursor::new("hello "), None).chain(Body::from("world"));
710            assert_eq!(body.len(), None);
711            assert_eq!(body.mime(), &mime::BYTE_STREAM);
712            assert_eq!(
713                read_with_buffers_of_size(&mut body, buf_len).await?,
714                "hello world"
715            );
716            assert_eq!(body.bytes_read, 11);
717        }
718
719        Ok(())
720    }
721
722    #[async_std::test]
723    async fn chain_mixed_len_nolen() -> crate::Result<()> {
724        for buf_len in 1..13 {
725            let mut body =
726                Body::from("hello ").chain(Body::from_reader(Cursor::new("world"), None));
727            assert_eq!(body.len(), None);
728            assert_eq!(body.mime(), &mime::BYTE_STREAM);
729            assert_eq!(
730                read_with_buffers_of_size(&mut body, buf_len).await?,
731                "hello world"
732            );
733            assert_eq!(body.bytes_read, 11);
734        }
735
736        Ok(())
737    }
738
739    #[async_std::test]
740    async fn chain_short() -> crate::Result<()> {
741        for buf_len in 1..26 {
742            let mut body = Body::from_reader(Cursor::new("hello xyz"), Some(6))
743                .chain(Body::from_reader(Cursor::new("world abc"), Some(5)));
744            assert_eq!(body.len(), Some(11));
745            assert_eq!(body.mime(), &mime::BYTE_STREAM);
746            assert_eq!(
747                read_with_buffers_of_size(&mut body, buf_len).await?,
748                "hello world"
749            );
750            assert_eq!(body.bytes_read, 11);
751        }
752
753        Ok(())
754    }
755
756    #[async_std::test]
757    async fn chain_many() -> crate::Result<()> {
758        for buf_len in 1..13 {
759            let mut body = Body::from("hello")
760                .chain(Body::from(&b" "[..]))
761                .chain(Body::from("world"));
762            assert_eq!(body.len(), Some(11));
763            assert_eq!(body.mime(), &mime::BYTE_STREAM);
764            assert_eq!(
765                read_with_buffers_of_size(&mut body, buf_len).await?,
766                "hello world"
767            );
768            assert_eq!(body.bytes_read, 11);
769        }
770
771        Ok(())
772    }
773
774    #[async_std::test]
775    async fn chain_skip_start() -> crate::Result<()> {
776        for buf_len in 1..26 {
777            let mut body1 = Body::from_reader(Cursor::new("1234 hello xyz"), Some(11));
778            let mut buf = vec![0; 5];
779            body1.read(&mut buf).await?;
780            assert_eq!(buf, b"1234 ");
781
782            let mut body2 = Body::from_reader(Cursor::new("321 world abc"), Some(9));
783            let mut buf = vec![0; 4];
784            body2.read(&mut buf).await?;
785            assert_eq!(buf, b"321 ");
786
787            let mut body = body1.chain(body2);
788            assert_eq!(body.len(), Some(11));
789            assert_eq!(body.mime(), &mime::BYTE_STREAM);
790            assert_eq!(
791                read_with_buffers_of_size(&mut body, buf_len).await?,
792                "hello world"
793            );
794            assert_eq!(body.bytes_read, 11);
795        }
796
797        Ok(())
798    }
799}