http_types_rs/
body.rs

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