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, 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
23// TODO: from_infallible method
24
25impl<S, E> BodyStream<S>
26where
27    S: Stream<Item = Result<Bytes, E>>,
28    E: Into<Box<dyn StdError>> + 'static,
29{
30    #[inline]
31    pub fn new(stream: S) -> Self {
32        BodyStream { stream }
33    }
34}
35
36impl<S, E> MessageBody for BodyStream<S>
37where
38    S: Stream<Item = Result<Bytes, E>>,
39    E: Into<Box<dyn StdError>> + 'static,
40{
41    type Error = E;
42
43    #[inline]
44    fn size(&self) -> BodySize {
45        BodySize::Stream
46    }
47
48    /// Attempts to pull out the next value of the underlying [`Stream`].
49    ///
50    /// Empty values are skipped to prevent [`BodyStream`]'s transmission being ended on a
51    /// zero-length chunk, but rather proceed until the underlying [`Stream`] ends.
52    fn poll_next(
53        mut self: Pin<&mut Self>,
54        cx: &mut Context<'_>,
55    ) -> Poll<Option<Result<Bytes, Self::Error>>> {
56        loop {
57            let stream = self.as_mut().project().stream;
58
59            let chunk = match ready!(stream.poll_next(cx)) {
60                Some(Ok(ref bytes)) if bytes.is_empty() => continue,
61                opt => opt,
62            };
63
64            return Poll::Ready(chunk);
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use std::{convert::Infallible, pin::pin, time::Duration};
72
73    use actix_utils::future::poll_fn;
74    use derive_more::{Display, Error};
75    use futures_core::ready;
76    use futures_util::{stream, FutureExt as _};
77    use pin_project_lite::pin_project;
78    use static_assertions::{assert_impl_all, assert_not_impl_any};
79    use tokio::time::{sleep, Sleep};
80
81    use super::*;
82    use crate::body::to_bytes;
83
84    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, crate::Error>>>: MessageBody);
85    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, &'static str>>>: MessageBody);
86    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, &'static str>>>: MessageBody);
87    assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);
88    assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);
89
90    assert_not_impl_any!(BodyStream<stream::Empty<Bytes>>: MessageBody);
91    assert_not_impl_any!(BodyStream<stream::Repeat<Bytes>>: MessageBody);
92    // crate::Error is not Clone
93    assert_not_impl_any!(BodyStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
94
95    #[actix_rt::test]
96    async fn skips_empty_chunks() {
97        let body = BodyStream::new(stream::iter(
98            ["1", "", "2"]
99                .iter()
100                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),
101        ));
102        let mut body = pin!(body);
103
104        assert_eq!(
105            poll_fn(|cx| body.as_mut().poll_next(cx))
106                .await
107                .unwrap()
108                .ok(),
109            Some(Bytes::from("1")),
110        );
111        assert_eq!(
112            poll_fn(|cx| body.as_mut().poll_next(cx))
113                .await
114                .unwrap()
115                .ok(),
116            Some(Bytes::from("2")),
117        );
118    }
119
120    #[actix_rt::test]
121    async fn read_to_bytes() {
122        let body = BodyStream::new(stream::iter(
123            ["1", "", "2"]
124                .iter()
125                .map(|&v| Ok::<_, Infallible>(Bytes::from(v))),
126        ));
127
128        assert_eq!(to_bytes(body).await.ok(), Some(Bytes::from("12")));
129    }
130    #[derive(Debug, Display, Error)]
131    #[display("stream error")]
132    struct StreamErr;
133
134    #[actix_rt::test]
135    async fn stream_immediate_error() {
136        let body = BodyStream::new(stream::once(async { Err(StreamErr) }));
137        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
138    }
139
140    #[actix_rt::test]
141    async fn stream_string_error() {
142        // `&'static str` does not impl `Error`
143        // but it does impl `Into<Box<dyn Error>>`
144
145        let body = BodyStream::new(stream::once(async { Err("stringy error") }));
146        assert!(matches!(to_bytes(body).await, Err("stringy error")));
147    }
148
149    #[actix_rt::test]
150    async fn stream_boxed_error() {
151        // `Box<dyn Error>` does not impl `Error`
152        // but it does impl `Into<Box<dyn Error>>`
153
154        let body = BodyStream::new(stream::once(async {
155            Err(Box::<dyn StdError>::from("stringy error"))
156        }));
157
158        assert_eq!(
159            to_bytes(body).await.unwrap_err().to_string(),
160            "stringy error"
161        );
162    }
163
164    #[actix_rt::test]
165    async fn stream_delayed_error() {
166        let body = BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)]));
167        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
168
169        pin_project! {
170            #[derive(Debug)]
171            #[project = TimeDelayStreamProj]
172            enum TimeDelayStream {
173                Start,
174                Sleep { delay: Pin<Box<Sleep>> },
175                Done,
176            }
177        }
178
179        impl Stream for TimeDelayStream {
180            type Item = Result<Bytes, StreamErr>;
181
182            fn poll_next(
183                mut self: Pin<&mut Self>,
184                cx: &mut Context<'_>,
185            ) -> Poll<Option<Self::Item>> {
186                match self.as_mut().get_mut() {
187                    TimeDelayStream::Start => {
188                        let sleep = sleep(Duration::from_millis(1));
189                        self.as_mut().set(TimeDelayStream::Sleep {
190                            delay: Box::pin(sleep),
191                        });
192                        cx.waker().wake_by_ref();
193                        Poll::Pending
194                    }
195
196                    TimeDelayStream::Sleep { ref mut delay } => {
197                        ready!(delay.poll_unpin(cx));
198                        self.set(TimeDelayStream::Done);
199                        cx.waker().wake_by_ref();
200                        Poll::Pending
201                    }
202
203                    TimeDelayStream::Done => Poll::Ready(Some(Err(StreamErr))),
204                }
205            }
206        }
207
208        let body = BodyStream::new(TimeDelayStream::Start);
209        assert!(matches!(to_bytes(body).await, Err(StreamErr)));
210    }
211}