Read a single frame from the stream. Returns `Ok(None)` on clean EOF (client disconnected). Returns `Err` on framing errors or I/O failures.
(stream: &mut R)
| 37 | /// Returns `Ok(None)` on clean EOF (client disconnected). |
| 38 | /// Returns `Err` on framing errors or I/O failures. |
| 39 | pub async fn read_frame<R: AsyncRead + Unpin>(stream: &mut R) -> crate::Result<Option<Vec<u8>>> { |
| 40 | // Read 4-byte length prefix. |
| 41 | let mut len_buf = [0u8; FRAME_HEADER_LEN]; |
| 42 | match stream.read_exact(&mut len_buf).await { |
| 43 | Ok(_) => {} |
| 44 | Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None), |
| 45 | Err(e) => return Err(crate::Error::Io(e)), |
| 46 | } |
| 47 | |
| 48 | let payload_len = u32::from_be_bytes(len_buf); |
| 49 | if payload_len > MAX_FRAME_SIZE { |
| 50 | return Err(crate::Error::BadRequest { |
| 51 | detail: format!("frame size {payload_len} exceeds maximum {MAX_FRAME_SIZE}"), |
| 52 | }); |
| 53 | } |
| 54 | if payload_len == 0 { |
| 55 | return Err(crate::Error::BadRequest { |
| 56 | detail: "empty frame".into(), |
| 57 | }); |
| 58 | } |
| 59 | |
| 60 | let mut payload = vec![0u8; payload_len as usize]; |
| 61 | stream |
| 62 | .read_exact(&mut payload) |
| 63 | .await |
| 64 | .map_err(crate::Error::Io)?; |
| 65 | |
| 66 | Ok(Some(payload)) |
| 67 | } |
| 68 | |
| 69 | /// Write a single frame to the stream. |
| 70 | pub async fn write_frame<W: AsyncWrite + Unpin>( |
no test coverage detected