diff options
Diffstat (limited to 'third_party/rust/neqo-qpack/src/header_block.rs')
-rw-r--r-- | third_party/rust/neqo-qpack/src/header_block.rs | 933 |
1 files changed, 933 insertions, 0 deletions
diff --git a/third_party/rust/neqo-qpack/src/header_block.rs b/third_party/rust/neqo-qpack/src/header_block.rs new file mode 100644 index 0000000000..38f8738df9 --- /dev/null +++ b/third_party/rust/neqo-qpack/src/header_block.rs @@ -0,0 +1,933 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::prefix::{ + BASE_PREFIX_NEGATIVE, BASE_PREFIX_POSITIVE, HEADER_FIELD_INDEX_DYNAMIC, + HEADER_FIELD_INDEX_DYNAMIC_POST, HEADER_FIELD_INDEX_STATIC, HEADER_FIELD_LITERAL_NAME_LITERAL, + HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC, HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST, + HEADER_FIELD_LITERAL_NAME_REF_STATIC, NO_PREFIX, +}; +use crate::qpack_send_buf::QpackData; +use crate::reader::{to_string, ReceiverBufferWrapper}; +use crate::table::HeaderTable; +use crate::{Error, Res}; +use neqo_common::{qtrace, Header}; +use std::mem; +use std::ops::{Deref, Div}; + +#[derive(Default, Debug, PartialEq)] +pub struct HeaderEncoder { + buf: QpackData, + base: u64, + use_huffman: bool, + max_entries: u64, + max_dynamic_index_ref: Option<u64>, +} + +impl ::std::fmt::Display for HeaderEncoder { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "HeaderEncoder") + } +} + +impl HeaderEncoder { + pub fn new(base: u64, use_huffman: bool, max_entries: u64) -> Self { + Self { + buf: QpackData::default(), + base, + use_huffman, + max_entries, + max_dynamic_index_ref: None, + } + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn read(&mut self, r: usize) { + self.buf.read(r); + } + + pub fn encode_indexed_static(&mut self, index: u64) { + qtrace!([self], "encode static index {}.", index); + self.buf + .encode_prefixed_encoded_int(HEADER_FIELD_INDEX_STATIC, index); + } + + fn new_ref(&mut self, index: u64) { + if let Some(r) = self.max_dynamic_index_ref { + if r < index { + self.max_dynamic_index_ref = Some(index); + } + } else { + self.max_dynamic_index_ref = Some(index); + } + } + + pub fn encode_indexed_dynamic(&mut self, index: u64) { + qtrace!([self], "encode dynamic index {}.", index); + if index < self.base { + self.buf + .encode_prefixed_encoded_int(HEADER_FIELD_INDEX_DYNAMIC, self.base - index - 1); + } else { + self.buf + .encode_prefixed_encoded_int(HEADER_FIELD_INDEX_DYNAMIC_POST, index - self.base); + } + self.new_ref(index); + } + + pub fn encode_literal_with_name_ref(&mut self, is_static: bool, index: u64, value: &[u8]) { + qtrace!( + [self], + "encode literal with name ref - index={}, static={}, value={:x?}", + index, + is_static, + value + ); + if is_static { + self.buf + .encode_prefixed_encoded_int(HEADER_FIELD_LITERAL_NAME_REF_STATIC, index); + } else if index < self.base { + self.buf.encode_prefixed_encoded_int( + HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC, + self.base - index - 1, + ); + self.new_ref(index); + } else { + self.buf.encode_prefixed_encoded_int( + HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST, + index - self.base, + ); + self.new_ref(index); + } + + self.buf.encode_literal(self.use_huffman, NO_PREFIX, value); + } + + pub fn encode_literal_with_name_literal(&mut self, name: &[u8], value: &[u8]) { + qtrace!( + [self], + "encode literal with name literal - name={:x?}, value={:x?}.", + name, + value + ); + self.buf + .encode_literal(self.use_huffman, HEADER_FIELD_LITERAL_NAME_LITERAL, name); + self.buf.encode_literal(self.use_huffman, NO_PREFIX, value); + } + + pub fn encode_header_block_prefix(&mut self) { + let tmp = mem::take(&mut self.buf); + let (enc_insert_cnt, delta, prefix) = + self.max_dynamic_index_ref + .map_or((0, self.base, BASE_PREFIX_POSITIVE), |r| { + let req_insert_cnt = r + 1; + if req_insert_cnt <= self.base { + ( + req_insert_cnt % (2 * self.max_entries) + 1, + self.base - req_insert_cnt, + BASE_PREFIX_POSITIVE, + ) + } else { + ( + req_insert_cnt % (2 * self.max_entries) + 1, + req_insert_cnt - self.base - 1, + BASE_PREFIX_NEGATIVE, + ) + } + }); + qtrace!( + [self], + "encode header block prefix max_dynamic_index_ref={:?}, base={}, enc_insert_cnt={}, delta={}, prefix={:?}.", + self.max_dynamic_index_ref, + self.base, + enc_insert_cnt, + delta, + prefix + ); + + self.buf + .encode_prefixed_encoded_int(NO_PREFIX, enc_insert_cnt); + self.buf.encode_prefixed_encoded_int(prefix, delta); + + self.buf.write_bytes(&tmp); + } +} + +impl Deref for HeaderEncoder { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + &self.buf + } +} + +pub(crate) struct HeaderDecoder<'a> { + buf: ReceiverBufferWrapper<'a>, + base: u64, + req_insert_cnt: u64, +} + +impl<'a> ::std::fmt::Display for HeaderDecoder<'a> { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "HeaderDecoder") + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum HeaderDecoderResult { + Blocked(u64), + Headers(Vec<Header>), +} + +impl<'a> HeaderDecoder<'a> { + pub fn new(buf: &'a [u8]) -> Self { + Self { + buf: ReceiverBufferWrapper::new(buf), + base: 0, + req_insert_cnt: 0, + } + } + + pub fn refers_dynamic_table( + &mut self, + max_entries: u64, + total_num_of_inserts: u64, + ) -> Res<bool> { + Error::map_error( + self.read_base(max_entries, total_num_of_inserts), + Error::DecompressionFailed, + )?; + Ok(self.req_insert_cnt != 0) + } + + pub fn decode_header_block( + &mut self, + table: &HeaderTable, + max_entries: u64, + total_num_of_inserts: u64, + ) -> Res<HeaderDecoderResult> { + Error::map_error( + self.read_base(max_entries, total_num_of_inserts), + Error::DecompressionFailed, + )?; + + if table.base() < self.req_insert_cnt { + qtrace!( + [self], + "decoding is blocked, requested inserts count={}", + self.req_insert_cnt + ); + return Ok(HeaderDecoderResult::Blocked(self.req_insert_cnt)); + } + let mut h: Vec<Header> = Vec::new(); + + while !self.buf.done() { + let b = Error::map_error(self.buf.peek(), Error::DecompressionFailed)?; + if HEADER_FIELD_INDEX_STATIC.cmp_prefix(b) { + h.push(Error::map_error( + self.read_indexed_static(), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_INDEX_DYNAMIC.cmp_prefix(b) { + h.push(Error::map_error( + self.read_indexed_dynamic(table), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_INDEX_DYNAMIC_POST.cmp_prefix(b) { + h.push(Error::map_error( + self.read_indexed_dynamic_post(table), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_LITERAL_NAME_REF_STATIC.cmp_prefix(b) { + h.push(Error::map_error( + self.read_literal_with_name_ref_static(), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC.cmp_prefix(b) { + h.push(Error::map_error( + self.read_literal_with_name_ref_dynamic(table), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_LITERAL_NAME_LITERAL.cmp_prefix(b) { + h.push(Error::map_error( + self.read_literal_with_name_literal(), + Error::DecompressionFailed, + )?); + } else if HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST.cmp_prefix(b) { + h.push(Error::map_error( + self.read_literal_with_name_ref_dynamic_post(table), + Error::DecompressionFailed, + )?); + } else { + unreachable!("All prefixes are covered"); + } + } + + qtrace!([self], "done decoding header block."); + Ok(HeaderDecoderResult::Headers(h)) + } + + pub fn get_req_insert_cnt(&self) -> u64 { + self.req_insert_cnt + } + + fn read_base(&mut self, max_entries: u64, total_num_of_inserts: u64) -> Res<()> { + let insert_cnt = self.buf.read_prefixed_int(0)?; + self.req_insert_cnt = + HeaderDecoder::calc_req_insert_cnt(insert_cnt, max_entries, total_num_of_inserts)?; + + let s = self.buf.peek()? & 0x80 != 0; + let base_delta = self.buf.read_prefixed_int(1)?; + self.base = if s { + if self.req_insert_cnt <= base_delta { + return Err(Error::DecompressionFailed); + } + self.req_insert_cnt - base_delta - 1 + } else { + self.req_insert_cnt + .checked_add(base_delta) + .ok_or(Error::DecompressionFailed)? + }; + qtrace!( + [self], + "requested inserts count is {} and base is {}", + self.req_insert_cnt, + self.base + ); + Ok(()) + } + + fn calc_req_insert_cnt(encoded: u64, max_entries: u64, total_num_of_inserts: u64) -> Res<u64> { + if encoded == 0 { + Ok(0) + } else if max_entries == 0 { + Err(Error::DecompressionFailed) + } else { + let full_range = 2 * max_entries; + if encoded > full_range { + return Err(Error::DecompressionFailed); + } + let max_value = total_num_of_inserts + max_entries; + let max_wrapped = max_value.div(full_range) * full_range; + let mut req_insert_cnt = max_wrapped + encoded - 1; + if req_insert_cnt > max_value { + if req_insert_cnt < full_range { + return Err(Error::DecompressionFailed); + } + req_insert_cnt -= full_range; + } + Ok(req_insert_cnt) + } + } + + fn read_indexed_static(&mut self) -> Res<Header> { + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_INDEX_STATIC.len())?; + qtrace!([self], "decoder static indexed {}.", index); + let entry = HeaderTable::get_static(index)?; + Ok(Header::new( + to_string(entry.name())?, + to_string(entry.value())?, + )) + } + + fn read_indexed_dynamic(&mut self, table: &HeaderTable) -> Res<Header> { + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_INDEX_DYNAMIC.len())?; + qtrace!([self], "decoder dynamic indexed {}.", index); + let entry = table.get_dynamic(index, self.base, false)?; + Ok(Header::new( + to_string(entry.name())?, + to_string(entry.value())?, + )) + } + + fn read_indexed_dynamic_post(&mut self, table: &HeaderTable) -> Res<Header> { + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_INDEX_DYNAMIC_POST.len())?; + qtrace!([self], "decode post-based {}.", index); + let entry = table.get_dynamic(index, self.base, true)?; + Ok(Header::new( + to_string(entry.name())?, + to_string(entry.value())?, + )) + } + + fn read_literal_with_name_ref_static(&mut self) -> Res<Header> { + qtrace!( + [self], + "read literal with name reference to the static table." + ); + + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_LITERAL_NAME_REF_STATIC.len())?; + + Ok(Header::new( + to_string(HeaderTable::get_static(index)?.name())?, + self.buf.read_literal_from_buffer(0)?, + )) + } + + fn read_literal_with_name_ref_dynamic(&mut self, table: &HeaderTable) -> Res<Header> { + qtrace!( + [self], + "read literal with name reference ot the dynamic table." + ); + + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC.len())?; + + Ok(Header::new( + to_string(table.get_dynamic(index, self.base, false)?.name())?, + self.buf.read_literal_from_buffer(0)?, + )) + } + + fn read_literal_with_name_ref_dynamic_post(&mut self, table: &HeaderTable) -> Res<Header> { + qtrace!([self], "decoder literal with post-based index."); + + let index = self + .buf + .read_prefixed_int(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST.len())?; + + Ok(Header::new( + to_string(table.get_dynamic(index, self.base, true)?.name())?, + self.buf.read_literal_from_buffer(0)?, + )) + } + + fn read_literal_with_name_literal(&mut self) -> Res<Header> { + qtrace!([self], "decode literal with name literal."); + + let name = self + .buf + .read_literal_from_buffer(HEADER_FIELD_LITERAL_NAME_LITERAL.len())?; + + Ok(Header::new(name, self.buf.read_literal_from_buffer(0)?)) + } +} + +#[cfg(test)] +mod tests { + + use super::{HeaderDecoder, HeaderDecoderResult, HeaderEncoder, HeaderTable}; + use crate::Error; + + const INDEX_STATIC_TEST: &[(u64, &[u8], &str, &str)] = &[ + (0, &[0x0, 0x0, 0xc0], ":authority", ""), + (10, &[0x0, 0x0, 0xca], "last-modified", ""), + (15, &[0x0, 0x0, 0xcf], ":method", "CONNECT"), + (65, &[0x0, 0x0, 0xff, 0x02], ":status", "206"), + ]; + + const INDEX_DYNAMIC_TEST: &[(u64, &[u8], &str, &str)] = &[ + (0, &[0x02, 0x41, 0xbf, 0x2], "header0", "0"), + (10, &[0x0c, 0x37, 0xb7], "header10", "10"), + (15, &[0x11, 0x32, 0xb2], "header15", "15"), + (65, &[0x43, 0x0, 0x80], "header65", "65"), + ]; + + const INDEX_DYNAMIC_POST_TEST: &[(u64, &[u8], &str, &str)] = &[ + (0, &[0x02, 0x80, 0x10], "header0", "0"), + (10, &[0x0c, 0x8a, 0x1a], "header10", "10"), + (15, &[0x11, 0x8f, 0x1f, 0x00], "header15", "15"), + (65, &[0x43, 0xc1, 0x1f, 0x32], "header65", "65"), + ]; + + const NAME_REF_STATIC: &[(u64, &[u8], &str, &str)] = &[ + ( + 0, + &[ + 0x00, 0x00, 0x50, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ], + ":authority", + "custom-key", + ), + ( + 10, + &[ + 0x00, 0x00, 0x5a, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ], + "last-modified", + "custom-key", + ), + ( + 15, + &[ + 0x00, 0x00, 0x5f, 0x00, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + ":method", + "custom-key", + ), + ( + 65, + &[ + 0x00, 0x00, 0x5f, 0x32, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + ":status", + "custom-key", + ), + ]; + + const NAME_REF_DYNAMIC: &[(u64, &[u8], &str, &str)] = &[ + ( + 0, + &[ + 0x02, 0x41, 0x4f, 0x32, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header0", + "custom-key", + ), + ( + 10, + &[ + 0x0c, 0x37, 0x4f, 0x28, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header10", + "custom-key", + ), + ( + 15, + &[ + 0x11, 0x32, 0x4f, 0x23, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header15", + "custom-key", + ), + ( + 65, + &[ + 0x43, 0x00, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ], + "header65", + "custom-key", + ), + ]; + + const NAME_REF_DYNAMIC_POST: &[(u64, &[u8], &str, &str)] = &[ + ( + 0, + &[ + 0x02, 0x80, 0x00, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ], + "header0", + "custom-key", + ), + ( + 10, + &[ + 0x0c, 0x8a, 0x07, 0x03, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header10", + "custom-key", + ), + ( + 15, + &[ + 0x11, 0x8f, 0x07, 0x08, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header15", + "custom-key", + ), + ( + 65, + &[ + 0x43, 0xc1, 0x07, 0x3a, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header65", + "custom-key", + ), + ]; + + const NAME_REF_DYNAMIC_HUFFMAN: &[(u64, &[u8], &str, &str)] = &[ + ( + 0, + &[ + 0x02, 0x41, 0x4f, 0x32, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ], + "header0", + "custom-key", + ), + ( + 10, + &[ + 0x0c, 0x37, 0x4f, 0x28, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ], + "header10", + "custom-key", + ), + ( + 15, + &[ + 0x11, 0x32, 0x4f, 0x23, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ], + "header15", + "custom-key", + ), + ( + 65, + &[ + 0x43, 0x00, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ], + "header65", + "custom-key", + ), + ]; + + const VALUE: &[u8] = &[0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79]; + + const LITERAL_LITERAL: &[u8] = &[ + 0x0, 0x42, 0x27, 0x03, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0a, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ]; + const LITERAL_LITERAL_HUFFMAN: &[u8] = &[ + 0x0, 0x42, 0x2f, 0x01, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x88, 0x25, 0xa8, + 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ]; + + const LITERAL_VALUE: &str = "custom-key"; + + #[test] + fn test_encode_indexed_static() { + for (index, result, _, _) in INDEX_STATIC_TEST { + let mut encoded_h = HeaderEncoder::new(0, true, 1000); + encoded_h.encode_indexed_static(*index); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_indexed_dynamic() { + for (index, result, _, _) in INDEX_DYNAMIC_TEST { + let mut encoded_h = HeaderEncoder::new(66, true, 1000); + encoded_h.encode_indexed_dynamic(*index); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_indexed_dynamic_post() { + for (index, result, _, _) in INDEX_DYNAMIC_POST_TEST { + let mut encoded_h = HeaderEncoder::new(0, true, 1000); + encoded_h.encode_indexed_dynamic(*index); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_literal_with_name_ref_static() { + for (index, result, _, _) in NAME_REF_STATIC { + let mut encoded_h = HeaderEncoder::new(0, false, 1000); + encoded_h.encode_literal_with_name_ref(true, *index, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_literal_with_name_ref_dynamic() { + for (index, result, _, _) in NAME_REF_DYNAMIC { + let mut encoded_h = HeaderEncoder::new(66, false, 1000); + encoded_h.encode_literal_with_name_ref(false, *index, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_literal_with_name_ref_dynamic_post() { + for (index, result, _, _) in NAME_REF_DYNAMIC_POST { + let mut encoded_h = HeaderEncoder::new(0, false, 1000); + encoded_h.encode_literal_with_name_ref(false, *index, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + + #[test] + fn test_encode_literal_with_name_ref_dynamic_huffman() { + for (index, result, _, _) in NAME_REF_DYNAMIC_HUFFMAN { + let mut encoded_h = HeaderEncoder::new(66, true, 1000); + encoded_h.encode_literal_with_name_ref(false, *index, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&&*encoded_h, result); + } + } + #[test] + fn test_encode_literal_with_literal() { + let mut encoded_h = HeaderEncoder::new(66, false, 1000); + encoded_h.encode_literal_with_name_literal(VALUE, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&*encoded_h, LITERAL_LITERAL); + + let mut encoded_h = HeaderEncoder::new(66, true, 1000); + encoded_h.encode_literal_with_name_literal(VALUE, VALUE); + encoded_h.encode_header_block_prefix(); + assert_eq!(&*encoded_h, LITERAL_LITERAL_HUFFMAN); + } + + #[test] + fn decode_indexed_static() { + for (_, encoded, decoded1, decoded2) in INDEX_STATIC_TEST { + let table = HeaderTable::new(false); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + fn fill_table(table: &mut HeaderTable) { + table.set_capacity(10000).unwrap(); + for i in 0..66 { + let mut v = b"header".to_vec(); + let mut num = i.to_string().as_bytes().to_vec(); + v.append(&mut num); + table.insert(&v[..], i.to_string().as_bytes()).unwrap(); + } + } + + #[test] + fn decode_indexed_dynamic() { + for (_, encoded, decoded1, decoded2) in INDEX_DYNAMIC_TEST { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_indexed_dynamic_post() { + for (_, encoded, decoded1, decoded2) in INDEX_DYNAMIC_POST_TEST { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_literal_with_name_ref_static() { + for (_, encoded, decoded1, decoded2) in NAME_REF_STATIC { + let table = HeaderTable::new(false); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_literal_with_name_ref_dynamic() { + for (_, encoded, decoded1, decoded2) in NAME_REF_DYNAMIC { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_literal_with_name_ref_dynamic_post() { + for (_, encoded, decoded1, decoded2) in NAME_REF_DYNAMIC_POST { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_literal_with_name_ref_dynamic_huffman() { + for (_, encoded, decoded1, decoded2) in NAME_REF_DYNAMIC_HUFFMAN { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + #[test] + fn decode_literal_literal() { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(LITERAL_LITERAL); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), LITERAL_VALUE); + assert_eq!(result[0].value(), LITERAL_VALUE); + } else { + panic!("No headers"); + } + + let mut decoder_h = HeaderDecoder::new(LITERAL_LITERAL_HUFFMAN); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), LITERAL_VALUE); + assert_eq!(result[0].value(), LITERAL_VALUE); + } else { + panic!("No headers"); + } + } + + // Test that we are ignoring N-bit. + #[test] + fn decode_ignore_n_bit() { + const TEST_N_BIT: &[(&[u8], &str, &str)] = &[ + ( + &[ + 0x02, 0x41, 0x6f, 0x32, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, + 0x65, 0x79, + ], + "header0", + "custom-key", + ), + ( + &[ + 0x02, 0x80, 0x08, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, + ], + "header0", + "custom-key", + ), + ( + &[ + 0x0, 0x42, 0x37, 0x03, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, + 0x79, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ], + "custom-key", + "custom-key", + ), + ( + &[ + 0x0, 0x42, 0x3f, 0x01, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x88, + 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, + ], + "custom-key", + "custom-key", + ), + ]; + + for (encoded, decoded1, decoded2) in TEST_N_BIT { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(encoded); + if let HeaderDecoderResult::Headers(result) = + decoder_h.decode_header_block(&table, 1000, 0).unwrap() + { + assert_eq!(result.len(), 1); + assert_eq!(result[0].name(), *decoded1); + assert_eq!(result[0].value(), *decoded2); + } else { + panic!("No headers"); + } + } + } + + /// If the base calculation goes negative, that is an error. + #[test] + fn negative_base() { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + let mut decoder_h = HeaderDecoder::new(&[0x0, 0x87, 0x01, 0x02, 0x03]); + assert_eq!( + Error::DecompressionFailed, + decoder_h.decode_header_block(&table, 1000, 0).unwrap_err() + ); + } + + /// If the base calculation overflows the largest value we support (`u64::MAX`), + /// then that is an error. + #[test] + fn overflow_base() { + let mut table = HeaderTable::new(false); + fill_table(&mut table); + // A small required insert count is necessary, but we can set the + // base delta to u64::MAX. + let mut decoder_h = HeaderDecoder::new(&[ + 0xff, 0x01, 0x7f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, + 0x03, + ]); + assert_eq!( + Error::DecompressionFailed, + decoder_h.decode_header_block(&table, 1000, 0).unwrap_err() + ); + } +} |