actix_http/body/
body_stream.rs1use 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 pub struct BodyStream<S> {
18 #[pin]
19 stream: S,
20 }
21}
22
23impl<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 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 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 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 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}