⎇ b08d5cbe0a6222f5b1479f40e68346b5d38246a8
crates/protocol/brahman-handshake/src/codec.rs
Historia
Quién tocó qué
crudo
refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG
1//! Codec de wire: frames length-prefixed con cuerpo postcard.
2//!
3//! Cada frame en el stream tiene la forma:
4//! ```text
5//! [4 bytes LE: longitud N] [N bytes: postcard(Frame)]
6//! ```
7//!
8//! El `MAX_FRAME_BYTES` evita que un cliente malicioso/buggy reserve memoria
9//! arbitraria al anunciar un length absurdo.
10
11use std::io::{Error, ErrorKind, Result};
12
13use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
14
15use crate::messages::Frame;
16
17/// Tamaño máximo de un frame antes de que el reader rechace la conexión.
18/// 4 MiB cubre cualquier Card razonable con margen amplio.
19pub const MAX_FRAME_BYTES: usize = 4 * 1024 * 1024;
20
21/// Escribe un frame al stream.
22pub async fn write_frame<W: AsyncWrite + Unpin>(w: &mut W, frame: &Frame) -> Result<()> {
23 let bytes = postcard::to_allocvec(frame)
24 .map_err(|e| Error::new(ErrorKind::InvalidData, format!("postcard encode: {e}")))?;
25 if bytes.len() > MAX_FRAME_BYTES {
26 return Err(Error::new(
27 ErrorKind::InvalidData,
28 format!("frame demasiado grande: {} bytes", bytes.len()),
29 ));
30 }
31 let len = bytes.len() as u32;
32 w.write_all(&len.to_le_bytes()).await?;
33 w.write_all(&bytes).await?;
34 w.flush().await?;
35 Ok(())
36}
37
38/// Lee un frame del stream.
39pub async fn read_frame<R: AsyncRead + Unpin>(r: &mut R) -> Result<Frame> {
40 let mut len_buf = [0u8; 4];
41 r.read_exact(&mut len_buf).await?;
42 let len = u32::from_le_bytes(len_buf) as usize;
43 if len > MAX_FRAME_BYTES {
44 return Err(Error::new(
45 ErrorKind::InvalidData,
46 format!("frame anunciado demasiado grande: {len} bytes"),
47 ));
48 }
49 let mut buf = vec![0u8; len];
50 r.read_exact(&mut buf).await?;
51 postcard::from_bytes(&buf)
52 .map_err(|e| Error::new(ErrorKind::InvalidData, format!("postcard decode: {e}")))
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::messages::{Frame, HandshakeError};
59
60 #[tokio::test]
61 async fn frame_roundtrip() {
62 let frame = Frame::Error(HandshakeError::Rejected("test".into()));
63 let mut buf = Vec::new();
64 write_frame(&mut buf, &frame).await.unwrap();
65 let mut cursor = std::io::Cursor::new(buf);
66 let decoded = read_frame(&mut cursor).await.unwrap();
67 match decoded {
68 Frame::Error(HandshakeError::Rejected(s)) => assert_eq!(s, "test"),
69 _ => panic!("variant mismatch"),
70 }
71 }
72}