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 | #[cfg_attr ( |
48 | feature = "deprecated" , |
49 | deprecated( |
50 | note = "This function has been replaced by a method on the `hyper::body::HttpBody` trait. Use `.collect().await?.to_bytes()` instead." |
51 | ) |
52 | )] |
53 | #[cfg_attr (feature = "deprecated" , allow(deprecated))] |
54 | pub async fn to_bytes<T>(body: T) -> Result<Bytes, T::Error> |
55 | where |
56 | T: HttpBody, |
57 | { |
58 | futures_util::pin_mut!(body); |
59 | |
60 | // If there's only 1 chunk, we can just return Buf::to_bytes() |
61 | let mut first = if let Some(buf) = body.data().await { |
62 | buf? |
63 | } else { |
64 | return Ok(Bytes::new()); |
65 | }; |
66 | |
67 | let second = if let Some(buf) = body.data().await { |
68 | buf? |
69 | } else { |
70 | return Ok(first.copy_to_bytes(first.remaining())); |
71 | }; |
72 | |
73 | // Don't pre-emptively reserve *too* much. |
74 | let rest = (body.size_hint().lower() as usize).min(1024 * 16); |
75 | let cap = first |
76 | .remaining() |
77 | .saturating_add(second.remaining()) |
78 | .saturating_add(rest); |
79 | // With more than 1 buf, we gotta flatten into a Vec first. |
80 | let mut vec = Vec::with_capacity(cap); |
81 | vec.put(first); |
82 | vec.put(second); |
83 | |
84 | while let Some(buf) = body.data().await { |
85 | vec.put(buf?); |
86 | } |
87 | |
88 | Ok(vec.into()) |
89 | } |
90 | |