Skip to main content

actix_http/body/
utils.rs

1use std::{pin::pin, task::Poll};
2
3use actix_utils::future::poll_fn;
4use bytes::{Bytes, BytesMut};
5use derive_more::{Display, Error};
6use futures_core::ready;
7
8use super::{BodySize, MessageBody};
9
10/// Collects all the bytes produced by `body`.
11///
12/// Any errors produced by the body stream are returned immediately.
13///
14/// Consider using [`to_bytes_limited`] instead to protect against memory exhaustion.
15///
16/// # Examples
17///
18/// ```
19/// use actix_http::body::{self, to_bytes};
20/// use bytes::Bytes;
21///
22/// # actix_rt::System::new().block_on(async {
23/// let body = body::None::new();
24/// let bytes = to_bytes(body).await.unwrap();
25/// assert!(bytes.is_empty());
26///
27/// let body = Bytes::from_static(b"123");
28/// let bytes = to_bytes(body).await.unwrap();
29/// assert_eq!(bytes, "123");
30/// # });
31/// ```
32pub async fn to_bytes<B: MessageBody>(body: B) -> Result<Bytes, B::Error> {
33    to_bytes_limited(body, usize::MAX)
34        .await
35        .expect("body should never yield more than usize::MAX bytes")
36}
37
38/// Error type returned from [`to_bytes_limited`] when body produced exceeds limit.
39#[derive(Debug, Display, Error)]
40#[display("limit exceeded while collecting body bytes")]
41#[non_exhaustive]
42pub struct BodyLimitExceeded;
43
44/// Collects the bytes produced by `body`, up to `limit` bytes.
45///
46/// If a chunk read from `poll_next` causes the total number of bytes read to exceed `limit`, an
47/// `Err(BodyLimitExceeded)` is returned.
48///
49/// Any errors produced by the body stream are returned immediately as `Ok(Err(B::Error))`.
50///
51/// # Examples
52///
53/// ```
54/// use actix_http::body::{self, to_bytes_limited};
55/// use bytes::Bytes;
56///
57/// # actix_rt::System::new().block_on(async {
58/// let body = body::None::new();
59/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();
60/// assert!(bytes.is_empty());
61///
62/// let body = Bytes::from_static(b"123");
63/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();
64/// assert_eq!(bytes, "123");
65///
66/// let body = Bytes::from_static(b"123");
67/// assert!(to_bytes_limited(body, 2).await.is_err());
68/// # });
69/// ```
70pub async fn to_bytes_limited<B: MessageBody>(
71    body: B,
72    limit: usize,
73) -> Result<Result<Bytes, B::Error>, BodyLimitExceeded> {
74    /// Sensible default (32kB) for initial, bounded allocation when collecting body bytes.
75    const INITIAL_ALLOC_BYTES: usize = 32 * 1024;
76
77    let cap = match body.size() {
78        BodySize::None | BodySize::Sized(0) => return Ok(Ok(Bytes::new())),
79        BodySize::Sized(size) if size as usize > limit => return Err(BodyLimitExceeded),
80        BodySize::Sized(size) => (size as usize).min(INITIAL_ALLOC_BYTES),
81        BodySize::Stream => INITIAL_ALLOC_BYTES,
82    };
83
84    let mut exceeded_limit = false;
85    let mut buf = BytesMut::with_capacity(cap);
86
87    let mut body = pin!(body);
88
89    match poll_fn(|cx| loop {
90        let body = body.as_mut();
91
92        match ready!(body.poll_next(cx)) {
93            Some(Ok(bytes)) => {
94                // if limit is exceeded...
95                if buf.len() + bytes.len() > limit {
96                    // ...set flag to true and break out of poll_fn
97                    exceeded_limit = true;
98                    return Poll::Ready(Ok(()));
99                }
100
101                buf.extend_from_slice(&bytes)
102            }
103            None => return Poll::Ready(Ok(())),
104            Some(Err(err)) => return Poll::Ready(Err(err)),
105        }
106    })
107    .await
108    {
109        // propagate error returned from body poll
110        Err(err) => Ok(Err(err)),
111
112        // limit was exceeded while reading body
113        Ok(()) if exceeded_limit => Err(BodyLimitExceeded),
114
115        // otherwise return body buffer
116        Ok(()) => Ok(Ok(buf.freeze())),
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use std::io;
123
124    use futures_util::{stream, StreamExt as _};
125
126    use super::*;
127    use crate::{
128        body::{BodyStream, SizedStream},
129        Error,
130    };
131
132    #[actix_rt::test]
133    async fn to_bytes_complete() {
134        let bytes = to_bytes(()).await.unwrap();
135        assert!(bytes.is_empty());
136
137        let body = Bytes::from_static(b"123");
138        let bytes = to_bytes(body).await.unwrap();
139        assert_eq!(bytes, b"123"[..]);
140    }
141
142    #[actix_rt::test]
143    async fn to_bytes_streams() {
144        let stream = stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")])
145            .map(Ok::<_, Error>);
146        let body = BodyStream::new(stream);
147        let bytes = to_bytes(body).await.unwrap();
148        assert_eq!(bytes, b"123abc"[..]);
149    }
150
151    #[actix_rt::test]
152    async fn to_bytes_limited_complete() {
153        let bytes = to_bytes_limited((), 0).await.unwrap().unwrap();
154        assert!(bytes.is_empty());
155
156        let bytes = to_bytes_limited((), 1).await.unwrap().unwrap();
157        assert!(bytes.is_empty());
158
159        assert!(to_bytes_limited(Bytes::from_static(b"12"), 0)
160            .await
161            .is_err());
162        assert!(to_bytes_limited(Bytes::from_static(b"12"), 1)
163            .await
164            .is_err());
165        assert!(to_bytes_limited(Bytes::from_static(b"12"), 2).await.is_ok());
166        assert!(to_bytes_limited(Bytes::from_static(b"12"), 3).await.is_ok());
167    }
168
169    #[actix_rt::test]
170    async fn to_bytes_limited_streams() {
171        // hinting a larger body fails
172        let body = SizedStream::new(8, stream::empty().map(Ok::<_, Error>));
173        assert!(to_bytes_limited(body, 3).await.is_err());
174
175        // hinting a smaller body is okay
176        let body = SizedStream::new(3, stream::empty().map(Ok::<_, Error>));
177        assert!(to_bytes_limited(body, 3).await.unwrap().unwrap().is_empty());
178
179        // hinting a smaller body then returning a larger one fails
180        let stream = stream::iter(vec![Bytes::from_static(b"1234")]).map(Ok::<_, Error>);
181        let body = SizedStream::new(3, stream);
182        assert!(to_bytes_limited(body, 3).await.is_err());
183
184        let stream = stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")])
185            .map(Ok::<_, Error>);
186        let body = BodyStream::new(stream);
187        assert!(to_bytes_limited(body, 3).await.is_err());
188    }
189
190    #[actix_rt::test]
191    async fn to_body_limit_error() {
192        let err_stream = stream::once(async { Err(io::Error::other("")) });
193        let body = SizedStream::new(8, err_stream);
194        // not too big, but propagates error from body stream
195        assert!(to_bytes_limited(body, 10).await.unwrap().is_err());
196    }
197}