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