melin_wire_protocol/
blocking.rs1use std::io::{self, BufReader, BufWriter, Read, Write};
8
9const MAX_FRAME_SIZE: usize = 1024;
11
12pub struct BlockingFrameReader<R> {
23 reader: BufReader<R>,
24 buf: [u8; MAX_FRAME_SIZE],
27 frame_len: usize,
29}
30
31impl<R: Read> BlockingFrameReader<R> {
32 pub fn new(reader: R) -> Self {
33 Self {
34 reader: BufReader::new(reader),
35 buf: [0u8; MAX_FRAME_SIZE],
36 frame_len: 0,
37 }
38 }
39
40 pub fn read_frame(&mut self) -> io::Result<Option<&[u8]>> {
44 let mut len_buf = [0u8; 4];
46 match self.reader.read_exact(&mut len_buf) {
47 Ok(()) => {}
48 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
49 Err(e) => return Err(e),
50 }
51
52 let len = u32::from_le_bytes(len_buf) as usize;
53 if len > MAX_FRAME_SIZE {
54 return Err(io::Error::new(
55 io::ErrorKind::InvalidData,
56 format!("frame too large: {len} bytes (max {MAX_FRAME_SIZE})"),
57 ));
58 }
59
60 self.reader.read_exact(&mut self.buf[..len])?;
61 self.frame_len = len;
62
63 Ok(Some(&self.buf[..len]))
64 }
65
66 pub fn get_ref(&self) -> &R {
71 self.reader.get_ref()
72 }
73}
74
75pub struct BlockingFrameWriter<W: Write> {
80 writer: BufWriter<W>,
81}
82
83impl<W: Write> BlockingFrameWriter<W> {
84 pub fn new(writer: W) -> Self {
85 Self {
86 writer: BufWriter::new(writer),
87 }
88 }
89
90 pub fn write_frame(&mut self, data: &[u8]) -> io::Result<()> {
92 let len = data.len() as u32;
93 self.writer.write_all(&len.to_le_bytes())?;
94 self.writer.write_all(data)?;
95 Ok(())
96 }
97
98 pub fn flush(&mut self) -> io::Result<()> {
100 self.writer.flush()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use std::net::{TcpListener, TcpStream};
108
109 #[test]
110 fn frame_round_trip_blocking() {
111 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
112 let addr = listener.local_addr().unwrap();
113
114 let client = TcpStream::connect(addr).unwrap();
115 let (server, _) = listener.accept().unwrap();
116
117 let mut writer = BlockingFrameWriter::new(client);
118 let mut reader = BlockingFrameReader::new(server);
119
120 let data = b"hello trading";
121 writer.write_frame(data).unwrap();
122 writer.flush().unwrap();
123
124 let received = reader.read_frame().unwrap().unwrap();
125 assert_eq!(received, data);
126 }
127
128 #[test]
129 fn clean_disconnect_returns_none() {
130 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
131 let addr = listener.local_addr().unwrap();
132
133 let client = TcpStream::connect(addr).unwrap();
134 let (server, _) = listener.accept().unwrap();
135
136 drop(client);
137
138 let mut reader = BlockingFrameReader::new(server);
139 let result = reader.read_frame().unwrap();
140 assert!(result.is_none());
141 }
142
143 #[test]
144 fn oversized_frame_rejected() {
145 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
146 let addr = listener.local_addr().unwrap();
147
148 let mut client = TcpStream::connect(addr).unwrap();
149 let (server, _) = listener.accept().unwrap();
150
151 let fake_len = 2_000_000u32;
153 client.write_all(&fake_len.to_le_bytes()).unwrap();
154
155 let mut reader = BlockingFrameReader::new(server);
156 let result = reader.read_frame();
157 assert!(result.is_err());
158 }
159
160 #[test]
161 fn multiple_frames_in_sequence() {
162 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
163 let addr = listener.local_addr().unwrap();
164
165 let client = TcpStream::connect(addr).unwrap();
166 let (server, _) = listener.accept().unwrap();
167
168 let mut writer = BlockingFrameWriter::new(client);
169 let mut reader = BlockingFrameReader::new(server);
170
171 for i in 0u32..100 {
172 writer.write_frame(&i.to_le_bytes()).unwrap();
173 }
174 writer.flush().unwrap();
175
176 for i in 0u32..100 {
177 let frame = reader.read_frame().unwrap().unwrap();
178 assert_eq!(frame, i.to_le_bytes());
179 }
180 }
181}