Skip to main content

actix_http/body/
body_stream.rs

1use std::{
2    error::Error as StdError,
3    pin::Pin,
4    task::{Context, Poll},
5};
6
7use bytes::Bytes;
8use futures_core::{ready, Stream};
9use pin_project_lite::pin_project;
10
11use super::{BodySize, InfallibleStream, MessageBody};
12
13pin_project! {
14    /// Streaming response wrapper.
15    ///
16    /// Response does not contain `Content-Length` header and appropriate transfer encoding is used.
17    pub struct BodyStream<S> {
18        #[pin]
19        stream: S,
20    }
21}
22
23impl<S> BodyStream<InfallibleStream<S>>
24where
25    S: Stream<Item = Bytes>,
26{
27    /// Creates a body from a stream of bytes that cannot yield errors.
28    ///
29    /// Empty chunks are skipped.
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// # use actix_http::body::{BodyStream, InfallibleStream};
35    /// # use bytes::Bytes;
36    /// use futures_util::stream;
37    ///
38    /// let stream = stream::iter([Bytes::from_static(b"hello")]);
39    /// let body = BodyStream::from_infallible(stream);
40    /// ```
41    #[inline]
42    #[must_use]
43    pub fn from_infallible(stream: S) -> Self {
44        BodyStream::new(InfallibleStream { stream })
45    }
46}
47
48impl<S, E> BodyStream<S>
49where
50    S: Stream<Item = Result<Bytes, E>>,
51    E: Into<Box<dyn StdError>> + 'static,
52{
53    #[inline]
54    #[must_use]
55    pub fn new(stream: S) -> Self {
56        BodyStream { stream }
57    }
58}
59
60impl<S, E> MessageBody for BodyStream<S>
61where
62    S: Stream<Item = Result<Bytes, E>>,
63    E: Into<Box<dyn StdError>> + 'static,
64{
65    type Error = E;
66
67    #[inline]
68    fn size(&self) -> BodySize {
69        BodySize::Stream
70    }
71
72    /// Attempts to pull out the next value of the underlying [`Stream`].
73    ///
74    /// Empty values are skipped to prevent [`BodyStream`]'s transmission being ended on a
75    /// zero-length chunk, but rather proceed until the underlying [`Stream`] ends.
76    fn poll_next(
77        mut self: Pin<&mut Self>,
78        cx: &mut Context<'_>,
79    ) -> Poll<Option<Result<Bytes, Self::Error>>> {
80        loop {
81            let stream = self.as_mut().project().stream;
82
83            let chunk = match ready!(stream.poll_next(cx)) {
84                Some(Ok(ref bytes)) if bytes.is_empty() => continue,
85                opt => opt,
86            };
87
88            return Poll::Ready(chunk);
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use std::{convert::Infallible, pin::pin, time::Duration};
96
97    use actix_utils::future::poll_fn;
98    use derive_more::{Display, Error};
99    use futures_core::ready;
100    use futures_util::{stream, FutureExt as _};
101    use pin_project_lite::pin_project;
102    use static_assertions::{assert_impl_all, assert_not_impl_any};
103    use tokio::time::{sleep, Sleep};
104
105    use super::*;
106    use crate::body::to_bytes;
107
108    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, crate::Error>>>: MessageBody);
109    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, &'static str>>>: MessageBody);
110    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, &'static str>>>: MessageBody);
111    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);
112    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);
113
114    assert_not_impl_any!(BodyStream<stream::Empty<Bytes>>: MessageBody);
115    assert_not_impl_any!(BodyStream<stream::Repeat<Bytes>>: MessageBody);
116    // crate::Error is not Clone
117    assert_not_impl_any!(BodyStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
118
119    #[actix_rt::test]
120    async fn infallible_stream() {
121        let stream = async_stream::stream! {
122            yield Bytes::new();
123            yield Bytes::from_static(b"1");
124            tokio::task::yield_now().await;
125            yield Bytes::new();
126            yield Bytes::from_static(b"2");
127            yield Bytes::new();
128        };
129        let body = BodyStream::from_infallible(stream);
130
131        assert_eq!(body.size(), BodySize::Stream);
132        assert_eq!(
133            to_bytes(body).await,
134            Ok::<_, Infallible>(Bytes::from_static(b"12"))
135        );
136    }
137
138    #[actix_rt::test]
139    async fn infallible_empty_stream() {
140        let body = BodyStream::from_infallible(stream::empty::<Bytes>());
141
142        assert_eq!(to_bytes(body).await, Ok::<_, Infallible>(Bytes::new()));
143    }
144
145    #[actix_rt::test]
146    async fn skips_empty_chunks() {
147        let body = BodyStream::new(stream::iter(
148            ["1", "", "2"]
149                .iter()
150                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),
151        ));
152        let mut body = pin!(body);
153
154        assert_eq!(
155            poll_fn(|cx| body.as_mut().poll_next(cx))
156                .await
157                .unwrap()
158                .ok(),
159            Some(Bytes::from("1")),
160        );
161        assert_eq!(
162            poll_fn(|cx| body.as_mut().poll_next(cx))
163                .await
164                .unwrap()
165                .ok(),
166            Some(Bytes::from("2")),
167        );
168    }
169
170    #[actix_rt::test]
171    async fn read_to_bytes() {
172        let body = BodyStream::new(stream::iter(
173            ["1", "", "2"]
174                .iter()
175                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),
176        ));
177
178        assert_eq!(to_bytes(body).await.ok(), Some(Bytes::from("12")));
179    }
180    #[derive(Debug, Display, Error)]
181    #[display("Stream error")]
182    struct StreamErr;
183
184    #[actix_rt::test]
185    async fn stream_immediate_error() {
186        let body = BodyStream::new(stream::once(async { Err(StreamErr) }));
187        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
188    }
189
190    #[actix_rt::test]
191    async fn stream_string_error() {
192        // `&'static str` does not impl `Error`
193        // but it does impl `Into<Box<dyn Error>>`
194
195        let body = BodyStream::new(stream::once(async { Err("stringy error") }));
196        assert!(matches!(to_bytes(body).await, Err("stringy error")));
197    }
198
199    #[actix_rt::test]
200    async fn stream_boxed_error() {
201        // `Box<dyn Error>` does not impl `Error`
202        // but it does impl `Into<Box<dyn Error>>`
203
204        let body = BodyStream::new(stream::once(async {
205            Err(Box::<dyn StdError>::from("stringy error"))
206        }));
207
208        assert_eq!(
209            to_bytes(body).await.unwrap_err().to_string(),
210            "stringy error"
211        );
212    }
213
214    #[actix_rt::test]
215    async fn stream_delayed_error() {
216        let body = BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)]));
217        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
218
219        pin_project! {
220            #[derive(Debug)]
221            #[project = TimeDelayStreamProj]
222            enum TimeDelayStream {
223                Start,
224                Sleep { delay: Pin<Box<Sleep>> },
225                Done,
226            }
227        }
228
229        impl Stream for TimeDelayStream {
230            type Item = Result<Bytes, StreamErr>;
231
232            fn poll_next(
233                mut self: Pin<&mut Self>,
234                cx: &mut Context<'_>,
235            ) -> Poll<Option<Self::Item>> {
236                match self.as_mut().get_mut() {
237                    TimeDelayStream::Start => {
238                        let sleep = sleep(Duration::from_millis(1));
239                        self.as_mut().set(TimeDelayStream::Sleep {
240                            delay: Box::pin(sleep),
241                        });
242                        cx.waker().wake_by_ref();
243                        Poll::Pending
244                    }
245
246                    TimeDelayStream::Sleep { ref mut delay } => {
247                        ready!(delay.poll_unpin(cx));
248                        self.set(TimeDelayStream::Done);
249                        cx.waker().wake_by_ref();
250                        Poll::Pending
251                    }
252
253                    TimeDelayStream::Done => Poll::Ready(Some(Err(StreamErr))),
254                }
255            }
256        }
257
258        let body = BodyStream::new(TimeDelayStream::Start);
259        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
260    }
261}