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}