Skip to main content

watermelon_proto/proto/decoder/
stream.rs

1use bytes::{BufMut, BytesMut};
2
3use crate::{
4    proto::{ServerOp, error::DecoderError},
5    util::CrlfFinder,
6};
7
8use super::DecoderStatus;
9
10const INITIAL_READ_BUF_CAPACITY: usize = 64 * 1024;
11
12#[derive(Debug)]
13pub struct StreamDecoder {
14    read_buf: BytesMut,
15    status: DecoderStatus,
16    crlf: CrlfFinder,
17}
18
19impl StreamDecoder {
20    #[must_use]
21    pub fn new() -> Self {
22        Self {
23            read_buf: BytesMut::with_capacity(INITIAL_READ_BUF_CAPACITY),
24            status: DecoderStatus::ControlLine { last_bytes_read: 0 },
25            crlf: CrlfFinder::new(),
26        }
27    }
28
29    #[must_use]
30    pub fn read_buf(&mut self) -> &mut (impl BufMut + use<>) {
31        &mut self.read_buf
32    }
33
34    /// Decodes the next frame of bytes into a [`ServerOp`].
35    ///
36    /// A `None` variant is returned in case no progress is made,
37    ///
38    /// # Errors
39    ///
40    /// It returns an error if a decoding error occurs.
41    pub fn decode(&mut self) -> Result<Option<ServerOp>, DecoderError> {
42        super::decode(&self.crlf, &mut self.status, &mut self.read_buf)
43    }
44}
45
46impl Default for StreamDecoder {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use bytes::{BufMut as _, Bytes};
55    use claims::{assert_matches, assert_ok_eq};
56
57    use bytestring::ByteString;
58
59    use crate::{
60        StatusCode, Subject,
61        error::ServerError,
62        headers::{HeaderMap, HeaderName, HeaderValue},
63        message::{MessageBase, ServerMessage},
64        proto::{error::DecoderError, server::ServerOp},
65        util::ParseUintError,
66    };
67
68    use super::StreamDecoder;
69
70    #[test]
71    fn decode_ping() {
72        let mut decoder = StreamDecoder::new();
73        decoder.read_buf().put_slice(b"PING\r\n");
74        assert_ok_eq!(decoder.decode(), Some(ServerOp::Ping));
75        assert_ok_eq!(decoder.decode(), None);
76    }
77
78    #[test]
79    fn decode_pong() {
80        let mut decoder = StreamDecoder::new();
81        decoder.read_buf().put_slice(b"PONG\r\n");
82        assert_ok_eq!(decoder.decode(), Some(ServerOp::Pong));
83        assert_ok_eq!(decoder.decode(), None);
84    }
85
86    #[test]
87    fn decode_ok() {
88        let mut decoder = StreamDecoder::new();
89        decoder.read_buf().put_slice(b"+OK\r\n");
90        assert_ok_eq!(decoder.decode(), Some(ServerOp::Success));
91        assert_ok_eq!(decoder.decode(), None);
92    }
93
94    #[test]
95    fn decode_error() {
96        let mut decoder = StreamDecoder::new();
97        decoder
98            .read_buf()
99            .put_slice(b"-ERR 'Authorization Violation'\r\n");
100        assert_ok_eq!(
101            decoder.decode(),
102            Some(ServerOp::Error {
103                error: ServerError::AuthorizationViolation
104            })
105        );
106        assert_ok_eq!(decoder.decode(), None);
107    }
108
109    #[test]
110    fn decode_error_bare_quote() {
111        let mut decoder = StreamDecoder::new();
112        // A single-quote-only message used to underflow the quote
113        // stripping logic (`len() - 1` on an empty buffer).
114        decoder.read_buf().put_slice(b"-ERR '\r\n");
115        assert_matches!(decoder.decode(), Err(DecoderError::InvalidErrorMessage));
116    }
117
118    #[test]
119    fn decode_error_empty_message() {
120        let mut decoder = StreamDecoder::new();
121        decoder.read_buf().put_slice(b"-ERR ''\r\n");
122        assert_ok_eq!(
123            decoder.decode(),
124            Some(ServerOp::Error {
125                error: ServerError::Other {
126                    raw_message: ByteString::new()
127                }
128            })
129        );
130    }
131
132    #[test]
133    fn decode_msg() {
134        let mut decoder = StreamDecoder::new();
135        decoder
136            .read_buf()
137            .put_slice(b"MSG hello.world 1 12\r\nHello World!\r\n");
138        assert_ok_eq!(
139            decoder.decode(),
140            Some(ServerOp::Message {
141                message: ServerMessage {
142                    status_code: None,
143                    status_description: None,
144                    subscription_id: 1.into(),
145                    base: MessageBase {
146                        subject: Subject::from_static("hello.world"),
147                        reply_subject: None,
148                        headers: HeaderMap::new(),
149                        payload: Bytes::from_static(b"Hello World!")
150                    }
151                }
152            })
153        );
154        assert_ok_eq!(decoder.decode(), None);
155    }
156
157    #[test]
158    fn decode_hmsg_status_description() {
159        const HEADERS: &str = "NATS/1.0 409 Batch Completed\r\nNats-Pending-Messages: 3\r\nNats-Pending-Bytes: 0\r\n\r\n";
160
161        let mut decoder = StreamDecoder::new();
162        decoder.read_buf().put_slice(
163            format!(
164                "HMSG _INBOX.abcd 9 {} {}\r\n{HEADERS}\r\n",
165                HEADERS.len(),
166                HEADERS.len()
167            )
168            .as_bytes(),
169        );
170        assert_ok_eq!(
171            decoder.decode(),
172            Some(ServerOp::Message {
173                message: ServerMessage {
174                    status_code: Some(StatusCode::CONFLICT),
175                    status_description: Some(ByteString::from_static("Batch Completed")),
176                    subscription_id: 9.into(),
177                    base: MessageBase {
178                        subject: Subject::from_static("_INBOX.abcd"),
179                        reply_subject: None,
180                        headers: HeaderMap::from_iter([
181                            (
182                                HeaderName::from_static("Nats-Pending-Messages"),
183                                HeaderValue::from_static("3")
184                            ),
185                            (
186                                HeaderName::from_static("Nats-Pending-Bytes"),
187                                HeaderValue::from_static("0")
188                            ),
189                        ]),
190                        payload: Bytes::new(),
191                    }
192                }
193            })
194        );
195        assert_ok_eq!(decoder.decode(), None);
196    }
197
198    #[test]
199    fn decode_hmsg_status_without_description() {
200        const HEADERS: &str = "NATS/1.0 100\r\n\r\n";
201
202        let mut decoder = StreamDecoder::new();
203        decoder.read_buf().put_slice(
204            format!(
205                "HMSG _INBOX.abcd 9 {} {}\r\n{HEADERS}\r\n",
206                HEADERS.len(),
207                HEADERS.len()
208            )
209            .as_bytes(),
210        );
211        assert_ok_eq!(
212            decoder.decode(),
213            Some(ServerOp::Message {
214                message: ServerMessage {
215                    status_code: Some(StatusCode::IDLE_HEARTBEAT),
216                    status_description: None,
217                    subscription_id: 9.into(),
218                    base: MessageBase {
219                        subject: Subject::from_static("_INBOX.abcd"),
220                        reply_subject: None,
221                        headers: HeaderMap::new(),
222                        payload: Bytes::new(),
223                    }
224                }
225            })
226        );
227        assert_ok_eq!(decoder.decode(), None);
228    }
229
230    #[test]
231    fn decode_hmsg_status_invalid_description_separator() {
232        const HEADERS: &str = "NATS/1.0 409XBatch Completed\r\n\r\n";
233
234        let mut decoder = StreamDecoder::new();
235        decoder.read_buf().put_slice(
236            format!(
237                "HMSG _INBOX.abcd 9 {} {}\r\n{HEADERS}\r\n",
238                HEADERS.len(),
239                HEADERS.len()
240            )
241            .as_bytes(),
242        );
243        assert_matches!(decoder.decode(), Err(DecoderError::InvalidHead));
244    }
245
246    #[test]
247    fn decode_hmsg_non_utf8_header_value() {
248        const HEADERS: &[u8] = b"NATS/1.0\r\nX-Binary: \x00\x80\xffabc\r\n\r\n";
249
250        let mut decoder = StreamDecoder::new();
251        decoder.read_buf().put_slice(
252            format!("HMSG _INBOX.abcd 9 {} {}\r\n", HEADERS.len(), HEADERS.len()).as_bytes(),
253        );
254        decoder.read_buf().put_slice(HEADERS);
255        decoder.read_buf().put_slice(b"\r\n");
256        assert_ok_eq!(
257            decoder.decode(),
258            Some(ServerOp::Message {
259                message: ServerMessage {
260                    status_code: None,
261                    status_description: None,
262                    subscription_id: 9.into(),
263                    base: MessageBase {
264                        subject: Subject::from_static("_INBOX.abcd"),
265                        reply_subject: None,
266                        headers: HeaderMap::from_iter([(
267                            HeaderName::from_static("X-Binary"),
268                            HeaderValue::from_bytes(b"\x00\x80\xffabc").unwrap(),
269                        )]),
270                        payload: Bytes::new(),
271                    }
272                }
273            })
274        );
275        assert_ok_eq!(decoder.decode(), None);
276    }
277
278    #[test]
279    fn decode_hmsg_empty_header_value() {
280        const HEADERS: &str = "NATS/1.0\r\nA:\r\nB: \r\n\r\n";
281
282        let mut decoder = StreamDecoder::new();
283        decoder.read_buf().put_slice(
284            format!(
285                "HMSG _INBOX.abcd 9 {} {}\r\n{HEADERS}\r\n",
286                HEADERS.len(),
287                HEADERS.len()
288            )
289            .as_bytes(),
290        );
291        assert_ok_eq!(
292            decoder.decode(),
293            Some(ServerOp::Message {
294                message: ServerMessage {
295                    status_code: None,
296                    status_description: None,
297                    subscription_id: 9.into(),
298                    base: MessageBase {
299                        subject: Subject::from_static("_INBOX.abcd"),
300                        reply_subject: None,
301                        headers: HeaderMap::from_iter([
302                            (
303                                HeaderName::from_static("A"),
304                                HeaderValue::from_bytes(b"").unwrap(),
305                            ),
306                            (
307                                HeaderName::from_static("B"),
308                                HeaderValue::from_bytes(b"").unwrap(),
309                            ),
310                        ]),
311                        payload: Bytes::new(),
312                    }
313                }
314            })
315        );
316        assert_ok_eq!(decoder.decode(), None);
317    }
318
319    #[test]
320    fn msg_huge_payload_len() {
321        let mut decoder = StreamDecoder::new();
322        decoder
323            .read_buf()
324            .put_slice(b"MSG hello.world 1 18446744073709551615\r\nHello");
325        // The decoder must reject the impossibly long declared payload
326        // instead of waiting for it (or, historically, overflowing while
327        // checking the buffered length and panicking in `split_to`).
328        assert_matches!(
329            decoder.decode(),
330            Err(DecoderError::InvalidPayloadLength(ParseUintError::Overflow))
331        );
332    }
333
334    #[test]
335    fn msg_payload_len_over_limit() {
336        let mut decoder = StreamDecoder::new();
337        decoder
338            .read_buf()
339            .put_slice(b"MSG hello.world 1 134217729\r\n");
340        assert_matches!(
341            decoder.decode(),
342            Err(DecoderError::InvalidPayloadLength(ParseUintError::Overflow))
343        );
344    }
345
346    #[test]
347    fn hmsg_total_len_over_limit() {
348        let mut decoder = StreamDecoder::new();
349        decoder
350            .read_buf()
351            .put_slice(b"HMSG hello.world 1 100 134217729\r\n");
352        assert_matches!(
353            decoder.decode(),
354            Err(DecoderError::InvalidPayloadLength(ParseUintError::Overflow))
355        );
356    }
357
358    #[test]
359    fn head_too_long() {
360        let mut decoder = StreamDecoder::new();
361        decoder.read_buf().put_bytes(0, 200_000);
362        assert_matches!(
363            decoder.decode(),
364            Err(DecoderError::HeadTooLong { len: 200_000 })
365        );
366    }
367}