mod builder;
use core::fmt;
use crate::qpack::integer::{QpackInteger, QpackIntegerParseError};
use crate::qpack::string::{QpackStringLiteral, QpackStringLiteralParseError};
pub use builder::{
QpackFieldLineBuildError, QpackIndexedFieldLineBuilder, QpackIndexedPostBaseFieldLineBuilder,
QpackLiteralNameFieldLineBuilder, QpackLiteralNameReferenceFieldLineBuilder,
QpackLiteralPostBaseNameReferenceFieldLineBuilder,
};
pub(crate) use builder::{QpackFieldLinePlan, QpackFieldLinePlanInput, plan_field_line};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackFieldLine<'a> {
Indexed(QpackIndexedFieldLine<'a>),
IndexedPostBase(QpackIndexedPostBaseFieldLine<'a>),
LiteralNameReference(QpackLiteralNameReferenceFieldLine<'a>),
LiteralPostBaseNameReference(QpackLiteralPostBaseNameReferenceFieldLine<'a>),
LiteralName(QpackLiteralNameFieldLine<'a>),
}
impl<'a> QpackFieldLine<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackFieldLineParseError> {
let first = *bytes
.first()
.ok_or(QpackFieldLineParseError::DispatchIncomplete)?;
if first & 0x80 != 0 {
let index =
QpackInteger::parse(bytes, 6).map_err(QpackFieldLineParseError::IndexedIndex)?;
return Ok(Self::Indexed(QpackIndexedFieldLine {
bytes: index.as_bytes(),
index,
}));
}
if first & 0xc0 == 0x40 {
let name_index = QpackInteger::parse(bytes, 4)
.map_err(QpackFieldLineParseError::LiteralNameReferenceNameIndex)?;
let name_length = name_index.as_bytes().len();
let value_bytes = bytes
.get(name_length..)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let value = QpackStringLiteral::parse(value_bytes, 8)
.map_err(QpackFieldLineParseError::LiteralNameReferenceValue)?;
let length = name_length
.checked_add(value.as_bytes().len())
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let line_bytes = bytes
.get(..length)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
return Ok(Self::LiteralNameReference(
QpackLiteralNameReferenceFieldLine {
bytes: line_bytes,
name_index,
value,
},
));
}
if first & 0xe0 == 0x20 {
let name = QpackStringLiteral::parse(bytes, 4)
.map_err(QpackFieldLineParseError::LiteralNameName)?;
let name_length = name.as_bytes().len();
let value_bytes = bytes
.get(name_length..)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let value = QpackStringLiteral::parse(value_bytes, 8)
.map_err(QpackFieldLineParseError::LiteralNameValue)?;
let length = name_length
.checked_add(value.as_bytes().len())
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let line_bytes = bytes
.get(..length)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
return Ok(Self::LiteralName(QpackLiteralNameFieldLine {
bytes: line_bytes,
name,
value,
}));
}
if first & 0xf0 == 0x10 {
let index = QpackInteger::parse(bytes, 4)
.map_err(QpackFieldLineParseError::IndexedPostBaseIndex)?;
return Ok(Self::IndexedPostBase(QpackIndexedPostBaseFieldLine {
bytes: index.as_bytes(),
index,
}));
}
let name_index = QpackInteger::parse(bytes, 3)
.map_err(QpackFieldLineParseError::LiteralPostBaseNameReferenceNameIndex)?;
let name_length = name_index.as_bytes().len();
let value_bytes = bytes
.get(name_length..)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let value = QpackStringLiteral::parse(value_bytes, 8)
.map_err(QpackFieldLineParseError::LiteralPostBaseNameReferenceValue)?;
let length = name_length
.checked_add(value.as_bytes().len())
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
let line_bytes = bytes
.get(..length)
.ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
Ok(Self::LiteralPostBaseNameReference(
QpackLiteralPostBaseNameReferenceFieldLine {
bytes: line_bytes,
name_index,
value,
},
))
}
pub const fn as_bytes(self) -> &'a [u8] {
match self {
Self::Indexed(line) => line.as_bytes(),
Self::IndexedPostBase(line) => line.as_bytes(),
Self::LiteralNameReference(line) => line.as_bytes(),
Self::LiteralPostBaseNameReference(line) => line.as_bytes(),
Self::LiteralName(line) => line.as_bytes(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackIndexedFieldLine<'a> {
bytes: &'a [u8],
index: QpackInteger<'a>,
}
impl<'a> QpackIndexedFieldLine<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn is_static(self) -> bool {
self.bytes[0] & 0x40 != 0
}
pub const fn index(self) -> QpackInteger<'a> {
self.index
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackIndexedPostBaseFieldLine<'a> {
bytes: &'a [u8],
index: QpackInteger<'a>,
}
impl<'a> QpackIndexedPostBaseFieldLine<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn index(self) -> QpackInteger<'a> {
self.index
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackLiteralNameReferenceFieldLine<'a> {
bytes: &'a [u8],
name_index: QpackInteger<'a>,
value: QpackStringLiteral<'a>,
}
impl<'a> QpackLiteralNameReferenceFieldLine<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn is_never_indexed(self) -> bool {
self.bytes[0] & 0x20 != 0
}
pub const fn is_static(self) -> bool {
self.bytes[0] & 0x10 != 0
}
pub const fn name_index(self) -> QpackInteger<'a> {
self.name_index
}
pub const fn value(self) -> QpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackLiteralPostBaseNameReferenceFieldLine<'a> {
bytes: &'a [u8],
name_index: QpackInteger<'a>,
value: QpackStringLiteral<'a>,
}
impl<'a> QpackLiteralPostBaseNameReferenceFieldLine<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn is_never_indexed(self) -> bool {
self.bytes[0] & 0x08 != 0
}
pub const fn name_index(self) -> QpackInteger<'a> {
self.name_index
}
pub const fn value(self) -> QpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackLiteralNameFieldLine<'a> {
bytes: &'a [u8],
name: QpackStringLiteral<'a>,
value: QpackStringLiteral<'a>,
}
impl<'a> QpackLiteralNameFieldLine<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn is_never_indexed(self) -> bool {
self.bytes[0] & 0x10 != 0
}
pub const fn name(self) -> QpackStringLiteral<'a> {
self.name
}
pub const fn value(self) -> QpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackFieldLineParseError {
DispatchIncomplete,
IndexedIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
IndexedPostBaseIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
LiteralNameReferenceNameIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
LiteralNameReferenceValue(
#[doc = "The string-literal parsing error."] QpackStringLiteralParseError,
),
LiteralPostBaseNameReferenceNameIndex(
#[doc = "The integer parsing error."] QpackIntegerParseError,
),
LiteralPostBaseNameReferenceValue(
#[doc = "The string-literal parsing error."] QpackStringLiteralParseError,
),
LiteralNameName(#[doc = "The string-literal parsing error."] QpackStringLiteralParseError),
LiteralNameValue(#[doc = "The string-literal parsing error."] QpackStringLiteralParseError),
RequiredLengthOverflow,
}
impl fmt::Display for QpackFieldLineParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DispatchIncomplete => formatter.write_str("QPACK field line is incomplete"),
Self::IndexedIndex(error) => {
write!(formatter, "invalid indexed field line index: {error}")
}
Self::IndexedPostBaseIndex(error) => write!(
formatter,
"invalid indexed post-base field line index: {error}"
),
Self::LiteralNameReferenceNameIndex(error) => {
write!(formatter, "invalid literal name-reference index: {error}")
}
Self::LiteralNameReferenceValue(error) => {
write!(formatter, "invalid literal name-reference value: {error}")
}
Self::LiteralPostBaseNameReferenceNameIndex(error) => write!(
formatter,
"invalid literal post-base name-reference index: {error}"
),
Self::LiteralPostBaseNameReferenceValue(error) => write!(
formatter,
"invalid literal post-base name-reference value: {error}"
),
Self::LiteralNameName(error) => {
write!(formatter, "invalid literal field name: {error}")
}
Self::LiteralNameValue(error) => {
write!(formatter, "invalid literal field value: {error}")
}
Self::RequiredLengthOverflow => formatter.write_str("QPACK field line length overflow"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackFieldLines<'a> {
bytes: &'a [u8],
}
impl<'a> QpackFieldLines<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackFieldLinesParseError> {
let mut remaining = bytes;
while !remaining.is_empty() {
let line = match QpackFieldLine::parse(remaining) {
Ok(line) => line,
Err(error) => {
return Err(QpackFieldLinesParseError {
offset: bytes.len() - remaining.len(),
error,
});
}
};
remaining = &remaining[line.as_bytes().len()..];
}
Ok(Self { bytes })
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn iter(self) -> QpackFieldLineIter<'a> {
QpackFieldLineIter::new(self.bytes)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackFieldLinesParseError {
offset: usize,
error: QpackFieldLineParseError,
}
impl QpackFieldLinesParseError {
pub const fn offset(self) -> usize {
self.offset
}
pub const fn error(self) -> QpackFieldLineParseError {
self.error
}
}
impl fmt::Display for QpackFieldLinesParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"QPACK field line at offset {}: {}",
self.offset, self.error
)
}
}
#[derive(Clone, Debug)]
pub struct QpackFieldLineIter<'a> {
remaining: &'a [u8],
failed: bool,
}
impl<'a> QpackFieldLineIter<'a> {
pub const fn new(bytes: &'a [u8]) -> Self {
Self {
remaining: bytes,
failed: false,
}
}
}
impl<'a> Iterator for QpackFieldLineIter<'a> {
type Item = Result<QpackFieldLine<'a>, QpackFieldLineParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed || self.remaining.is_empty() {
return None;
}
match QpackFieldLine::parse(self.remaining) {
Ok(line) => {
self.remaining = &self.remaining[line.as_bytes().len()..];
Some(Ok(line))
}
Err(error) => {
self.remaining = &[];
self.failed = true;
Some(Err(error))
}
}
}
}
impl core::iter::FusedIterator for QpackFieldLineIter<'_> {}