#![cfg_attr(docsrs, doc(cfg(feature = "qpack")))]
extern crate alloc;
use alloc::vec::Vec;
use crate::error::Error;
use crate::hpack::HeaderField;
use crate::hpack::huffman;
mod dynamic_table;
mod integer;
mod static_table;
use dynamic_table::DynamicTable;
use integer::{decode_int, encode_int};
pub const DEFAULT_MAX_TABLE_CAPACITY: usize = 4096;
#[derive(Debug)]
pub struct QpackEncoder {
use_huffman: bool,
}
impl Default for QpackEncoder {
fn default() -> Self {
Self::new()
}
}
impl QpackEncoder {
pub fn new() -> Self {
QpackEncoder { use_huffman: true }
}
pub fn set_huffman(&mut self, on: bool) {
self.use_huffman = on;
}
pub fn encode_field_section(&mut self, fields: &[HeaderField]) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x00); out.push(0x00); for f in fields {
self.encode_field(&mut out, f);
}
out
}
fn encode_field(&self, out: &mut Vec<u8>, f: &HeaderField) {
match static_table::find(&f.name, &f.value) {
Some((idx, true)) if !f.sensitive => {
encode_int(out, idx, 6, 0b1100_0000);
}
Some((idx, _)) => {
let n_bit = if f.sensitive { 0b0010_0000 } else { 0 };
encode_int(out, idx, 4, 0b0101_0000 | n_bit);
self.emit_string(out, &f.value, 7, 0);
}
None => {
let n_bit = if f.sensitive { 0b0001_0000 } else { 0 };
self.emit_string(out, &f.name, 3, 0b0010_0000 | n_bit);
self.emit_string(out, &f.value, 7, 0);
}
}
}
fn emit_string(&self, out: &mut Vec<u8>, s: &[u8], n: u32, pattern: u8) {
let h_flag = 1u8 << n;
if self.use_huffman && huffman::encoded_len(s) < s.len() {
let coded = huffman::encode(s);
encode_int(out, coded.len(), n, pattern | h_flag);
out.extend_from_slice(&coded);
} else {
encode_int(out, s.len(), n, pattern);
out.extend_from_slice(s);
}
}
}
#[derive(Debug)]
pub struct QpackDecoder {
table: DynamicTable,
max_capacity: usize,
}
impl Default for QpackDecoder {
fn default() -> Self {
Self::new()
}
}
impl QpackDecoder {
pub fn new() -> Self {
Self::with_max_table_capacity(DEFAULT_MAX_TABLE_CAPACITY)
}
pub fn with_max_table_capacity(max: usize) -> Self {
QpackDecoder {
table: DynamicTable::new(),
max_capacity: max,
}
}
pub fn feed_encoder_stream(&mut self, data: &[u8]) -> Result<(), Error> {
let mut pos = 0;
while pos < data.len() {
let b = data[pos];
if b & 0b1000_0000 != 0 {
let t_static = b & 0b0100_0000 != 0;
let (name_idx, np) = decode_int(data, pos, 6)?;
pos = np;
let (value, np) = read_string(data, pos, 7)?;
pos = np;
let name = self.resolve_insert_name(name_idx, t_static)?;
self.do_insert(&name, &value)?;
} else if b & 0b0100_0000 != 0 {
let (name, np) = read_string(data, pos, 5)?;
pos = np;
let (value, np) = read_string(data, pos, 7)?;
pos = np;
self.do_insert(&name, &value)?;
} else if b & 0b0010_0000 != 0 {
let (cap, np) = decode_int(data, pos, 5)?;
pos = np;
if !self.table.set_capacity(cap, self.max_capacity) {
return Err(Error::Corrupt);
}
} else {
let (rel, np) = decode_int(data, pos, 5)?;
pos = np;
let abs = self
.table
.relative_to_absolute_encoder(rel)
.ok_or(Error::Corrupt)?;
let (n, v) = self.table.get_absolute(abs).ok_or(Error::Corrupt)?;
let (n, v) = (n.to_vec(), v.to_vec());
self.do_insert(&n, &v)?;
}
}
Ok(())
}
fn resolve_insert_name(&self, idx: usize, t_static: bool) -> Result<Vec<u8>, Error> {
if t_static {
let (n, _) = static_table::get(idx).ok_or(Error::Corrupt)?;
Ok(n.to_vec())
} else {
let abs = self
.table
.relative_to_absolute_encoder(idx)
.ok_or(Error::Corrupt)?;
let (n, _) = self.table.get_absolute(abs).ok_or(Error::Corrupt)?;
Ok(n.to_vec())
}
}
fn do_insert(&mut self, name: &[u8], value: &[u8]) -> Result<(), Error> {
if !self.table.can_insert(name, value) {
return Err(Error::Corrupt);
}
self.table.insert(name, value).ok_or(Error::Corrupt)?;
Ok(())
}
pub fn decode_field_section(&mut self, block: &[u8]) -> Result<Vec<HeaderField>, Error> {
let (req_insert_count, mut pos) = self.decode_required_insert_count(block)?;
let base = self.decode_base(block, &mut pos, req_insert_count)?;
if req_insert_count > self.table.insert_count() {
return Err(Error::Corrupt);
}
let mut fields = Vec::new();
while pos < block.len() {
let b = block[pos];
if b & 0b1000_0000 != 0 {
let t_static = b & 0b0100_0000 != 0;
let (idx, np) = decode_int(block, pos, 6)?;
pos = np;
let (n, v) = self.lookup_indexed(idx, t_static, base, req_insert_count)?;
fields.push(HeaderField::new(n.as_slice(), v.as_slice()));
} else if b & 0b0100_0000 != 0 {
let sensitive = b & 0b0010_0000 != 0;
let t_static = b & 0b0001_0000 != 0;
let (idx, np) = decode_int(block, pos, 4)?;
pos = np;
let name = self.lookup_name_ref(idx, t_static, base, req_insert_count)?;
let (value, np) = read_string(block, pos, 7)?;
pos = np;
fields.push(HeaderField {
name,
value,
sensitive,
});
} else if b & 0b0010_0000 != 0 {
let sensitive = b & 0b0001_0000 != 0;
let (name, np) = read_string(block, pos, 3)?;
pos = np;
let (value, np) = read_string(block, pos, 7)?;
pos = np;
fields.push(HeaderField {
name,
value,
sensitive,
});
} else if b & 0b0001_0000 != 0 {
let (idx, np) = decode_int(block, pos, 4)?;
pos = np;
let abs = DynamicTable::post_base_to_absolute(base, idx).ok_or(Error::Corrupt)?;
let (n, v) = self.lookup_dynamic_abs(abs, req_insert_count)?;
fields.push(HeaderField::new(n.as_slice(), v.as_slice()));
} else {
let sensitive = b & 0b0000_1000 != 0;
let (idx, np) = decode_int(block, pos, 3)?;
pos = np;
let abs = DynamicTable::post_base_to_absolute(base, idx).ok_or(Error::Corrupt)?;
let (n, _) = self.lookup_dynamic_abs(abs, req_insert_count)?;
let (value, np) = read_string(block, pos, 7)?;
pos = np;
fields.push(HeaderField {
name: n,
value,
sensitive,
});
}
}
Ok(fields)
}
fn decode_required_insert_count(&self, block: &[u8]) -> Result<(usize, usize), Error> {
let (enc, pos) = decode_int(block, 0, 8)?;
if enc == 0 {
return Ok((0, pos));
}
let max_entries = self.max_capacity / 32;
let full_range = 2 * max_entries;
if full_range == 0 || enc > full_range {
return Err(Error::Corrupt);
}
let total_inserts = self.table.insert_count();
let max_value = total_inserts + max_entries;
let max_wrapped = (max_value / full_range) * full_range;
let mut req = max_wrapped + enc - 1;
if req > max_value {
if req <= full_range {
return Err(Error::Corrupt);
}
req -= full_range;
}
if req == 0 {
return Err(Error::Corrupt);
}
Ok((req, pos))
}
fn decode_base(
&self,
block: &[u8],
pos: &mut usize,
req_insert_count: usize,
) -> Result<usize, Error> {
let sign = *block.get(*pos).ok_or(Error::UnexpectedEnd)? & 0x80 != 0;
let (delta, np) = decode_int(block, *pos, 7)?;
*pos = np;
if sign {
req_insert_count
.checked_sub(delta)
.and_then(|x| x.checked_sub(1))
.ok_or(Error::Corrupt)
} else {
req_insert_count.checked_add(delta).ok_or(Error::Corrupt)
}
}
fn lookup_indexed(
&self,
idx: usize,
t_static: bool,
base: usize,
req_insert_count: usize,
) -> Result<(Vec<u8>, Vec<u8>), Error> {
if t_static {
let (n, v) = static_table::get(idx).ok_or(Error::Corrupt)?;
Ok((n.to_vec(), v.to_vec()))
} else {
let abs = DynamicTable::field_relative_to_absolute(base, idx).ok_or(Error::Corrupt)?;
self.lookup_dynamic_abs(abs, req_insert_count)
}
}
fn lookup_name_ref(
&self,
idx: usize,
t_static: bool,
base: usize,
req_insert_count: usize,
) -> Result<Vec<u8>, Error> {
if t_static {
let (n, _) = static_table::get(idx).ok_or(Error::Corrupt)?;
Ok(n.to_vec())
} else {
let abs = DynamicTable::field_relative_to_absolute(base, idx).ok_or(Error::Corrupt)?;
Ok(self.lookup_dynamic_abs(abs, req_insert_count)?.0)
}
}
fn lookup_dynamic_abs(
&self,
abs: usize,
req_insert_count: usize,
) -> Result<(Vec<u8>, Vec<u8>), Error> {
if abs >= req_insert_count {
return Err(Error::Corrupt);
}
let (n, v) = self.table.get_absolute(abs).ok_or(Error::Corrupt)?;
Ok((n.to_vec(), v.to_vec()))
}
pub fn insert_count(&self) -> usize {
self.table.insert_count()
}
#[cfg(test)]
pub(crate) fn table_size(&self) -> usize {
self.table.size()
}
#[cfg(test)]
pub(crate) fn table_capacity(&self) -> usize {
self.table.capacity()
}
#[cfg(test)]
pub(crate) fn table_len(&self) -> usize {
self.table.len()
}
}
fn read_string(block: &[u8], pos: usize, n: u32) -> Result<(Vec<u8>, usize), Error> {
let first = *block.get(pos).ok_or(Error::UnexpectedEnd)?;
let huff = first & (1u8 << n) != 0;
let (len, p) = decode_int(block, pos, n)?;
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;