1use serde::{Deserialize, Serialize};
17
18use crate::wire::DEFAULT_MAX_FRAME_BYTES;
19
20#[derive(Debug, thiserror::Error)]
22pub enum DecodeError {
23 #[error("frame body {body} bytes exceeds limit {max} bytes")]
26 FrameTooLarge { body: usize, max: usize },
27 #[error("decode error: {0}")]
29 Rmp(#[from] rmp_serde::decode::Error),
30 #[error("zero-length frame (keep-alive), not a message body")]
42 KeepAlive,
43}
44
45pub fn encode_frame<T: Serialize>(msg: &T) -> Result<Vec<u8>, rmp_serde::encode::Error> {
47 let body = rmp_serde::to_vec(msg)?;
48 let len = body.len() as u32;
49 let mut frame = Vec::with_capacity(4 + body.len());
50 frame.extend_from_slice(&len.to_le_bytes());
51 frame.extend_from_slice(&body);
52 Ok(frame)
53}
54
55pub fn decode_frame<T: for<'de> Deserialize<'de>>(
61 buf: &[u8],
62) -> Result<Option<(T, usize)>, DecodeError> {
63 decode_frame_with_limit(buf, DEFAULT_MAX_FRAME_BYTES)
64}
65
66pub fn decode_frame_raw(buf: &[u8], max: usize) -> Result<Option<(&[u8], usize)>, DecodeError> {
87 if buf.len() < 4 {
88 return Ok(None);
89 }
90 let len = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as usize;
91 if len > max {
94 return Err(DecodeError::FrameTooLarge { body: len, max });
95 }
96 let total = 4 + len;
97 if buf.len() < total {
98 return Ok(None);
99 }
100 Ok(Some((&buf[4..total], total)))
101}
102
103pub fn decode_frame_with_limit<T: for<'de> Deserialize<'de>>(
110 buf: &[u8],
111 max: usize,
112) -> Result<Option<(T, usize)>, DecodeError> {
113 let Some((body, total)) = decode_frame_raw(buf, max)? else {
114 return Ok(None);
115 };
116 if body.is_empty() {
117 return Err(DecodeError::KeepAlive);
118 }
119 let value = rmp_serde::from_slice(body)?;
120 Ok(Some((value, total)))
121}
122
123#[cfg(feature = "tokio")]
126mod tokio_io {
127 use std::io;
128
129 use serde::{Deserialize, Serialize};
130 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
131
132 use crate::wire::value::{Request, Response};
133 use crate::wire::DEFAULT_MAX_FRAME_BYTES;
134
135 pub async fn read_frame<T: for<'de> Deserialize<'de>, R: AsyncRead + Unpin>(
139 reader: &mut R,
140 max: usize,
141 ) -> io::Result<(T, usize)> {
142 let mut len_buf = [0u8; 4];
143 reader.read_exact(&mut len_buf).await?;
144 let len = u32::from_le_bytes(len_buf) as usize;
145 if len > max {
146 return Err(io::Error::new(
147 io::ErrorKind::InvalidData,
148 format!("frame body {len} bytes exceeds limit {max} bytes"),
149 ));
150 }
151 if len == 0 {
161 return Err(io::Error::new(
162 io::ErrorKind::InvalidData,
163 "zero-length frame (keep-alive), not a message body",
164 ));
165 }
166 let mut body = vec![0u8; len];
167 reader.read_exact(&mut body).await?;
168 let value = rmp_serde::from_slice(&body)
169 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
170 Ok((value, 4 + len))
171 }
172
173 pub async fn read_request<R: AsyncRead + Unpin>(
175 reader: &mut R,
176 ) -> io::Result<(Request, usize)> {
177 read_frame(reader, DEFAULT_MAX_FRAME_BYTES).await
178 }
179
180 pub async fn read_request_with_limit<R: AsyncRead + Unpin>(
182 reader: &mut R,
183 max: usize,
184 ) -> io::Result<(Request, usize)> {
185 read_frame(reader, max).await
186 }
187
188 pub async fn read_response<R: AsyncRead + Unpin>(
190 reader: &mut R,
191 ) -> io::Result<(Response, usize)> {
192 read_frame(reader, DEFAULT_MAX_FRAME_BYTES).await
193 }
194
195 pub async fn read_response_with_limit<R: AsyncRead + Unpin>(
197 reader: &mut R,
198 max: usize,
199 ) -> io::Result<(Response, usize)> {
200 read_frame(reader, max).await
201 }
202
203 pub async fn write_frame<T: Serialize, W: AsyncWrite + Unpin>(
205 writer: &mut W,
206 msg: &T,
207 ) -> io::Result<usize> {
208 let frame = crate::wire::frame::encode_frame(msg)
209 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
210 writer.write_all(&frame).await?;
211 Ok(frame.len())
212 }
213
214 pub async fn write_request<W: AsyncWrite + Unpin>(
216 writer: &mut W,
217 req: &Request,
218 ) -> io::Result<usize> {
219 write_frame(writer, req).await
220 }
221
222 pub async fn write_response<W: AsyncWrite + Unpin>(
224 writer: &mut W,
225 resp: &Response,
226 ) -> io::Result<usize> {
227 write_frame(writer, resp).await
228 }
229}
230
231#[cfg(feature = "tokio")]
232pub use tokio_io::{
233 read_frame, read_request, read_request_with_limit, read_response, read_response_with_limit,
234 write_frame, write_request, write_response,
235};
236
237#[cfg(test)]
238#[allow(clippy::unwrap_used, clippy::expect_used)]
239mod tests {
240 use super::*;
241 use crate::wire::value::{Request, Response, Value};
242 use crate::wire::PUSH_ID;
243
244 fn hex(bytes: &[u8]) -> String {
245 bytes
246 .iter()
247 .map(|b| format!("{b:02x}"))
248 .collect::<Vec<_>>()
249 .join(" ")
250 }
251
252 #[test]
255 fn ping_request_matches_family_golden_vector() {
256 let req = Request {
257 id: 1,
258 command: "PING".to_owned(),
259 args: vec![],
260 };
261 let frame = encode_frame(&req).unwrap();
262 assert_eq!(
263 hex(&frame),
264 "08 00 00 00 93 01 a4 50 49 4e 47 90",
265 "frame must match the corpus request-ping vector"
266 );
267 let (decoded, consumed): (Request, usize) = decode_frame(&frame).unwrap().unwrap();
268 assert_eq!(decoded, req);
269 assert_eq!(consumed, frame.len());
270 }
271
272 #[test]
273 fn pong_response_matches_nested_ok_golden_vector() {
274 let resp = Response::ok(1, Value::Str("PONG".to_owned()));
275 let frame = encode_frame(&resp).unwrap();
276 assert_eq!(
278 hex(&frame),
279 "10 00 00 00 92 01 81 a2 4f 6b 81 a3 53 74 72 a4 50 4f 4e 47"
280 );
281 let (decoded, _): (Response, usize) = decode_frame(&frame).unwrap().unwrap();
282 assert_eq!(decoded, resp);
283 }
284
285 #[test]
286 fn null_is_bare_string_and_int_is_single_key_map() {
287 assert_eq!(
288 hex(&rmp_serde::to_vec(&Value::Null).unwrap()),
289 "a4 4e 75 6c 6c"
290 );
291 assert_eq!(
292 hex(&rmp_serde::to_vec(&Value::Int(42)).unwrap()),
293 "81 a3 49 6e 74 2a"
294 );
295 }
296
297 #[test]
300 fn bytes_emit_as_bin_canonical() {
301 let encoded = rmp_serde::to_vec(&Value::bytes(vec![1, 2, 3, 255])).unwrap();
302 assert_eq!(hex(&encoded), "81 a5 42 79 74 65 73 c4 04 01 02 03 ff");
304 }
305
306 #[test]
307 fn bytes_decode_legacy_int_array_form() {
308 let legacy: Vec<u8> = vec![
310 0x81, 0xa5, 0x42, 0x79, 0x74, 0x65, 0x73, 0x94, 0x01, 0x02, 0x03, 0xcc, 0xff, ];
313 let decoded: Value = rmp_serde::from_slice(&legacy).unwrap();
314 assert_eq!(decoded, Value::bytes(vec![1, 2, 3, 255]));
315 }
316
317 #[test]
320 fn map_shaped_request_decodes() {
321 let req = Request {
323 id: 7,
324 command: "GET".to_owned(),
325 args: vec![Value::Str("key".to_owned())],
326 };
327 let map_shaped = rmp_serde::to_vec_named(&req).unwrap();
328 let array_shaped = rmp_serde::to_vec(&req).unwrap();
329 assert_ne!(map_shaped, array_shaped, "shapes must actually differ");
330 let decoded: Request = rmp_serde::from_slice(&map_shaped).unwrap();
331 assert_eq!(decoded, req);
332 }
333
334 #[test]
337 fn round_trip_all_variants() {
338 let all = Value::Array(vec![
339 Value::Null,
340 Value::Bool(true),
341 Value::Bool(false),
342 Value::Int(0),
343 Value::Int(i64::MIN),
344 Value::Int(i64::MAX),
345 Value::Int(-32),
346 Value::Int(127),
347 Value::Int(255),
348 Value::Int(65535),
349 Value::Float(0.0),
350 Value::Float(-0.0),
351 Value::Float(f64::INFINITY),
352 Value::Float(f64::NEG_INFINITY),
353 Value::bytes(vec![]),
354 Value::bytes(vec![0, 1, 2, 255]),
355 Value::Str(String::new()),
356 Value::Str("héllo wörld".to_owned()),
357 Value::Array(vec![]),
358 Value::Map(vec![]),
359 Value::Map(vec![
360 (Value::Str("k".to_owned()), Value::Int(1)),
361 (Value::Int(2), Value::Str("non-string key".to_owned())),
362 ]),
363 ]);
364 let frame = encode_frame(&all).unwrap();
365 let (decoded, consumed): (Value, usize) = decode_frame(&frame).unwrap().unwrap();
366 assert_eq!(decoded, all);
367 assert_eq!(consumed, frame.len());
368 }
369
370 #[test]
371 fn nan_bit_pattern_survives() {
372 let frame = encode_frame(&Value::Float(f64::NAN)).unwrap();
373 let (decoded, _): (Value, usize) = decode_frame(&frame).unwrap().unwrap();
374 match decoded {
375 Value::Float(f) => assert_eq!(f.to_bits(), f64::NAN.to_bits()),
376 other => panic!("expected Float, got {other:?}"),
377 }
378 }
379
380 #[test]
381 fn error_response_round_trips_with_prefix_conventions() {
382 for msg in [
383 "ERR unknown command",
384 "NOAUTH Authentication required.",
385 "WRONGPASS invalid username-password pair or user is disabled.",
386 "[collection_not_found] no such collection: docs",
387 ] {
388 let resp = Response::err(9, msg);
389 let frame = encode_frame(&resp).unwrap();
390 let (decoded, _): (Response, usize) = decode_frame(&frame).unwrap().unwrap();
391 assert_eq!(decoded.result, Err(msg.to_owned()));
392 }
393 }
394
395 #[test]
398 fn partial_header_and_partial_body_return_none() {
399 let frame = encode_frame(&Request {
400 id: 1,
401 command: "PING".to_owned(),
402 args: vec![],
403 })
404 .unwrap();
405 for cut in [0, 1, 3, 4, frame.len() - 1] {
406 let out: Option<(Request, usize)> = decode_frame(&frame[..cut]).unwrap();
407 assert!(out.is_none(), "cut at {cut} must ask for more bytes");
408 }
409 }
410
411 #[test]
412 fn two_frames_in_one_buffer_consume_exactly_one_each() {
413 let a = encode_frame(&Response::ok(1, Value::Int(1))).unwrap();
414 let b = encode_frame(&Response::ok(2, Value::Int(2))).unwrap();
415 let mut buf = a.clone();
416 buf.extend_from_slice(&b);
417 let (first, used): (Response, usize) = decode_frame(&buf).unwrap().unwrap();
418 assert_eq!(first.id, 1);
419 assert_eq!(used, a.len());
420 let (second, used2): (Response, usize) = decode_frame(&buf[used..]).unwrap().unwrap();
421 assert_eq!(second.id, 2);
422 assert_eq!(used2, b.len());
423 }
424
425 #[test]
426 fn oversized_prefix_rejected_before_body_arrives() {
427 let over = (DEFAULT_MAX_FRAME_BYTES + 1) as u32;
430 let buf = over.to_le_bytes();
431 let err = decode_frame::<Request>(&buf).unwrap_err();
432 match err {
433 DecodeError::FrameTooLarge { body, max } => {
434 assert_eq!(body, DEFAULT_MAX_FRAME_BYTES + 1);
435 assert_eq!(max, DEFAULT_MAX_FRAME_BYTES);
436 }
437 other => panic!("expected FrameTooLarge, got {other:?}"),
438 }
439 }
440
441 #[test]
442 fn custom_limit_is_honored() {
443 let frame = encode_frame(&Value::Str("x".repeat(100))).unwrap();
444 let err = decode_frame_with_limit::<Value>(&frame, 8).unwrap_err();
445 assert!(matches!(err, DecodeError::FrameTooLarge { .. }));
446 }
447
448 #[test]
449 fn garbage_body_is_a_typed_error_not_a_panic() {
450 let mut buf = 4u32.to_le_bytes().to_vec();
451 buf.extend_from_slice(&[0xc1, 0xc1, 0xc1, 0xc1]); let err = decode_frame::<Request>(&buf).unwrap_err();
453 assert!(matches!(err, DecodeError::Rmp(_)));
454 }
455
456 #[test]
457 fn zero_length_body_is_a_keep_alive_not_a_parse_failure() {
458 let buf = 0u32.to_le_bytes();
462 let err = decode_frame::<Request>(&buf).unwrap_err();
463 assert!(
464 matches!(err, DecodeError::KeepAlive),
465 "expected KeepAlive, got {err:?}"
466 );
467 }
468
469 #[test]
472 fn raw_decode_borrows_the_body_and_reports_bytes_consumed() {
473 let frame = encode_frame(&Request {
474 id: 7,
475 command: "PING".to_owned(),
476 args: vec![],
477 })
478 .unwrap();
479
480 let (body, consumed) = decode_frame_raw(&frame, DEFAULT_MAX_FRAME_BYTES)
481 .unwrap()
482 .expect("a complete frame decodes");
483 assert_eq!(consumed, frame.len(), "consumed is 4 + body");
484 assert_eq!(body, &frame[4..], "the body is borrowed from the input");
485 assert!(std::ptr::eq(body.as_ptr(), frame[4..].as_ptr()));
487
488 let (request, typed_consumed): (Request, usize) = decode_frame(&frame).unwrap().unwrap();
490 assert_eq!(typed_consumed, consumed);
491 assert_eq!(request.command, "PING");
492 }
493
494 #[test]
495 fn raw_decode_needs_more_bytes_for_a_partial_frame() {
496 let frame = encode_frame(&Request {
497 id: 1,
498 command: "ECHO".to_owned(),
499 args: vec![Value::bytes(vec![9u8; 64])],
500 })
501 .unwrap();
502
503 assert!(decode_frame_raw(&frame[..3], DEFAULT_MAX_FRAME_BYTES)
505 .unwrap()
506 .is_none());
507 assert!(
509 decode_frame_raw(&frame[..frame.len() - 1], DEFAULT_MAX_FRAME_BYTES)
510 .unwrap()
511 .is_none()
512 );
513 assert!(decode_frame_raw(&frame, DEFAULT_MAX_FRAME_BYTES)
515 .unwrap()
516 .is_some());
517 }
518
519 #[test]
520 fn raw_decode_enforces_the_cap_before_slicing() {
521 let mut buf = (1024u32 * 1024).to_le_bytes().to_vec();
524 buf.extend_from_slice(b"only a few bytes");
525 let err = decode_frame_raw(&buf, 4096).unwrap_err();
526 assert!(matches!(
527 err,
528 DecodeError::FrameTooLarge {
529 body: 1_048_576,
530 max: 4096
531 }
532 ));
533 }
534
535 #[test]
536 fn raw_decode_returns_a_keep_alive_as_an_empty_body() {
537 let buf = 0u32.to_le_bytes();
540 let (body, consumed) = decode_frame_raw(&buf, DEFAULT_MAX_FRAME_BYTES)
541 .unwrap()
542 .expect("a zero-length frame is a complete frame");
543 assert!(body.is_empty());
544 assert_eq!(consumed, 4, "prefix only");
545 }
546
547 #[test]
548 fn raw_decode_walks_a_buffer_of_several_frames() {
549 let mut stream = Vec::new();
553 for id in 1..=3u32 {
554 stream.extend_from_slice(
555 &encode_frame(&Request {
556 id,
557 command: "X".to_owned(),
558 args: vec![],
559 })
560 .unwrap(),
561 );
562 }
563 stream.extend_from_slice(&0u32.to_le_bytes());
565
566 let mut at = 0;
567 let mut bodies = 0;
568 let mut keep_alives = 0;
569 while at < stream.len() {
570 let (body, consumed) = decode_frame_raw(&stream[at..], DEFAULT_MAX_FRAME_BYTES)
571 .unwrap()
572 .expect("each frame is complete");
573 if body.is_empty() {
574 keep_alives += 1;
575 } else {
576 bodies += 1;
577 }
578 at += consumed;
579 }
580 assert_eq!(bodies, 3);
581 assert_eq!(keep_alives, 1);
582 assert_eq!(at, stream.len(), "the buffer is fully consumed");
583 }
584
585 #[test]
586 fn push_id_is_reserved_u32_max() {
587 assert_eq!(PUSH_ID, u32::MAX);
588 }
589
590 #[cfg(feature = "tokio")]
593 #[tokio::test]
594 async fn async_write_then_read_reports_frame_size() {
595 let req = Request {
596 id: 3,
597 command: "PING".to_owned(),
598 args: vec![],
599 };
600 let mut buf = Vec::new();
601 let written = write_request(&mut buf, &req).await.unwrap();
602 assert_eq!(written, buf.len());
603 let mut cursor = std::io::Cursor::new(buf);
604 let (decoded, size) = read_request(&mut cursor).await.unwrap();
605 assert_eq!(decoded, req);
606 assert_eq!(size, written);
607 }
608
609 #[cfg(feature = "tokio")]
610 #[tokio::test]
611 async fn async_read_rejects_oversized_prefix_without_reading_body() {
612 let over = ((DEFAULT_MAX_FRAME_BYTES + 1) as u32).to_le_bytes();
613 let mut cursor = std::io::Cursor::new(over.to_vec());
614 let err = read_request(&mut cursor).await.unwrap_err();
615 assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
616 }
617}