use crate::huffman;
use crate::io::SliceReader;
use crate::literal::{self, HpackString};
use crate::table::{Index, StaticEntry};
use crate::Result;
use byteorder::WriteBytesExt;
use std;
use std::borrow::Cow;
use std::io::Write;
use trackable::error::Failed;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderField<'a> {
name: Cow<'a, [u8]>,
value: Cow<'a, [u8]>,
}
impl<'a> HeaderField<'a> {
pub fn new(name: &'a [u8], value: &'a [u8]) -> Result<Self> {
let entry_size = name.len() + value.len() + 32;
track_assert!(
entry_size <= std::u16::MAX as usize,
Failed,
"Too large header field: {}",
entry_size
);
Ok(HeaderField {
name: Cow::Borrowed(name),
value: Cow::Borrowed(value),
})
}
pub fn name(&self) -> &[u8] {
self.name.as_ref()
}
pub fn value(&self) -> &[u8] {
self.value.as_ref()
}
pub fn entry_size(&self) -> u16 {
(self.name.len() + self.value.len() + 32) as u16
}
pub(crate) fn from_cow(name: Cow<'a, [u8]>, value: Cow<'a, [u8]>) -> Self {
let entry_size = name.len() + value.len() + 32;
debug_assert!(entry_size <= std::u16::MAX as usize);
HeaderField { name, value }
}
pub(crate) fn into_cow_name(self) -> Cow<'a, [u8]> {
self.name
}
pub(crate) fn as_borrowed(&self) -> HeaderField {
HeaderField {
name: Cow::Borrowed(self.name.as_ref()),
value: Cow::Borrowed(self.value.as_ref()),
}
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub enum RawHeaderField<'a> {
Indexed(IndexedHeaderField),
Literal(LiteralHeaderField<'a>),
}
impl<'a> RawHeaderField<'a> {
pub(crate) fn encode<W: Write>(&self, writer: W) -> Result<()> {
match *self {
RawHeaderField::Indexed(ref field) => track!(field.encode(writer)),
RawHeaderField::Literal(ref field) => track!(field.encode(writer)),
}
}
pub(crate) fn decode(reader: &mut SliceReader<'a>) -> Result<Self> {
let octet = track_io!(reader.peek_u8())?;
if octet >> 7 == 0b1 {
track!(IndexedHeaderField::decode(reader)).map(RawHeaderField::Indexed)
} else if octet >> 5 == 0b001 {
track_panic!(
Failed,
"Dynamic table size update MUST occur at the beginning of the first header block"
);
} else {
track!(LiteralHeaderField::decode(reader, octet).map(RawHeaderField::Literal,))
}
}
}
impl<'a> From<StaticEntry> for RawHeaderField<'a> {
fn from(f: StaticEntry) -> Self {
RawHeaderField::Indexed(IndexedHeaderField(Index::from(f)))
}
}
impl<'a> From<Index> for RawHeaderField<'a> {
fn from(f: Index) -> Self {
RawHeaderField::Indexed(IndexedHeaderField(f))
}
}
impl<'a> From<LiteralHeaderField<'a>> for RawHeaderField<'a> {
fn from(f: LiteralHeaderField<'a>) -> Self {
RawHeaderField::Literal(f)
}
}
#[derive(Debug)]
pub struct IndexedHeaderField(Index);
impl IndexedHeaderField {
pub fn new(index: Index) -> Self {
IndexedHeaderField(index)
}
pub fn index(&self) -> Index {
self.0
}
fn encode<W: Write>(&self, writer: W) -> Result<()> {
track!(literal::encode_u16(writer, 1, 7, self.0.as_u16()))
}
fn decode(reader: &mut SliceReader) -> Result<Self> {
let index = track!(literal::decode_u16(reader, 7))?.1;
let index = track!(Index::new(index))?;
Ok(IndexedHeaderField(index))
}
}
#[derive(Debug)]
pub struct LiteralHeaderField<'a> {
name: FieldName<'a>,
value: HpackString<'a>,
form: LiteralFieldForm,
}
impl<'a> LiteralHeaderField<'a> {
pub fn new(name: &'a [u8], value: &'a [u8]) -> Self {
LiteralHeaderField {
name: FieldName::Name(HpackString::Plain(Cow::Borrowed(name))),
value: HpackString::Plain(Cow::Borrowed(value)),
form: LiteralFieldForm::WithoutIndexing,
}
}
pub fn with_indexed_name<N>(name: N, value: &'a [u8]) -> Self
where
N: Into<Index>,
{
LiteralHeaderField {
name: FieldName::Index(name.into()),
value: HpackString::Plain(Cow::Borrowed(value)),
form: LiteralFieldForm::WithoutIndexing,
}
}
pub fn with_indexing(mut self) -> Self {
self.form = LiteralFieldForm::WithIndexing;
self
}
pub fn never_indexed(mut self) -> Self {
self.form = LiteralFieldForm::NeverIndexed;
self
}
pub fn with_huffman_encoded_name(mut self) -> Self {
if let FieldName::Name(HpackString::Plain(name)) = self.name {
self.name = FieldName::Name(HpackString::Huffman(Cow::Owned(huffman::encode(&name))));
self
} else {
self
}
}
pub fn with_huffman_encoded_value(mut self) -> Self {
if let HpackString::Plain(value) = self.value {
self.value = HpackString::Huffman(Cow::Owned(huffman::encode(&value)));
}
self
}
pub fn name(&self) -> &FieldName {
&self.name
}
pub fn value(&self) -> &HpackString {
&self.value
}
pub fn form(&self) -> LiteralFieldForm {
self.form
}
pub(crate) fn unwrap(self) -> (FieldName<'a>, HpackString<'a>, LiteralFieldForm) {
(self.name, self.value, self.form)
}
fn encode<W: Write>(&self, mut writer: W) -> Result<()> {
track!(self.encode_name(&mut writer))?;
track!(self.value.encode(writer))
}
fn decode(reader: &mut SliceReader<'a>, first_octet: u8) -> Result<Self> {
let (name, form) = track!(Self::decode_name_and_form(reader, first_octet))?;
let value = track!(HpackString::decode(reader))?;
Ok(LiteralHeaderField { name, value, form })
}
fn decode_name_and_form(
mut reader: &mut SliceReader<'a>,
first_octet: u8,
) -> Result<(FieldName<'a>, LiteralFieldForm)> {
if first_octet >> 6 == 0b01 {
let name = if first_octet & 0b11_1111 == 0 {
reader.consume(1);
let name = track!(HpackString::decode(reader))?;
FieldName::Name(name)
} else {
let index = track!(literal::decode_u16(&mut reader, 6))?.1;
let index = track!(Index::new(index))?;
FieldName::Index(index)
};
Ok((name, LiteralFieldForm::WithIndexing))
} else if first_octet == 0b0001_0000 {
let name = if first_octet & 0b1111 == 0 {
reader.consume(1);
let name = track!(HpackString::decode(reader))?;
FieldName::Name(name)
} else {
let index = track!(literal::decode_u16(&mut reader, 4))?.1;
let index = track!(Index::new(index))?;
FieldName::Index(index)
};
Ok((name, LiteralFieldForm::NeverIndexed))
} else {
let name = if first_octet & 0b1111 == 0 {
reader.consume(1);
let name = track!(HpackString::decode(reader))?;
FieldName::Name(name)
} else {
let index = track!(literal::decode_u16(&mut reader, 4))?.1;
let index = track!(Index::new(index))?;
FieldName::Index(index)
};
Ok((name, LiteralFieldForm::WithoutIndexing))
}
}
fn encode_name<W: Write>(&self, mut writer: W) -> Result<()> {
use self::FieldName::*;
use self::LiteralFieldForm::*;
match (self.form, &self.name) {
(WithIndexing, &Index(index)) => {
track!(literal::encode_u16(writer, 0b01, 6, index.as_u16()))
}
(WithIndexing, &Name(ref name)) => {
track_io!(writer.write_u8(0b01_000000))?;
track!(name.encode(writer))
}
(WithoutIndexing, &Index(index)) => {
track!(literal::encode_u16(writer, 0b0000, 4, index.as_u16()))
}
(WithoutIndexing, &Name(ref name)) => {
track_io!(writer.write_u8(0b0000_0000))?;
track!(name.encode(writer))
}
(NeverIndexed, &Index(index)) => {
track!(literal::encode_u16(writer, 0b0001, 4, index.as_u16()))
}
(NeverIndexed, &Name(ref name)) => {
track_io!(writer.write_u8(0b0001_0000))?;
track!(name.encode(writer))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LiteralFieldForm {
WithIndexing,
WithoutIndexing,
NeverIndexed,
}
#[derive(Debug)]
#[allow(missing_docs)]
pub enum FieldName<'a> {
Index(Index),
Name(HpackString<'a>),
}