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}