use crate::messages::{Decode, Header, MessageError};
#[derive(Debug, PartialEq)]
pub struct RawFrame<'a> {
pub header: Header,
pub payload: &'a [u8],
}
pub fn try_frame(buf: &[u8]) -> Result<Option<(RawFrame<'_>, usize)>, MessageError> {
if buf.len() < Header::SIZE {
return Ok(None);
}
let (header, _) = Header::decode(&buf[..Header::SIZE])?;
let payload_len = header.payload_length as usize;
if buf.len() - Header::SIZE < payload_len {
return Ok(None);
}
let total = Header::SIZE + payload_len;
Ok(Some((
RawFrame {
header,
payload: &buf[Header::SIZE..total],
},
total,
)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::messages::{MessageError, Payload, PayloadType};
#[test]
fn incomplete_header_returns_none() {
let buf = [0x02u8, 0xFD, 0x00];
assert!(matches!(try_frame(&buf), Ok(None)));
}
#[test]
fn complete_nack_frame_is_framed() {
let buf: [u8; 9] = [0x02, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03];
let (frame, consumed) = try_frame(&buf).unwrap().unwrap();
assert_eq!(consumed, 9);
assert_eq!(
frame.header.payload_type,
crate::messages::PayloadType::NegativeAcknowledge
);
assert_eq!(frame.payload, &buf[8..9]);
}
#[test]
fn huge_payload_length_does_not_overflow() {
let buf: [u8; 8] = [0x02, 0xFD, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF];
assert!(matches!(try_frame(&buf), Ok(None)));
}
#[test]
fn corrupt_inverse_errors() {
let buf: [u8; 8] = [0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert!(matches!(
try_frame(&buf),
Err(MessageError::VersionInverseIncorrect { .. })
));
}
#[test]
fn frame_then_decode_recoverability() {
let buf: [u8; 9] = [0x02, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03];
let (frame, consumed) = try_frame(&buf).unwrap().unwrap();
assert_eq!(consumed, 9);
let payload = Payload::decode(frame.payload, frame.header.payload_type)
.expect("a well-formed NACK payload should decode");
assert!(matches!(payload, Payload::DoIPNack(_)));
let buf: [u8; 9] = [0x02, 0xFD, 0x99, 0x99, 0x00, 0x00, 0x00, 0x01, 0x00];
let (frame, consumed) = try_frame(&buf).unwrap().unwrap();
assert_eq!(consumed, 9);
assert_eq!(frame.payload, &buf[8..9]);
let err = Payload::decode(frame.payload, frame.header.payload_type).unwrap_err();
assert!(matches!(
err,
MessageError::UnsupportedPayloadType(PayloadType::Reserved(0x9999))
));
assert!(!err.is_framing_fatal());
}
}