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.
40pub fn encode_stream<'b, S, T, ENC>(
41    stream: S,
42    encoder: ENC,
43) -> impl Stream<Item = Result<Bytes, StreamError>> + Send + 'b
44where
45    S: Stream<Item = Result<T, StreamError>> + Send + Unpin + 'b,
46    T: Send + 'b,
47    ENC: ItemEncoder<T> + Send + 'b,
48{
49    let state = EncodeState {
50        inner: stream,
51        encoder,
52        index: 0,
53        phase: Phase::Prologue,
54    };
55
56    futures::stream::unfold(state, |mut st| async move {
57        loop {
58            match st.phase {
59                Phase::Prologue => {
60                    let mut buf = BytesMut::new();
61                    match st.encoder.prologue(&mut buf) {
62                        Err(e) => {
63                            st.phase = Phase::Done;
64                            return Some((Err(e), st));
65                        }
66                        Ok(()) => {
67                            st.phase = Phase::Items;
68                            if !buf.is_empty() {
69                                return Some((Ok(buf.freeze()), st));
70                            }
71                        }
72                    }
73                }
74                Phase::Items => match st.inner.next().await {
75                    Some(Ok(item)) => {
76                        let mut buf = BytesMut::new();
77                        let index = st.index;
78                        match st.encoder.encode(&item, index, &mut buf) {
79                            Err(e) => {
80                                st.phase = Phase::Done;
81                                return Some((Err(e), st));
82                            }
83                            Ok(()) => {
84                                st.index += 1;
85                                if !buf.is_empty() {
86                                    return Some((Ok(buf.freeze()), st));
87                                }
88                            }
89                        }
90                    }
91                    // The source's own error is forwarded verbatim, so that every error in the
92                    // pipeline surfaces at exactly one place downstream.
93                    Some(Err(e)) => {
94                        st.phase = Phase::Done;
95                        return Some((Err(e), st));
96                    }
97                    None => st.phase = Phase::Epilogue,
98                },
99                Phase::Epilogue => {
100                    let mut buf = BytesMut::new();
101                    let result = st.encoder.epilogue(&mut buf);
102                    st.phase = Phase::Done;
103                    match result {
104                        Err(e) => return Some((Err(e), st)),
105                        Ok(()) => {
106                            if !buf.is_empty() {
107                                return Some((Ok(buf.freeze()), st));
108                            }
109                        }
110                    }
111                }
112                Phase::Done => return None,
113            }
114        }
115    })
116}
117
118/// Decode a stream of body chunks into a stream of items.
119///
120/// The byte stream's error type is [`std::io::Error`] because that is what both hyper and
121/// reqwest body streams already produce, and what [`StreamReader`] requires.
122///
123/// Framing errors and per-record deserialisation errors are flattened into one stream here,
124/// but the difference stays observable: after a framing error the stream ends, after a
125/// deserialisation error it continues with the next record.
126///
127/// Note what is *not* required: `T: 'b`. Neither the framer nor the parser mentions `T` in its
128/// own type for formats that can separate the two, so callers are not forced to add an
129/// outlives bound to their public signatures.
130pub fn decode_stream<'b, S, F, D, P, T>(
131    stream: S,
132    framer: D,
133    parser: P,
134    options: &DecodeOptions,
135) -> impl Stream<Item = Result<T, StreamError>> + Send + 'b
136where
137    S: Stream<Item = Result<Bytes, std::io::Error>> + Send + 'b,
138    D: Decoder<Item = F, Error = StreamError> + Send + 'b,
139    P: FrameParser<F, T> + Send + 'b,
140    F: 'b,
141{
142    let reader = StreamReader::new(Box::pin(stream));
143    FramedRead::with_capacity(reader, framer, options.buf_capacity).map(
144        move |framed| match framed {
145            Ok(frame) => parser.parse(frame),
146            Err(err) => Err(err),
147        },
148    )
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154    use crate::error::StreamErrorKind;
155
156    /// A format that brackets its items, so prologue/epilogue/index are all exercised.
157    struct Bracketed {
158        fail_at: Option<u64>,
159    }
160
161    impl ItemEncoder<u32> for Bracketed {
162        fn prologue(&mut self, buf: &mut BytesMut) -> Result<(), StreamError> {
163            buf.extend_from_slice(b"[");
164            Ok(())
165        }
166
167        fn encode(
168            &mut self,
169            item: &u32,
170            index: u64,
171            buf: &mut BytesMut,
172        ) -> Result<(), StreamError> {
173            if self.fail_at == Some(index) {
174                return Err(StreamError::new(
175                    StreamErrorKind::CodecError,
176                    None,
177                    Some("boom".into()),
178                ));
179            }
180            if index != 0 {
181                buf.extend_from_slice(b",");
182            }
183            buf.extend_from_slice(item.to_string().as_bytes());
184            Ok(())
185        }
186
187        fn epilogue(&mut self, buf: &mut BytesMut) -> Result<(), StreamError> {
188            buf.extend_from_slice(b"]");
189            Ok(())
190        }
191    }
192
193    async fn collect(s: impl Stream<Item = Result<Bytes, StreamError>>) -> (Vec<u8>, usize) {
194        let items: Vec<_> = Box::pin(s).collect().await;
195        let errors = items.iter().filter(|i| i.is_err()).count();
196        let mut out = Vec::new();
197        for i in items.into_iter().flatten() {
198            out.extend_from_slice(&i);
199        }
200        (out, errors)
201    }
202
203    #[tokio::test]
204    async fn encodes_prologue_items_and_epilogue() {
205        let source = futures::stream::iter(vec![Ok(1u32), Ok(2), Ok(3)]);
206        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
207        assert_eq!(String::from_utf8(bytes).unwrap(), "[1,2,3]");
208        assert_eq!(errors, 0);
209    }
210
211    #[tokio::test]
212    async fn empty_source_still_brackets() {
213        let source = futures::stream::iter(Vec::<Result<u32, StreamError>>::new());
214        let (bytes, _) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
215        assert_eq!(String::from_utf8(bytes).unwrap(), "[]");
216    }
217
218    /// The closing bracket must NOT appear: a truncated body that looks complete is worse than
219    /// one that is visibly truncated.
220    #[tokio::test]
221    async fn encoder_error_suppresses_the_epilogue() {
222        let source = futures::stream::iter(vec![Ok(1u32), Ok(2), Ok(3)]);
223        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: Some(1) })).await;
224        assert_eq!(String::from_utf8(bytes).unwrap(), "[1");
225        assert_eq!(errors, 1);
226    }
227
228    #[tokio::test]
229    async fn source_error_is_forwarded_and_suppresses_the_epilogue() {
230        let source = futures::stream::iter(vec![
231            Ok(1u32),
232            Err(StreamError::new(
233                StreamErrorKind::InputOutputError,
234                None,
235                None,
236            )),
237        ]);
238        let (bytes, errors) = collect(encode_stream(source, Bracketed { fail_at: None })).await;
239        assert_eq!(String::from_utf8(bytes).unwrap(), "[1");
240        assert_eq!(errors, 1);
241    }
242}