use core::fmt;
use super::integer::{HpackInteger, HpackIntegerParseError};
use super::string::{HpackStringLiteral, HpackStringLiteralParseError};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HpackRepresentation<'a> {
Indexed(HpackIndexedField<'a>),
Literal(HpackLiteralField<'a>),
DynamicTableSizeUpdate(HpackDynamicTableSizeUpdate<'a>),
}
impl<'a> HpackRepresentation<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
let first = *bytes
.first()
.ok_or(HpackRepresentationParseError::IndexedIndex(
HpackIntegerParseError::Incomplete {
required: 1,
available: 0,
},
))?;
if first & 0x80 != 0 {
HpackIndexedField::parse(bytes).map(Self::Indexed)
} else if first & 0x40 != 0 {
HpackLiteralField::parse(bytes, HpackLiteralMode::IncrementalIndexing)
.map(Self::Literal)
} else if first & 0x20 != 0 {
HpackDynamicTableSizeUpdate::parse(bytes).map(Self::DynamicTableSizeUpdate)
} else if first & 0x10 != 0 {
HpackLiteralField::parse(bytes, HpackLiteralMode::NeverIndexed).map(Self::Literal)
} else {
HpackLiteralField::parse(bytes, HpackLiteralMode::WithoutIndexing).map(Self::Literal)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HpackIndexedField<'a> {
bytes: &'a [u8],
index: HpackInteger<'a>,
}
impl<'a> HpackIndexedField<'a> {
fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
let index =
HpackInteger::parse(bytes, 7).map_err(HpackRepresentationParseError::IndexedIndex)?;
if index.value() == 0 {
return Err(HpackRepresentationParseError::IndexedFieldZero);
}
Ok(Self {
bytes: index.as_bytes(),
index,
})
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn index(self) -> HpackInteger<'a> {
self.index
}
pub const fn index_value(self) -> u64 {
self.index.value()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HpackLiteralMode {
IncrementalIndexing,
WithoutIndexing,
NeverIndexed,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HpackLiteralField<'a> {
bytes: &'a [u8],
mode: HpackLiteralMode,
name_index: HpackInteger<'a>,
name: Option<HpackStringLiteral<'a>>,
value: HpackStringLiteral<'a>,
}
impl<'a> HpackLiteralField<'a> {
fn parse(
bytes: &'a [u8],
mode: HpackLiteralMode,
) -> Result<Self, HpackRepresentationParseError> {
let prefix_bits = match mode {
HpackLiteralMode::IncrementalIndexing => 6,
HpackLiteralMode::WithoutIndexing | HpackLiteralMode::NeverIndexed => 4,
};
let name_index = HpackInteger::parse(bytes, prefix_bits)
.map_err(HpackRepresentationParseError::LiteralNameIndex)?;
let after_name_index = bytes
.get(name_index.as_bytes().len()..)
.ok_or(HpackRepresentationParseError::LengthOverflow)?;
let name = if name_index.value() == 0 {
let name = HpackStringLiteral::parse(after_name_index)
.map_err(HpackRepresentationParseError::LiteralName)?;
Some(name)
} else {
None
};
let after_name = match name {
Some(name) => after_name_index
.get(name.as_bytes().len()..)
.ok_or(HpackRepresentationParseError::LengthOverflow)?,
None => after_name_index,
};
let value = HpackStringLiteral::parse(after_name)
.map_err(HpackRepresentationParseError::LiteralValue)?;
let total = name_index
.as_bytes()
.len()
.checked_add(name.map_or(0, |literal| literal.as_bytes().len()))
.and_then(|length| length.checked_add(value.as_bytes().len()))
.ok_or(HpackRepresentationParseError::LengthOverflow)?;
let representation = bytes
.get(..total)
.ok_or(HpackRepresentationParseError::LengthOverflow)?;
Ok(Self {
bytes: representation,
mode,
name_index,
name,
value,
})
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn mode(self) -> HpackLiteralMode {
self.mode
}
pub const fn name_index(self) -> HpackInteger<'a> {
self.name_index
}
pub const fn name_index_value(self) -> u64 {
self.name_index.value()
}
pub const fn name(self) -> Option<HpackStringLiteral<'a>> {
self.name
}
pub const fn value(self) -> HpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HpackDynamicTableSizeUpdate<'a> {
bytes: &'a [u8],
size: HpackInteger<'a>,
}
impl<'a> HpackDynamicTableSizeUpdate<'a> {
fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
let size = HpackInteger::parse(bytes, 5)
.map_err(HpackRepresentationParseError::DynamicTableSize)?;
Ok(Self {
bytes: size.as_bytes(),
size,
})
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn size(self) -> HpackInteger<'a> {
self.size
}
pub const fn size_value(self) -> u64 {
self.size.value()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HpackRepresentationParseError {
IndexedIndex(HpackIntegerParseError),
IndexedFieldZero,
LiteralNameIndex(HpackIntegerParseError),
LiteralName(HpackStringLiteralParseError),
LiteralValue(HpackStringLiteralParseError),
DynamicTableSize(HpackIntegerParseError),
LengthOverflow,
}
impl fmt::Display for HpackRepresentationParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IndexedIndex(error) => write!(f, "invalid HPACK indexed field index: {error}"),
Self::IndexedFieldZero => f.write_str("HPACK indexed field index must not be zero"),
Self::LiteralNameIndex(error) => {
write!(f, "invalid HPACK literal field name index: {error}")
}
Self::LiteralName(error) => write!(f, "invalid HPACK literal field name: {error}"),
Self::LiteralValue(error) => write!(f, "invalid HPACK literal field value: {error}"),
Self::DynamicTableSize(error) => write!(f, "invalid HPACK dynamic table size: {error}"),
Self::LengthOverflow => f.write_str("HPACK representation length overflows usize"),
}
}
}