Skip to main content

http_streams_core/
stream.rs

1//! Driving an [`ItemEncoder`] or a [`Decoder`] over a stream.
2//!
3//! These two functions are the API boundary that keeps `tokio-util` an implementation detail:
4//! a dependent crate hands in a byte stream and gets items back (or vice versa) without ever
5//! naming `FramedRead`, `StreamReader` or [`Decoder`].
6//!
7//! [`Decoder`]: tokio_util::codec::Decoder
8
9use crate::error::StreamError;
10use crate::format::{DecodeOptions, FrameParser, ItemEncoder};
11use bytes::{Bytes, BytesMut};
12use futures::stream::{Stream, StreamExt};
13use tokio_util::codec::{Decoder, FramedRead};
14use tokio_util::io::StreamReader;
15
16/// Where an encode run has got to.
17///
18/// The epilogue is reachable only from a clean end of the item stream. After an error the run
19/// jumps straight to `Done`, because a body that stopped early must not be closed with a
20/// well-formed terminator: a truncated JSON array ending in `]` would parse as complete and
21/// silently lose items.
22enum Phase {
23    Prologue,
24    Items,
25    Epilogue,
26    Done,
27}
28
29struct EncodeState<S, ENC> {
30    inner: S,
31    encoder: ENC,
32    index: u64,
33    phase: Phase,
34}
35
36/// Encode a stream of items into a stream of body chunks.
37///
38/// Empty chunks are swallowed rather than yielded, so a format with no prologue or epilogue
39/// does not emit zero-length frames.
40///
41/// The returned stream is fused, so an extra poll after the end yields `None` rather than
42/// panicking. Bodies are polled once more after their last frame, and errors reach the
43/// terminal phase early, which makes the over-poll ordinary rather than exceptional.
44pub fn encode_stream<'b, S, T, ENC>(
45    stream: S,
46    encoder: ENC,
47) -> impl Stream<Item = Result<Bytes, StreamError>> + Send + 'b
48where
49    S: Stream<Item = Result<T, StreamError>> + Send + Unpin + 'b,
50    T: Send + 'b,
51    ENC: ItemEncoder<T> + Send + 'b,
52{
53    let state = EncodeState {
54        inner: stream,
55        encoder,
56        index: 0,
57        phase: Phase::Prologue,
58    };
59
60    futures::stream::unfold(state, |mut st| async move {
61        loop {
62            match st.phase {
63                Phase::Prologue => {
64                    let mut buf = BytesMut::new();
65                    match st.encoder.prologue(&mut buf) {
66                        Err(e) => {
67                            st.phase = Phase::Done;
68                            return Some((Err(e), st));
69                        }
70                        Ok(()) => {
71                            st.phase = Phase::Items;
72                            if !buf.is_empty() {
73                                return Some((Ok(buf.freeze()), st));
74                            }
75                        }
76                    }
77                }
78                Phase::Items => match st.inner.next().await {
79                    Some(Ok(item)) => {
80                        let mut buf = BytesMut::new();
81                        let index = st.index;
82                        match st.encoder.encode(&item, index, &mut buf) {
83                            Err(e) => {
84                                st.phase = Phase::Done;
85                                return Some((Err(e), st));
86                            }
87                            Ok(()) => {
88                                st.index += 1;
89                                if !buf.is_empty() {
90                                    return Some((Ok(buf.freeze()), st));
91                                }
92                            }
93                        }
94                    }
95                    // The source's own error is forwarded verbatim, so that every error in the
96                    // pipeline surfaces at exactly one place downstream.
97                    Some(Err(e)) => {
98                        st.phase = Phase::Done;
99                        return Some((Err(e), st));
100                    }
101                    None => st.phase = Phase::Epilogue,
102                },
103                Phase::Epilogue => {
104                    let mut buf = BytesMut::new();
105                    let result = st.encoder.epilogue(&mut buf);
106                    st.phase = Phase::Done;
107                    match result {
108                        Err(e) => return Some((Err(e), st)),
109                        Ok(()) => {
110                            if !buf.is_empty() {
111                                return Some((Ok(buf.freeze()), st));
112                            }
113                        }
114                    }
115                }
116                Phase::Done => return None,
117            }
118        }
119    })
120    // `unfold` drops its state when the closure returns `None` and panics on the next poll, so
121    // the fuse is what makes the terminal state repeatable.
122    .fuse()
123}
124
125/// Decode a stream of body chunks into a stream of items.
126///
127/// The byte stream's error type is [`std::io::Error`] because that is what both hyper and
128/// reqwest body streams already produce, and what [`StreamReader`] requires.
129///
130/// Framing errors and per-record deserialisation errors are flattened into one stream here,
131/// but the difference stays observable: after a framing error the stream ends, after a
132/// deserialisation error it continues with the next record.
133///
134/// Note what is *not* required: `T: 'b`. Neither the framer nor the parser mentions `T` in its
135/// own type for formats that can separate the two, so callers are not forced to add an
136/// outlives bound to their public signatures.
137pub fn decode_stream<'b, S, F, D, P, T>(
138    stream: S,
139    framer: D,
140    parser: P,
141    options: &DecodeOptions,
142) -> impl Stream<Item = Result<T, StreamError>> + Send + 'b
143where
144    S: Stream<Item = Result<Bytes, std::io::Error>> + Send + 'b,
145    D: Decoder<Item = F, Error = StreamError> + Send + 'b,
146    P: FrameParser<F, T> + Send + 'b,
147    F: 'b,
148{
149    let reader = StreamReader::new(Box::pin(stream));
150    FramedRead::with_capacity(reader, framer, options.buf_capacity).map(
151        move |framed| match framed {
152            Ok(frame) => parser.parse(frame),
153            Err(err) => Err(err),
154        },
155    )
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::error::StreamErrorKind;
162
163    /// A format that brackets its items, so prologue/epilogue/index are all exercised.
164    struct Bracketed {
165        fail_at: Option<u64>,
166    }
167
168    impl ItemEncoder<u32> for Bracketed {
169        fn prologue(&mut self, buf: &mut BytesMut) -> Result<(), StreamError> {
170            buf.extend_from_slice(b"[");
171            Ok(())
172        }
173
174        fn encode(
175            &mut self,
176            item: &u32,
177            index: u64,
178            buf: &mut BytesMut,
179        ) -> Result<(), StreamError> {
180            if self.fail_at == Some(index) {
181                return Err(StreamError::new(
182                    StreamErrorKind::CodecError,
183                    None,
184                    Some("boom".into()),
185                ));
186            }
187            if index != 0 {
188                buf.extend_from_slice(b",");
189            }
190            buf.extend_from_slice(item.to_string().as_bytes());
191            Ok(())
192        }
193
194        fn epilogue(&mut self, buf: &mut BytesMut) -> Result<(), StreamError> {
195            buf.extend_from_slice(b"]");
196            Ok(())
197        }
198    }
199
200    async fn collect(s: impl Stream<Item = Result<Bytes, StreamError>>) -> (Vec<u8>, usize) {
201        let items: Vec<_> = Box::pin(s).collect().await;
202        let errors = items.iter().filter(|i| i.is_err()).count();
203        let mut out = Vec::new();
204        for i in items.into_iter().flatten() {
205            out.extend_from_slice(&i);
206        }
207        (out, errors)
208    }
209
210    #[tokio::test]
211    async fn encodes_prologue_items_and_epilogue() {
212        let source = futures::stream::iter(vec![Ok(1u32), Ok(2), Ok(3)]);
213        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
214        assert_eq!(String::from_utf8(bytes).unwrap(), "[1,2,3]");
215        assert_eq!(errors, 0);
216    }
217
218    #[tokio::test]
219    async fn empty_source_still_brackets() {
220        let source = futures::stream::iter(Vec::<Result<u32, StreamError>>::new());
221        let (bytes, _) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
222        assert_eq!(String::from_utf8(bytes).unwrap(), "[]");
223    }
224
225    /// The closing bracket must NOT appear: a truncated body that looks complete is worse than
226    /// one that is visibly truncated.
227    #[tokio::test]
228    async fn encoder_error_suppresses_the_epilogue() {
229        let source = futures::stream::iter(vec![Ok(1u32), Ok(2), Ok(3)]);
230        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: Some(1) })).await;
231        assert_eq!(String::from_utf8(bytes).unwrap(), "[1");
232        assert_eq!(errors, 1);
233    }
234
235    #[tokio::test]
236    async fn source_error_is_forwarded_and_suppresses_the_epilogue() {
237        let source = futures::stream::iter(vec![
238            Ok(1u32),
239            Err(StreamError::new(
240                StreamErrorKind::InputOutputError,
241                None,
242                None,
243            )),
244        ]);
245        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
246        assert_eq!(String::from_utf8(bytes).unwrap(), "[1");
247        assert_eq!(errors, 1);
248    }
249
250    /// A body outlives its own last frame: hyper polls it again to learn whether trailers
251    /// follow, because `is_end_stream` cannot promise otherwise. The terminal state has to
252    /// survive being asked more than once.
253    #[tokio::test]
254    async fn polling_after_completion_yields_none() {
255        let source = futures::stream::iter(vec![Ok(1u32), Ok(2)]);
256        let mut stream = Box::pin(encode_stream(source, Bracketed { fail_at: None }));
257        while stream.next().await.is_some() {}
258        assert!(stream.next().await.is_none());
259        assert!(stream.next().await.is_none());
260    }
261
262    /// An encoder error skips the epilogue, so the terminal state arrives one poll after the
263    /// error rather than at the end of the source.
264    #[tokio::test]
265    async fn polling_after_an_error_yields_none() {
266        let source = futures::stream::iter(vec![Ok(1u32), Ok(2), Ok(3)]);
267        let mut stream = Box::pin(encode_stream(source, Bracketed { fail_at: Some(1) }));
268        while stream.next().await.is_some() {}
269        assert!(stream.next().await.is_none());
270        assert!(stream.next().await.is_none());
271    }
272}