Skip to main content

gproxy_protocol/adapt/generate/stream/
reader.rs

1//! Transport framing only. Native event validation belongs to the concrete pair.
2
3use crate::{
4    HttpBody,
5    codec::{self, CodecError, CodecErrorKind, CodecErrorStage, CodecLimits},
6    connection::ByteStream,
7    wire::DeclaredFields,
8};
9use bytes::{Buf, Bytes};
10use futures_util::StreamExt;
11use serde::de::DeserializeOwned;
12use std::collections::VecDeque;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SourceFraming {
16    Sse,
17    JsonArray,
18    Ndjson,
19}
20
21#[derive(Debug)]
22pub enum NativeFrame<T> {
23    Event { name: Option<String>, value: T },
24    Done,
25}
26
27enum Decoder {
28    Sse(codec::SseDecoder),
29    Array(codec::JsonArrayDecoder),
30    Ndjson(codec::NdjsonDecoder),
31}
32
33enum Frame {
34    Sse(codec::SseFrame),
35    Json(serde_json::Value),
36}
37
38/// Reads only until another framed event is available. Dropping `next` while
39/// awaiting another body chunk retains decoder state and the current chunk.
40/// A decoder/transport error poisons this reader and releases its transport;
41/// transport is never replayed. Large/empty ready input cooperatively yields
42/// so cancellation and host deadlines remain observable.
43pub struct NativeReader {
44    stream: Option<ByteStream>,
45    pending: Bytes,
46    decoder: Decoder,
47    queued: VecDeque<Frame>,
48    limits: CodecLimits,
49    received: u64,
50
51    eof: bool,
52    failed: bool,
53}
54
55impl NativeReader {
56    pub fn new(body: HttpBody, framing: SourceFraming, limits: CodecLimits) -> Self {
57        let (stream, pending) = match body {
58            HttpBody::Bytes(bytes) => (None, bytes),
59            HttpBody::Stream(stream) => (Some(stream), Bytes::new()),
60        };
61        Self {
62            received: pending.len() as u64,
63            stream,
64            pending,
65            decoder: match framing {
66                SourceFraming::Sse => Decoder::Sse(codec::SseDecoder::new(limits)),
67                SourceFraming::JsonArray => Decoder::Array(codec::JsonArrayDecoder::new(limits)),
68                SourceFraming::Ndjson => Decoder::Ndjson(codec::NdjsonDecoder::new(limits)),
69            },
70            queued: VecDeque::new(),
71            limits,
72
73            eof: false,
74            failed: false,
75        }
76    }
77    pub async fn next<T: DeserializeOwned + DeclaredFields>(
78        &mut self,
79    ) -> Result<Option<NativeFrame<T>>, CodecError> {
80        if self.failed {
81            return Err(error(
82                CodecErrorKind::Invalid,
83                "native reader terminated after failure",
84            ));
85        }
86        let result = self.next_inner().await;
87        if result.is_err() {
88            self.failed = true;
89            self.stream = None;
90            self.pending = Bytes::new();
91            self.queued.clear();
92        }
93        result
94    }
95    async fn next_inner<T: DeserializeOwned + DeclaredFields>(
96        &mut self,
97    ) -> Result<Option<NativeFrame<T>>, CodecError> {
98        if self.received > self.limits.max_body_bytes {
99            return Err(error(
100                CodecErrorKind::Limit,
101                "native body byte limit exceeded",
102            ));
103        }
104        let mut work = 0usize;
105        loop {
106            if let Some(frame) = self.queued.pop_front() {
107                return match frame {
108                    Frame::Sse(codec::SseFrame::Done) => Ok(Some(NativeFrame::Done)),
109                    Frame::Sse(codec::SseFrame::Event(event)) => {
110                        let value: T = codec::decode_json(event.data.as_bytes(), self.limits)?;
111                        Ok(Some(NativeFrame::Event {
112                            name: event.event,
113                            value: value.into_declared(),
114                        }))
115                    }
116                    Frame::Json(value) => {
117                        let value: T = serde_json::from_value(value).map_err(|e| {
118                            CodecError::with_source(
119                                CodecErrorKind::Json,
120                                CodecErrorStage::Stream,
121                                "invalid native event",
122                                e,
123                            )
124                        })?;
125                        Ok(Some(NativeFrame::Event {
126                            name: None,
127                            value: value.into_declared(),
128                        }))
129                    }
130                };
131            }
132            if work >= 1024 {
133                yield_once().await;
134                work = 0;
135            }
136            if !self.pending.is_empty() {
137                // Stop exactly when a value becomes available. Feeding a larger
138                // array batch could let a later syntax error swallow a valid
139                // prefix event from the same transport chunk. SSE/NDJSON need
140                // at most one physical line to dispatch the next frame.
141                let count = match &self.decoder {
142                    Decoder::Array(_) => 1,
143                    _ => self
144                        .pending
145                        .iter()
146                        .position(|b| *b == b'\n' || *b == b'\r')
147                        .map_or(self.pending.len(), |n| n + 1),
148                };
149                match &mut self.decoder {
150                    Decoder::Sse(d) => self
151                        .queued
152                        .extend(d.push(&self.pending[..count])?.into_iter().map(Frame::Sse)),
153                    Decoder::Array(d) => self
154                        .queued
155                        .extend(d.push(&self.pending[..count])?.into_iter().map(Frame::Json)),
156                    Decoder::Ndjson(d) => self
157                        .queued
158                        .extend(d.push(&self.pending[..count])?.into_iter().map(Frame::Json)),
159                }
160                self.pending.advance(count);
161                work += 1;
162                continue;
163            }
164            if self.eof {
165                return Ok(None);
166            }
167            if let Some(stream) = &mut self.stream {
168                if let Some(chunk) = stream.next().await {
169                    let chunk = chunk.map_err(|e| {
170                        CodecError::with_source(
171                            CodecErrorKind::Transport,
172                            CodecErrorStage::Stream,
173                            "native stream transport failed",
174                            e,
175                        )
176                    })?;
177                    self.received = self
178                        .received
179                        .checked_add(chunk.len() as u64)
180                        .ok_or_else(|| error(CodecErrorKind::Limit, "native body size overflow"))?;
181                    if self.received > self.limits.max_body_bytes {
182                        return Err(error(
183                            CodecErrorKind::Limit,
184                            "native body byte limit exceeded",
185                        ));
186                    }
187                    self.pending = chunk;
188                    work += 1;
189                    continue;
190                }
191                self.stream = None;
192            }
193            self.eof = true;
194            match &mut self.decoder {
195                Decoder::Sse(d) => self.queued.extend(d.finish()?.into_iter().map(Frame::Sse)),
196                Decoder::Array(d) => d.finish()?,
197                Decoder::Ndjson(d) => self.queued.extend(d.finish()?.into_iter().map(Frame::Json)),
198            }
199        }
200    }
201}
202
203fn error(kind: CodecErrorKind, message: &'static str) -> CodecError {
204    CodecError::new(kind, CodecErrorStage::Stream, message)
205}
206
207async fn yield_once() {
208    let mut yielded = false;
209    std::future::poll_fn(move |cx| {
210        if yielded {
211            std::task::Poll::Ready(())
212        } else {
213            yielded = true;
214            cx.waker().wake_by_ref();
215            std::task::Poll::Pending
216        }
217    })
218    .await
219}