1pub mod frame;
6
7use std::{
8 io::{self, Read, Write},
9 net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs},
10 sync::{Arc, Mutex, PoisonError},
11 thread,
12};
13
14use frame::{CapturedFrame, Pending, parse_request_prefix, read_correlation_id};
15
16pub type Recorder = Arc<dyn Fn(CapturedFrame) + Send + Sync>;
18
19pub fn spawn(
30 listen: impl ToSocketAddrs,
31 upstream: &str,
32 recorder: Recorder,
33) -> io::Result<SocketAddr> {
34 let listener = TcpListener::bind(listen)?;
35 let addr = listener.local_addr()?;
36 let upstream = upstream.to_owned();
37 thread::spawn(move || {
38 for client in listener.incoming() {
39 let Ok(client) = client else { continue };
40 let upstream = upstream.clone();
41 let recorder = Arc::clone(&recorder);
42 thread::spawn(move || {
43 if let Err(e) = handle_conn(client, &upstream, &recorder) {
44 eprintln!("tap conn error: {e}");
45 }
46 });
47 }
48 });
49 Ok(addr)
50}
51
52fn handle_conn(client: TcpStream, upstream: &str, recorder: &Recorder) -> io::Result<()> {
53 let server = TcpStream::connect(upstream)?;
54 let pending = Arc::new(Mutex::new(Pending::default()));
55
56 let c2s_client = client.try_clone()?;
57 let c2s_server = server.try_clone()?;
58 let pend_req = Arc::clone(&pending);
59 let rec_req = Arc::clone(recorder);
60 let t = thread::spawn(move || {
61 let _ = pump(c2s_client, c2s_server, true, &pend_req, &*rec_req);
62 });
63
64 pump(server, client, false, &pending, &**recorder)?;
65 let _ = t.join();
66 Ok(())
67}
68
69fn pump(
80 mut src: TcpStream,
81 mut dst: TcpStream,
82 is_request: bool,
83 pending: &Mutex<Pending>,
84 recorder: &(dyn Fn(CapturedFrame) + Send + Sync),
85) -> io::Result<()> {
86 loop {
87 let mut len_buf = [0u8; 4];
88 if let Err(e) = src.read_exact(&mut len_buf) {
89 if e.kind() == io::ErrorKind::UnexpectedEof {
90 return Ok(());
91 }
92 return Err(e);
93 }
94 let Ok(n) = usize::try_from(i32::from_be_bytes(len_buf)) else {
96 return Ok(());
97 };
98 let mut body = vec![0u8; n];
99 src.read_exact(&mut body)?;
100
101 if is_request {
102 if let Some(p) = parse_request_prefix(&body) {
103 pending
104 .lock()
105 .unwrap_or_else(PoisonError::into_inner)
106 .record(p.correlation_id, p.api_key, p.api_version);
107 recorder(CapturedFrame {
108 api_key: p.api_key,
109 version: p.api_version,
110 is_request: true,
111 body: body.clone(),
112 });
113 }
114 } else if let Some(corr) = read_correlation_id(&body) {
115 let request = pending
116 .lock()
117 .unwrap_or_else(PoisonError::into_inner)
118 .take(corr);
119 if let Some((api_key, version)) = request {
120 recorder(CapturedFrame {
121 api_key,
122 version,
123 is_request: false,
124 body: body.clone(),
125 });
126 }
127 }
128
129 dst.write_all(&len_buf)?;
130 dst.write_all(&body)?;
131 dst.flush()?;
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use std::time::{Duration, Instant};
138
139 use krabka_ids::{ApiKey, ApiVersion};
140
141 use super::*;
142
143 fn framed(body: &[u8]) -> Vec<u8> {
144 let mut out = i32::try_from(body.len()).unwrap().to_be_bytes().to_vec();
145 out.extend_from_slice(body);
146 out
147 }
148
149 fn request(api_key: i16, version: i16, corr: i32) -> Vec<u8> {
151 let mut b = api_key.to_be_bytes().to_vec();
152 b.extend_from_slice(&version.to_be_bytes());
153 b.extend_from_slice(&corr.to_be_bytes());
154 b
155 }
156
157 fn upstream_echoing(response: Vec<u8>) -> io::Result<SocketAddr> {
159 let listener = TcpListener::bind("127.0.0.1:0")?;
160 let addr = listener.local_addr()?;
161 thread::spawn(move || {
162 let Ok((mut server, _)) = listener.accept() else {
163 return;
164 };
165 let mut len = [0u8; 4];
166 if server.read_exact(&mut len).is_err() {
167 return;
168 }
169 let mut body = vec![0u8; usize::try_from(i32::from_be_bytes(len)).unwrap()];
171 if server.read_exact(&mut body).is_err() {
172 return;
173 }
174 let _ = server.write_all(&framed(&response));
175 let _ = server.flush();
176 });
177 Ok(addr)
178 }
179
180 #[test]
190 fn relays_frames_both_ways_and_records_them() {
191 let response = 7i32.to_be_bytes().to_vec();
192 let upstream = upstream_echoing(response.clone()).unwrap();
193
194 let seen = Arc::new(Mutex::new(Vec::new()));
195 let sink = Arc::clone(&seen);
196 let addr = spawn(
197 "127.0.0.1:0",
198 &upstream.to_string(),
199 Arc::new(move |f: CapturedFrame| sink.lock().unwrap().push(f)),
200 )
201 .unwrap();
202
203 let mut client = TcpStream::connect(addr).unwrap();
204 client
205 .set_read_timeout(Some(Duration::from_secs(10)))
206 .unwrap();
207 client.write_all(&framed(&request(18, 3, 7))).unwrap();
208 client.flush().unwrap();
209
210 let mut len = [0u8; 4];
211 client.read_exact(&mut len).unwrap();
212 let mut body = vec![0u8; usize::try_from(i32::from_be_bytes(len)).unwrap()];
213 client.read_exact(&mut body).unwrap();
214
215 assert2::check!(body == response);
217
218 let kinds: Vec<_> = seen
224 .lock()
225 .unwrap()
226 .iter()
227 .map(|f| (f.api_key, f.version, f.is_request))
228 .collect();
229 assert2::check!(
230 kinds
231 == vec![
232 (ApiKey(18), ApiVersion(3), true),
233 (ApiKey(18), ApiVersion(3), false)
234 ]
235 );
236 }
237
238 #[test]
243 fn a_zero_length_frame_is_forwarded_rather_than_ending_the_relay() {
244 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
245 let upstream = listener.local_addr().unwrap();
246
247 let got = Arc::new(Mutex::new(Vec::new()));
249 let sink = Arc::clone(&got);
250 thread::spawn(move || {
251 let Ok((mut server, _)) = listener.accept() else {
252 return;
253 };
254 for _ in 0..2 {
255 let mut len = [0u8; 4];
256 if server.read_exact(&mut len).is_err() {
257 return;
258 }
259 let n = i32::from_be_bytes(len);
260 let mut body = vec![0u8; usize::try_from(n).unwrap()];
261 if server.read_exact(&mut body).is_err() {
262 return;
263 }
264 sink.lock().unwrap().push(n);
265 }
266 });
267
268 let addr = spawn("127.0.0.1:0", &upstream.to_string(), Arc::new(|_| {})).unwrap();
269 let mut client = TcpStream::connect(addr).unwrap();
270 client.write_all(&framed(&[])).unwrap();
271 client.write_all(&framed(&request(18, 3, 7))).unwrap();
272 client.flush().unwrap();
273
274 let deadline = Instant::now() + Duration::from_secs(10);
275 while got.lock().unwrap().len() < 2 && Instant::now() < deadline {
276 thread::sleep(Duration::from_millis(5));
277 }
278 assert2::check!(*got.lock().unwrap() == vec![0, 8]);
279 }
280
281 #[test]
285 fn clean_close_ends_the_pump_without_an_error() {
286 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
287 let addr = listener.local_addr().unwrap();
288 let client = TcpStream::connect(addr).unwrap();
289 let (server, _) = listener.accept().unwrap();
290
291 drop(client);
293
294 let result = pump(
295 server.try_clone().unwrap(),
296 server,
297 true,
298 &Mutex::new(Pending::default()),
299 &drop::<CapturedFrame>,
300 );
301 assert2::check!(result.is_ok());
302 }
303}