Validate the CRC32C of an inbound frame and return the payload slice. `data` must start at byte 0 (version byte). Returns `(rpc_type, payload)`. Side effect: observes the peer's cluster epoch via [`observe_peer_cluster_epoch`] (monotonic max).
(data: &[u8])
| 55 | /// Side effect: observes the peer's cluster epoch via |
| 56 | /// [`observe_peer_cluster_epoch`] (monotonic max). |
| 57 | pub fn parse_frame(data: &[u8]) -> Result<(u8, &[u8])> { |
| 58 | if data.is_empty() { |
| 59 | return Err(ClusterError::Codec { |
| 60 | detail: format!("frame too short: 0 bytes, need {HEADER_SIZE}"), |
| 61 | }); |
| 62 | } |
| 63 | |
| 64 | let version = data[0]; |
| 65 | if version != RPC_FRAME_VERSION { |
| 66 | return Err(ClusterError::UnsupportedWireVersion { |
| 67 | got: version, |
| 68 | supported_min: RPC_FRAME_VERSION, |
| 69 | supported_max: RPC_FRAME_VERSION, |
| 70 | }); |
| 71 | } |
| 72 | |
| 73 | if data.len() < HEADER_SIZE { |
| 74 | return Err(ClusterError::Codec { |
| 75 | detail: format!("frame too short: {} bytes, need {HEADER_SIZE}", data.len()), |
| 76 | }); |
| 77 | } |
| 78 | |
| 79 | let rpc_type = data[1]; |
| 80 | let payload_len = u32::from_le_bytes([data[2], data[3], data[4], data[5]]); |
| 81 | let expected_crc = u32::from_le_bytes([data[6], data[7], data[8], data[9]]); |
| 82 | let peer_epoch = u64::from_le_bytes([ |
| 83 | data[10], data[11], data[12], data[13], data[14], data[15], data[16], data[17], |
| 84 | ]); |
| 85 | |
| 86 | if payload_len > MAX_RPC_PAYLOAD_SIZE { |
| 87 | return Err(ClusterError::Codec { |
| 88 | detail: format!("payload length {payload_len} exceeds maximum {MAX_RPC_PAYLOAD_SIZE}"), |
| 89 | }); |
| 90 | } |
| 91 | |
| 92 | let expected_total = HEADER_SIZE + payload_len as usize; |
| 93 | if data.len() < expected_total { |
| 94 | return Err(ClusterError::Codec { |
| 95 | detail: format!( |
| 96 | "frame truncated: got {} bytes, expected {expected_total}", |
| 97 | data.len() |
| 98 | ), |
| 99 | }); |
| 100 | } |
| 101 | |
| 102 | let payload = &data[HEADER_SIZE..expected_total]; |
| 103 | let actual_crc = crc32c::crc32c(payload); |
| 104 | if actual_crc != expected_crc { |
| 105 | return Err(ClusterError::Codec { |
| 106 | detail: format!( |
| 107 | "CRC32C mismatch: expected {expected_crc:#010x}, got {actual_crc:#010x}" |
| 108 | ), |
| 109 | }); |
| 110 | } |
| 111 | |
| 112 | if peer_epoch > 0 { |
| 113 | observe_peer_cluster_epoch(peer_epoch); |
| 114 | } |