use crate::varint;
pub const DATA: u64 = 0x00;
pub const HEADERS: u64 = 0x01;
pub const CANCEL_PUSH: u64 = 0x03;
pub const SETTINGS: u64 = 0x04;
pub const PUSH_PROMISE: u64 = 0x05;
pub const GOAWAY: u64 = 0x07;
pub const MAX_PUSH_ID: u64 = 0x0d;
pub const H2_RESERVED: [u64; 4] = [0x02, 0x06, 0x08, 0x09];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FrameHeader {
pub ty: u64,
pub len: u64,
}
#[derive(Clone, Debug, Default)]
pub struct FrameHeaderParser {
buf: [u8; 16],
n: usize,
}
impl FrameHeaderParser {
pub fn new() -> Self {
Self::default()
}
pub fn feed(&mut self, buf: &[u8]) -> (usize, Option<FrameHeader>) {
for (i, &b) in buf.iter().enumerate() {
self.buf[self.n] = b;
self.n += 1;
let Some((ty, a)) = varint::decode(&self.buf[..self.n]) else {
continue;
};
if let Some((len, l)) = varint::decode(&self.buf[a..self.n]) {
if a + l == self.n {
self.n = 0;
return (i + 1, Some(FrameHeader { ty, len }));
}
}
}
(buf.len(), None)
}
pub fn mid_header(&self) -> bool {
self.n > 0
}
}
pub fn encode_header(ty: u64, len: u64, out: &mut Vec<u8>) {
varint::encode(ty, out);
varint::encode(len, out);
}
pub fn data_prefix(len: u64) -> ([u8; 9], u8) {
let mut out = [0u8; 9];
let mut v = [0u8; 8];
let n = varint::encode_to(len, &mut v);
out[1..=n].copy_from_slice(&v[..n]);
(out, (n + 1) as u8)
}
pub fn goaway_payload(id: u64, out: &mut Vec<u8>) {
varint::encode(id, out);
}
pub fn parse_single_varint_payload(p: &[u8]) -> Option<u64> {
match varint::decode(p) {
Some((v, n)) if n == p.len() => Some(v),
_ => None,
}
}
pub fn grease_frame_type(seed: u64) -> u64 {
0x1f * (seed % 0x1000) + 0x21
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_split_at_every_byte() {
let mut w = Vec::new();
encode_header(0x21 + 0x1f * 3, 16_384, &mut w);
for cut in 0..=w.len() {
let mut p = FrameHeaderParser::new();
let (n1, h1) = p.feed(&w[..cut]);
let (n2, h2) = p.feed(&w[cut..]);
assert_eq!(n1 + n2, w.len());
assert_eq!(
h1.or(h2),
Some(FrameHeader {
ty: 0x21 + 0x1f * 3,
len: 16_384
})
);
}
}
#[test]
fn feed_stops_at_header_end() {
let mut p = FrameHeaderParser::new();
assert_eq!(
p.feed(&[0x01, 0x02, 0xaa, 0xbb]),
(
2,
Some(FrameHeader {
ty: HEADERS,
len: 2
})
)
);
assert!(!p.mid_header());
assert_eq!(p.feed(&[0x40]), (1, None));
assert!(p.mid_header());
}
#[test]
fn data_prefix_is_type_and_len() {
let (b, n) = data_prefix(300);
assert_eq!(&b[..n as usize], &[0x00, 0x41, 0x2c]);
}
#[test]
fn single_varint_payload_rejects_trailing_or_short() {
assert_eq!(parse_single_varint_payload(&[0x04]), Some(4));
assert_eq!(parse_single_varint_payload(&[0x04, 0x00]), None);
assert_eq!(parse_single_varint_payload(&[0x40]), None);
}
}