use crate::state::{ParseError, ParseErrorKind, StreamId};
#[cfg(test)]
mod tests;
pub const CONNECTION_PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
pub(crate) const FRAME_TYPE_DATA: u8 = 0x00;
pub(crate) const FRAME_TYPE_HEADERS: u8 = 0x01;
pub(crate) const FRAME_TYPE_PRIORITY: u8 = 0x02;
pub(crate) const FRAME_TYPE_RST_STREAM: u8 = 0x03;
pub(crate) const FRAME_TYPE_SETTINGS: u8 = 0x04;
pub(crate) const FRAME_TYPE_PUSH_PROMISE: u8 = 0x05;
pub(crate) const FRAME_TYPE_PING: u8 = 0x06;
pub(crate) const FRAME_TYPE_GOAWAY: u8 = 0x07;
pub(crate) const FRAME_TYPE_WINDOW_UPDATE: u8 = 0x08;
pub(crate) const FRAME_TYPE_CONTINUATION: u8 = 0x09;
pub(crate) const FLAG_END_STREAM: u8 = 0x01;
pub(crate) const FLAG_END_HEADERS: u8 = 0x04;
pub(crate) const FLAG_PADDED: u8 = 0x08;
pub(crate) const FLAG_PRIORITY: u8 = 0x20;
pub(crate) const FRAME_HEADER_SIZE: usize = 9;
pub(crate) const MAX_FRAME_PAYLOAD_LENGTH: u32 = (1 << 24) - 1;
#[derive(Debug, Clone)]
pub(crate) struct FrameHeader {
pub(crate) length: u32,
pub(crate) frame_type: u8,
pub(crate) flags: u8,
pub(crate) stream_id: StreamId,
}
pub fn is_http2_preface(buffer: &[u8]) -> bool {
buffer.len() >= CONNECTION_PREFACE.len() && buffer.starts_with(CONNECTION_PREFACE)
}
pub fn looks_like_http2_frame(buffer: &[u8]) -> bool {
if buffer.len() < FRAME_HEADER_SIZE {
return false;
}
let length = u32::from_be_bytes([0, buffer[0], buffer[1], buffer[2]]);
let frame_type = buffer[3];
if frame_type > 9 {
return false;
}
let raw_stream_id = u32::from_be_bytes([buffer[5] & 0x7F, buffer[6], buffer[7], buffer[8]]);
if length > MAX_FRAME_PAYLOAD_LENGTH {
return false;
}
if frame_type == FRAME_TYPE_SETTINGS && raw_stream_id == 0 && !length.is_multiple_of(6) {
return false;
}
true
}
pub(crate) fn parse_frame_header(buffer: &[u8]) -> Result<FrameHeader, ParseError> {
if buffer.len() < FRAME_HEADER_SIZE {
return Err(ParseError::new(ParseErrorKind::Http2BufferTooSmall));
}
let length = u32::from_be_bytes([0, buffer[0], buffer[1], buffer[2]]);
let frame_type = buffer[3];
let flags = buffer[4];
let stream_id = StreamId(u32::from_be_bytes([
buffer[5] & 0x7F,
buffer[6],
buffer[7],
buffer[8],
]));
Ok(FrameHeader {
length,
frame_type,
flags,
stream_id,
})
}