use super::{
error::H3ErrorCode,
quic_varint::{self, QuicVarIntError},
settings::H3Settings,
};
mod stream;
#[cfg(feature = "unstable")]
pub use stream::ActiveFrame;
pub use stream::FrameStream;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum FrameType {
Data = 0x00,
Headers = 0x01,
CancelPush = 0x03,
Settings = 0x04,
PushPromise = 0x05,
Goaway = 0x07,
MaxPushId = 0x0d,
WebTransport = 0x41,
}
impl From<FrameType> for u64 {
fn from(val: FrameType) -> Self {
val as u64
}
}
impl TryFrom<u64> for FrameType {
type Error = u64;
fn try_from(value: u64) -> Result<Self, u64> {
match value {
0x00 => Ok(Self::Data),
0x01 => Ok(Self::Headers),
0x03 => Ok(Self::CancelPush),
0x04 => Ok(Self::Settings),
0x05 => Ok(Self::PushPromise),
0x07 => Ok(Self::Goaway),
0x0d => Ok(Self::MaxPushId),
0x41 => Ok(Self::WebTransport),
other => {
log::trace!("did not recognize frame type {value}");
Err(other)
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct FrameHeader {
frame_type: Option<FrameType>,
payload_length: u64,
}
impl FrameHeader {
fn decode(input: &[u8]) -> Result<(Self, usize), FrameDecodeError> {
let (frame_type, frame_type_bytes) = match quic_varint::decode::<FrameType>(input) {
Ok((ft, bytes)) => (Some(ft), bytes),
Err(QuicVarIntError::UnexpectedEnd) => return Err(FrameDecodeError::Incomplete),
Err(QuicVarIntError::UnknownValue { bytes, .. }) => (None, bytes),
};
let (payload_length, payload_length_bytes) =
quic_varint::decode::<u64>(&input[frame_type_bytes..])
.map_err(|_| FrameDecodeError::Incomplete)?;
Ok((
Self {
frame_type,
payload_length,
},
frame_type_bytes + payload_length_bytes,
))
}
#[cfg(test)]
fn encode(&self, buf: &mut [u8]) -> Option<usize> {
let mut written = 0;
if let Some(ft) = self.frame_type {
written += quic_varint::encode(ft, buf)?;
written += quic_varint::encode(self.payload_length, &mut buf[written..])?;
}
Some(written)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameDecodeError {
Incomplete,
Error(H3ErrorCode),
}
impl From<H3ErrorCode> for FrameDecodeError {
fn from(code: H3ErrorCode) -> Self {
Self::Error(code)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
#[non_exhaustive]
pub enum Frame {
Data(u64),
Headers(u64),
CancelPush(u64),
Settings(H3Settings),
PushPromise {
push_id: u64,
field_section_length: u64,
},
Goaway(u64),
MaxPushId(u64),
WebTransport(u64),
Unknown(u64),
}
impl Frame {
pub fn decode(input: &[u8]) -> Result<(Self, usize), FrameDecodeError> {
let mut bytes_read = 0;
let (header, header_len) = FrameHeader::decode(input)?;
log::trace!("Decoded frame header {header:?}");
bytes_read += header_len;
match header.frame_type {
Some(FrameType::Data) => Ok((Frame::Data(header.payload_length), header_len)),
Some(FrameType::Headers) => Ok((Frame::Headers(header.payload_length), header_len)),
Some(FrameType::WebTransport) => {
Ok((Frame::WebTransport(header.payload_length), header_len))
}
None => Ok((Frame::Unknown(header.payload_length), header_len)),
Some(FrameType::PushPromise) => {
let (push_id, push_id_len) = quic_varint::decode::<u64>(&input[bytes_read..])
.map_err(|_| FrameDecodeError::Incomplete)?;
bytes_read += push_id_len;
if push_id_len as u64 > header.payload_length {
return Err(H3ErrorCode::FrameError.into());
}
Ok((
Frame::PushPromise {
push_id,
field_section_length: header.payload_length - push_id_len as u64,
},
bytes_read,
))
}
Some(FrameType::Settings) => {
let payload = require_payload(&input[bytes_read..], header.payload_length)?;
let settings = H3Settings::decode(payload).ok_or(H3ErrorCode::SettingsError)?;
Ok((Frame::Settings(settings), header_len + payload.len()))
}
Some(FrameType::Goaway) => decode_single_varint(
&input[bytes_read..],
header_len,
header.payload_length,
Frame::Goaway,
),
Some(FrameType::CancelPush) => decode_single_varint(
&input[bytes_read..],
header_len,
header.payload_length,
Frame::CancelPush,
),
Some(FrameType::MaxPushId) => decode_single_varint(
&input[bytes_read..],
header_len,
header.payload_length,
Frame::MaxPushId,
),
}
}
pub fn encoded_len(&self) -> usize {
match self {
Frame::Data(len) => frame_header_len(FrameType::Data, *len),
Frame::Headers(len) => frame_header_len(FrameType::Headers, *len),
Frame::CancelPush(id) => single_varint_frame_len(FrameType::CancelPush, *id),
Frame::Goaway(id) => single_varint_frame_len(FrameType::Goaway, *id),
Frame::MaxPushId(id) => single_varint_frame_len(FrameType::MaxPushId, *id),
Frame::Settings(settings) => {
let payload_len = settings.encoded_len();
frame_header_len(FrameType::Settings, payload_len as u64) + payload_len
}
Frame::PushPromise {
push_id,
field_section_length,
} => {
let push_id_len = quic_varint::encoded_len(*push_id);
let payload_len = push_id_len as u64 + field_section_length;
frame_header_len(FrameType::PushPromise, payload_len) + push_id_len
}
Frame::WebTransport(session_id) => {
quic_varint::encoded_len(FrameType::WebTransport)
+ quic_varint::encoded_len(*session_id)
}
Frame::Unknown(_) => 0,
}
}
pub fn encode(&self, buf: &mut [u8]) -> Option<usize> {
let len = self.encoded_len();
if buf.len() < len {
return None;
}
match self {
Frame::Data(payload_len) => encode_frame_header(FrameType::Data, *payload_len, buf),
Frame::Headers(payload_len) => {
encode_frame_header(FrameType::Headers, *payload_len, buf)
}
Frame::CancelPush(id) => encode_single_varint_frame(FrameType::CancelPush, *id, buf),
Frame::Goaway(id) => encode_single_varint_frame(FrameType::Goaway, *id, buf),
Frame::MaxPushId(id) => encode_single_varint_frame(FrameType::MaxPushId, *id, buf),
Frame::Settings(settings) => {
let mut written = 0;
let payload_len = settings.encoded_len() as u64;
written +=
encode_frame_header(FrameType::Settings, payload_len, &mut buf[written..])?;
written += settings.encode(&mut buf[written..])?;
Some(written)
}
Frame::PushPromise {
push_id,
field_section_length,
} => {
let mut written = 0;
let push_id_len = quic_varint::encoded_len(*push_id) as u64;
let payload_length = push_id_len + field_section_length;
written += encode_frame_header(
FrameType::PushPromise,
payload_length,
&mut buf[written..],
)?;
written += quic_varint::encode(*push_id, &mut buf[written..])?;
Some(written)
}
Frame::WebTransport(session_id) => {
let mut written = quic_varint::encode(FrameType::WebTransport, buf)?;
written += quic_varint::encode(*session_id, &mut buf[written..])?;
Some(written)
}
Frame::Unknown(_) => Some(0),
}
}
}
fn frame_header_len(frame_type: FrameType, payload_length: u64) -> usize {
quic_varint::encoded_len(frame_type) + quic_varint::encoded_len(payload_length)
}
fn single_varint_frame_len(frame_type: FrameType, value: u64) -> usize {
let payload_len = quic_varint::encoded_len(value);
frame_header_len(frame_type, payload_len as u64) + payload_len
}
fn encode_frame_header(
frame_type: FrameType,
payload_length: u64,
buf: &mut [u8],
) -> Option<usize> {
let mut written = 0;
written += quic_varint::encode(frame_type, &mut buf[written..])?;
written += quic_varint::encode(payload_length, &mut buf[written..])?;
Some(written)
}
fn encode_single_varint_frame(frame_type: FrameType, value: u64, buf: &mut [u8]) -> Option<usize> {
let payload_len = quic_varint::encoded_len(value) as u64;
let mut written = encode_frame_header(frame_type, payload_len, buf)?;
written += quic_varint::encode(value, &mut buf[written..])?;
Some(written)
}
fn require_payload(after_header: &[u8], payload_length: u64) -> Result<&[u8], FrameDecodeError> {
let len = usize::try_from(payload_length).map_err(|_| H3ErrorCode::FrameError)?;
if after_header.len() < len {
Err(FrameDecodeError::Incomplete)
} else {
Ok(&after_header[..len])
}
}
fn decode_single_varint(
after_header: &[u8],
header_len: usize,
payload_length: u64,
wrap: fn(u64) -> Frame,
) -> Result<(Frame, usize), FrameDecodeError> {
let payload = require_payload(after_header, payload_length)?;
let (value, bytes_read) =
quic_varint::decode::<u64>(payload).map_err(|_| H3ErrorCode::FrameError)?;
if bytes_read != after_header.len() {
return Err(H3ErrorCode::FrameError.into());
}
Ok((wrap(value), header_len + payload.len()))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum UniStreamType {
Control = 0x00,
Push = 0x01,
QpackEncoder = 0x02,
QpackDecoder = 0x03,
WebTransport = 0x54,
}
impl From<UniStreamType> for u64 {
fn from(value: UniStreamType) -> Self {
value as u64
}
}
impl TryFrom<u64> for UniStreamType {
type Error = u64;
fn try_from(value: u64) -> Result<Self, u64> {
match value {
0x00 => Ok(Self::Control),
0x01 => Ok(Self::Push),
0x02 => Ok(Self::QpackEncoder),
0x03 => Ok(Self::QpackDecoder),
0x54 => Ok(Self::WebTransport),
other => Err(other),
}
}
}