1 | use bytes::{Buf, BufMut, Bytes}; |
2 | |
3 | use super::HttpBody; |
4 | |
5 | /// Concatenate the buffers from a body into a single `Bytes` asynchronously. |
6 | /// |
7 | /// This may require copying the data into a single buffer. If you don't need |
8 | /// a contiguous buffer, prefer the [`aggregate`](crate::body::aggregate()) |
9 | /// function. |
10 | /// |
11 | /// # Note |
12 | /// |
13 | /// Care needs to be taken if the remote is untrusted. The function doesn't implement any length |
14 | /// checks and an malicious peer might make it consume arbitrary amounts of memory. Checking the |
15 | /// `Content-Length` is a possibility, but it is not strictly mandated to be present. |
16 | /// |
17 | /// # Example |
18 | /// |
19 | /// ``` |
20 | /// # #[cfg (all(feature = "client" , feature = "tcp" , any(feature = "http1" , feature = "http2" )))] |
21 | /// # async fn doc() -> hyper::Result<()> { |
22 | /// use hyper::{body::HttpBody}; |
23 | /// |
24 | /// # let request = hyper::Request::builder() |
25 | /// # .method(hyper::Method::POST) |
26 | /// # .uri("http://httpbin.org/post" ) |
27 | /// # .header("content-type" , "application/json" ) |
28 | /// # .body(hyper::Body::from(r#"{"library":"hyper"}"# )).unwrap(); |
29 | /// # let client = hyper::Client::new(); |
30 | /// let response = client.request(request).await?; |
31 | /// |
32 | /// const MAX_ALLOWED_RESPONSE_SIZE: u64 = 1024; |
33 | /// |
34 | /// let response_content_length = match response.body().size_hint().upper() { |
35 | /// Some(v) => v, |
36 | /// None => MAX_ALLOWED_RESPONSE_SIZE + 1 // Just to protect ourselves from a malicious response |
37 | /// }; |
38 | /// |
39 | /// if response_content_length < MAX_ALLOWED_RESPONSE_SIZE { |
40 | /// let body_bytes = hyper::body::to_bytes(response.into_body()).await?; |
41 | /// println!("body: {:?}" , body_bytes); |
42 | /// } |
43 | /// |
44 | /// # Ok(()) |
45 | /// # } |
46 | /// ``` |
47 | pub async fn to_bytes<T>(body: T) -> Result<Bytes, T::Error> |
48 | where |
49 | T: HttpBody, |
50 | { |
51 | futures_util::pin_mut!(body); |
52 | |
53 | // If there's only 1 chunk, we can just return Buf::to_bytes() |
54 | let mut first = if let Some(buf) = body.data().await { |
55 | buf? |
56 | } else { |
57 | return Ok(Bytes::new()); |
58 | }; |
59 | |
60 | let second = if let Some(buf) = body.data().await { |
61 | buf? |
62 | } else { |
63 | return Ok(first.copy_to_bytes(first.remaining())); |
64 | }; |
65 | |
66 | // With more than 1 buf, we gotta flatten into a Vec first. |
67 | let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; |
68 | let mut vec = Vec::with_capacity(cap); |
69 | vec.put(first); |
70 | vec.put(second); |
71 | |
72 | while let Some(buf) = body.data().await { |
73 | vec.put(buf?); |
74 | } |
75 | |
76 | Ok(vec.into()) |
77 | } |
78 | |