use crate::multiplex::{
read_varuint_slice, CLIENT_POLL_SERVER, CLIENT_POLL_SERVER_FLOW, CLIENT_TO_SERVER,
SERVER_TO_CLIENT, SERVER_TO_CLIENT_FLOW,
};
pub const MAX_WRITE: usize = 61;
pub fn write_client_to_server(channel: u8, data: &[u8], out: &mut [u8; 64]) -> u8 {
assert!(data.len() <= MAX_WRITE);
out[0] = CLIENT_TO_SERVER;
out[1] = channel;
out[2] = data.len() as u8;
out[3..3 + data.len()].copy_from_slice(data);
(3 + data.len()) as u8
}
pub fn write_client_poll(channel: u8, max_length: u8, out: &mut [u8; 64]) -> u8 {
out[0] = CLIENT_POLL_SERVER;
out[1] = channel;
out[2] = max_length;
3
}
pub fn write_client_poll_flow(
channel: u8,
packet_number: u8,
max_length: u8,
out: &mut [u8; 64],
) -> u8 {
out[0] = CLIENT_POLL_SERVER_FLOW;
out[1] = channel;
out[2] = packet_number;
out[3] = max_length;
4
}
pub fn parse_server_to_client(payload: &[u8], channel: u8) -> Option<&[u8]> {
let (action, rest) = payload.split_first()?;
if *action != SERVER_TO_CLIENT {
return None;
}
let (msg_channel, rest) = rest.split_first()?;
if *msg_channel != channel {
return None;
}
let (len, rest) = read_varuint_slice(rest)?;
rest.get(..len as usize)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FlowData<'a> {
pub packet_number: u8,
pub data: &'a [u8],
}
pub fn parse_server_to_client_flow(payload: &[u8], channel: u8) -> Option<FlowData<'_>> {
let (action, rest) = payload.split_first()?;
if *action != SERVER_TO_CLIENT_FLOW {
return None;
}
let (msg_channel, rest) = rest.split_first()?;
if *msg_channel != channel {
return None;
}
let (packet_number, rest) = rest.split_first()?;
let (len, rest) = read_varuint_slice(rest)?;
Some(FlowData {
packet_number: *packet_number,
data: rest.get(..len as usize)?,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_write_client_to_server() {
let mut out = [0u8; 64];
let size = write_client_to_server(1, b"hello", &mut out);
assert_eq!(size, 8);
assert_eq!(&out[..8], &[0x40, 0x01, 0x05, b'h', b'e', b'l', b'l', b'o']);
}
#[test]
fn test_write_client_poll() {
let mut out = [0u8; 64];
let size = write_client_poll(1, 48, &mut out);
assert_eq!(size, 3);
assert_eq!(&out[..3], &[0x42, 0x01, 0x30]);
}
#[test]
fn test_write_client_poll_flow() {
let mut out = [0u8; 64];
let size = write_client_poll_flow(1, 0xAB, 48, &mut out);
assert_eq!(size, 4);
assert_eq!(&out[..4], &[0x44, 0x01, 0xAB, 0x30]);
}
#[test]
fn test_parse_server_to_client() {
assert_eq!(
parse_server_to_client(&[0x41, 0x01, 0x03, b'a', b'b', b'c'], 1),
Some(b"abc".as_slice())
);
assert_eq!(
parse_server_to_client(&[0x41, 0x01, 0x00], 1),
Some(b"".as_slice())
);
assert_eq!(parse_server_to_client(&[0x41, 0x02, 0x01, b'a'], 1), None);
assert_eq!(parse_server_to_client(&[0x43, 0x01, 0x01, b'a'], 1), None);
assert_eq!(parse_server_to_client(&[0x41, 0x01, 0x05, b'a'], 1), None);
assert_eq!(parse_server_to_client(&[], 1), None);
}
#[test]
fn test_parse_server_to_client_flow() {
let result =
parse_server_to_client_flow(&[0x43, 0x01, 0x07, 0x03, b'a', b'b', b'c'], 1).unwrap();
assert_eq!(result.packet_number, 0x07);
assert_eq!(result.data, b"abc");
let result = parse_server_to_client_flow(&[0x43, 0x01, 0xFF, 0x00], 1).unwrap();
assert_eq!(result.packet_number, 0xFF);
assert_eq!(result.data, b"");
assert_eq!(
parse_server_to_client_flow(&[0x41, 0x01, 0x01, b'a'], 1),
None
);
assert_eq!(
parse_server_to_client_flow(&[0x43, 0x02, 0x07, 0x00], 1),
None
);
assert_eq!(parse_server_to_client_flow(&[0x43, 0x01, 0x07], 1), None);
assert_eq!(
parse_server_to_client_flow(&[0x43, 0x01, 0x07, 0x05, b'a'], 1),
None
);
}
#[test]
fn test_round_trip_via_write_combiner_sizes() {
let mut out = [0u8; 64];
let size = write_client_poll_flow(2, 5, 10, &mut out) as usize;
assert_eq!(&out[..size], &[0x44, 0x02, 0x05, 0x0A]);
let response = [0x43, 0x02, 0x06, 0x02, 0x11, 0x22];
let parsed = parse_server_to_client_flow(&response, 2).unwrap();
assert_eq!(parsed.packet_number, 6);
assert_eq!(parsed.data, &[0x11, 0x22]);
}
#[test]
fn test_read_varuint_multibyte() {
let (value, rest) = read_varuint_slice(&[0x80, 0x01, 0xAA]).unwrap();
assert_eq!(value, 128);
assert_eq!(rest, &[0xAA]);
assert!(read_varuint_slice(&[0x80, 0x80, 0x80, 0x80, 0x80]).is_none());
}
}