1use serde_json::Value;
11use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader};
12
13pub const MAX_FRAME_BYTES: usize = 16 * 1024 * 1024;
16
17#[derive(Debug)]
19pub enum Inbound {
20 Frame(Value),
21 Violation(Violation),
22}
23
24#[derive(Debug, PartialEq, Eq)]
25pub enum Violation {
26 TooLarge,
27 InvalidJson,
28}
29
30#[derive(Debug)]
37pub struct FrameReader<R> {
38 reader: BufReader<R>,
39 max_frame: usize,
40 buf: Vec<u8>,
41 discarding: bool,
43}
44
45impl<R: AsyncRead + Unpin> FrameReader<R> {
46 pub fn new(reader: R, max_frame: usize) -> Self {
47 debug_assert!(max_frame > 0);
48 Self {
49 reader: BufReader::new(reader),
50 max_frame,
51 buf: Vec::new(),
52 discarding: false,
53 }
54 }
55
56 pub fn into_inner(self) -> BufReader<R> {
63 self.reader
64 }
65
66 pub async fn next(&mut self) -> std::io::Result<Option<Inbound>> {
72 let mut byte = [0u8; 1];
73 loop {
74 let n = self.reader.read(&mut byte).await?;
75 if n == 0 {
76 if std::mem::take(&mut self.discarding) {
77 return Ok(Some(Inbound::Violation(Violation::TooLarge)));
78 }
79 return Ok(None);
80 }
81 if byte[0] == b'\n' {
82 if std::mem::take(&mut self.discarding) {
83 return Ok(Some(Inbound::Violation(Violation::TooLarge)));
84 }
85 let line = std::mem::take(&mut self.buf);
88 return Ok(Some(match serde_json::from_slice::<Value>(&line) {
90 Ok(v) => Inbound::Frame(v),
91 Err(_) => Inbound::Violation(Violation::InvalidJson),
92 }));
93 }
94 if self.discarding {
95 continue;
96 }
97 if self.buf.len() >= self.max_frame {
98 self.discarding = true;
99 self.buf.clear();
100 continue;
101 }
102 self.buf.push(byte[0]);
103 }
104 }
105}
106
107pub async fn write_frame<W: AsyncWrite + Unpin>(w: &mut W, frame: &Value) -> std::io::Result<()> {
110 let mut line = serde_json::to_vec(frame)?;
111 line.push(b'\n');
112 w.write_all(&line).await?;
113 w.flush().await
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use serde_json::json;
120
121 fn reader_over(bytes: &[u8], cap: usize) -> FrameReader<&[u8]> {
122 FrameReader::new(bytes, cap)
123 }
124
125 #[tokio::test]
126 async fn valid_frames_roundtrip() {
127 let mut buf = Vec::new();
128 write_frame(&mut buf, &json!({"jsonrpc":"2.0","id":1,"method":"ping"}))
129 .await
130 .unwrap();
131 write_frame(&mut buf, &json!({"jsonrpc":"2.0","id":2,"method":"pong"}))
132 .await
133 .unwrap();
134 let mut r = reader_over(&buf, 1024);
135 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 1));
136 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 2));
137 assert!(r.next().await.unwrap().is_none()); }
139
140 #[tokio::test]
141 async fn oversized_frame_is_discarded_and_reported_and_stream_continues() {
142 let big = format!("{{\"pad\":\"{}\"}}\n", "x".repeat(100));
143 let mut bytes = big.into_bytes();
144 bytes.extend_from_slice(b"{\"jsonrpc\":\"2.0\",\"id\":7,\"method\":\"ok\"}\n");
145 let mut r = reader_over(&bytes, 64);
146 assert!(matches!(
147 r.next().await.unwrap().unwrap(),
148 Inbound::Violation(Violation::TooLarge)
149 ));
150 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 7));
151 }
152
153 #[tokio::test]
154 async fn invalid_json_is_a_violation() {
155 let mut r = reader_over(b"not json at all\n", 1024);
156 assert!(matches!(
157 r.next().await.unwrap().unwrap(),
158 Inbound::Violation(Violation::InvalidJson)
159 ));
160 }
161
162 #[tokio::test]
163 async fn oversized_frame_truncated_by_eof_is_still_reported() {
164 let bytes = format!("{{\"pad\":\"{}\"}}", "x".repeat(100)).into_bytes(); let mut r = reader_over(&bytes, 64);
166 assert!(matches!(
167 r.next().await.unwrap().unwrap(),
168 Inbound::Violation(Violation::TooLarge)
169 ));
170 assert!(r.next().await.unwrap().is_none());
171 }
172
173 #[tokio::test]
174 async fn back_to_back_oversized_frames_then_valid_frame() {
175 let big = format!("{{\"pad\":\"{}\"}}\n", "x".repeat(100));
176 let mut bytes = big.clone().into_bytes();
177 bytes.extend_from_slice(big.as_bytes());
178 bytes.extend_from_slice(b"{\"jsonrpc\":\"2.0\",\"id\":9,\"method\":\"ok\"}\n");
179 let mut r = reader_over(&bytes, 64);
180 assert!(matches!(
181 r.next().await.unwrap().unwrap(),
182 Inbound::Violation(Violation::TooLarge)
183 ));
184 assert!(matches!(
185 r.next().await.unwrap().unwrap(),
186 Inbound::Violation(Violation::TooLarge)
187 ));
188 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 9));
189 }
190
191 #[tokio::test]
192 async fn frame_of_exactly_max_frame_bytes_passes_one_more_is_too_large() {
193 let payload = "x".repeat(62);
196 let exact = format!("\"{payload}\"\n");
197 let mut r = reader_over(exact.as_bytes(), 64);
198 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v == payload));
199
200 let over = format!("\"{}\"\n", "x".repeat(63)); let mut r = reader_over(over.as_bytes(), 64);
202 assert!(matches!(
203 r.next().await.unwrap().unwrap(),
204 Inbound::Violation(Violation::TooLarge)
205 ));
206 }
207
208 #[tokio::test]
213 async fn into_inner_preserves_pipelined_read_ahead() {
214 let bytes = b"{\"id\":1}\n{\"id\":2}\n";
215 let mut r = reader_over(bytes, 1024);
216 assert!(matches!(r.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 1));
219 let boxed: Box<dyn AsyncRead + Unpin + Send> = Box::new(r.into_inner());
221 let mut r2 = FrameReader::new(boxed, 1024);
222 assert!(matches!(r2.next().await.unwrap().unwrap(), Inbound::Frame(v) if v["id"] == 2));
223 assert!(r2.next().await.unwrap().is_none());
224 }
225}