#![cfg_attr(docsrs, doc(cfg(feature = "hpack")))]
extern crate alloc;
use alloc::vec::Vec;
use crate::error::Error;
pub mod huffman;
mod integer;
mod table;
pub use huffman::Http2Huffman;
use integer::{decode_int, encode_int};
use table::DynamicTable;
pub const DEFAULT_TABLE_SIZE: usize = 4096;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderField {
pub name: Vec<u8>,
pub value: Vec<u8>,
pub sensitive: bool,
}
impl HeaderField {
pub fn new(name: &[u8], value: &[u8]) -> Self {
HeaderField {
name: name.to_vec(),
value: value.to_vec(),
sensitive: false,
}
}
pub fn sensitive(name: &[u8], value: &[u8]) -> Self {
HeaderField {
name: name.to_vec(),
value: value.to_vec(),
sensitive: true,
}
}
}
#[derive(Debug)]
pub struct HpackEncoder {
table: DynamicTable,
use_huffman: bool,
pending_size_update: Option<usize>,
}
impl Default for HpackEncoder {
fn default() -> Self {
Self::new()
}
}
impl HpackEncoder {
pub fn new() -> Self {
HpackEncoder {
table: DynamicTable::new(DEFAULT_TABLE_SIZE),
use_huffman: true,
pending_size_update: None,
}
}
pub fn with_max_table_size(max: usize) -> Self {
HpackEncoder {
table: DynamicTable::new(max),
use_huffman: true,
pending_size_update: Some(max),
}
}
pub fn set_huffman(&mut self, on: bool) {
self.use_huffman = on;
}
pub fn encode(&mut self, fields: &[HeaderField]) -> Vec<u8> {
let mut out = Vec::new();
if let Some(max) = self.pending_size_update.take() {
encode_int(&mut out, max, 5, 0x20);
}
for f in fields {
self.encode_field(&mut out, f);
}
out
}
fn encode_field(&mut self, out: &mut Vec<u8>, f: &HeaderField) {
if f.sensitive {
let name_idx = self.table.find(&f.name, &f.value).map(|m| m.index);
let name_idx = match name_idx {
Some(i) if self.table.get(i).map(|(n, _)| n == f.name).unwrap_or(false) => Some(i),
_ => None,
};
self.emit_literal(out, 0x10, 4, name_idx, &f.name, &f.value);
return;
}
match self.table.find(&f.name, &f.value) {
Some(m) if m.value_matched => {
encode_int(out, m.index, 7, 0x80);
}
Some(m) => {
self.emit_literal(out, 0x40, 6, Some(m.index), &f.name, &f.value);
self.table.insert(&f.name, &f.value);
}
None => {
self.emit_literal(out, 0x40, 6, None, &f.name, &f.value);
self.table.insert(&f.name, &f.value);
}
}
}
fn emit_literal(
&self,
out: &mut Vec<u8>,
pattern: u8,
prefix: u32,
name_idx: Option<usize>,
name: &[u8],
value: &[u8],
) {
match name_idx {
Some(i) => encode_int(out, i, prefix, pattern),
None => {
encode_int(out, 0, prefix, pattern);
self.emit_string(out, name);
}
}
self.emit_string(out, value);
}
fn emit_string(&self, out: &mut Vec<u8>, s: &[u8]) {
if self.use_huffman && huffman::encoded_len(s) < s.len() {
let coded = huffman::encode(s);
encode_int(out, coded.len(), 7, 0x80); out.extend_from_slice(&coded);
} else {
encode_int(out, s.len(), 7, 0x00);
out.extend_from_slice(s);
}
}
}
#[derive(Debug)]
pub struct HpackDecoder {
table: DynamicTable,
size_limit: usize,
}
impl Default for HpackDecoder {
fn default() -> Self {
Self::new()
}
}
impl HpackDecoder {
pub fn new() -> Self {
Self::with_max_table_size(DEFAULT_TABLE_SIZE)
}
pub fn with_max_table_size(max: usize) -> Self {
HpackDecoder {
table: DynamicTable::new(max),
size_limit: max,
}
}
pub fn decode(&mut self, block: &[u8]) -> Result<Vec<HeaderField>, Error> {
let mut fields = Vec::new();
let mut pos = 0;
while pos < block.len() {
let b = block[pos];
if b & 0x80 != 0 {
let (idx, np) = decode_int(block, pos, 7)?;
pos = np;
if idx == 0 {
return Err(Error::Corrupt);
}
let (n, v) = self.table.get(idx).ok_or(Error::Corrupt)?;
fields.push(HeaderField::new(n, v));
} else if b & 0x40 != 0 {
let (name, value, np) = self.read_literal(block, pos, 6)?;
pos = np;
self.table.insert(&name, &value);
fields.push(HeaderField {
name,
value,
sensitive: false,
});
} else if b & 0x20 != 0 {
let (new_max, np) = decode_int(block, pos, 5)?;
pos = np;
if new_max > self.size_limit {
return Err(Error::Corrupt);
}
self.table.set_max_size(new_max);
} else {
let sensitive = b & 0x10 != 0;
let (name, value, np) = self.read_literal(block, pos, 4)?;
pos = np;
fields.push(HeaderField {
name,
value,
sensitive,
});
}
}
Ok(fields)
}
fn read_literal(
&self,
block: &[u8],
pos: usize,
prefix: u32,
) -> Result<(Vec<u8>, Vec<u8>, usize), Error> {
let (idx, mut p) = decode_int(block, pos, prefix)?;
let name = if idx == 0 {
let (n, np) = read_string(block, p)?;
p = np;
n
} else {
let (n, _) = self.table.get(idx).ok_or(Error::Corrupt)?;
n.to_vec()
};
let (value, np) = read_string(block, p)?;
Ok((name, value, np))
}
#[cfg(test)]
pub(crate) fn table_max_size(&self) -> usize {
self.table.max_size()
}
}
fn read_string(block: &[u8], pos: usize) -> Result<(Vec<u8>, usize), Error> {
let first = *block.get(pos).ok_or(Error::UnexpectedEnd)?;
let huff = first & 0x80 != 0;
let (len, p) = decode_int(block, pos, 7)?;
let end = p.checked_add(len).ok_or(Error::Corrupt)?;
if end > block.len() {
return Err(Error::UnexpectedEnd);
}
let raw = &block[p..end];
let data = if huff {
huffman::decode(raw)?
} else {
raw.to_vec()
};
Ok((data, end))
}
#[cfg(test)]
mod tests;