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, InfallibleStream, MessageBody};
12
13pin_project! {
14 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 #[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 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 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 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 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}