#[macro_use] extern crate log;
#[cfg(feature="interop_tests")]
extern crate rustc_serialize;
use std::fmt;
use std::iter;
use std::slice;
use std::collections::VecDeque;
use std::collections::vec_deque;
pub use self::decoder::Decoder;
pub use self::encoder::Encoder;
pub mod encoder;
pub mod decoder;
pub mod huffman;
struct DynamicTableIter<'a> {
inner: vec_deque::Iter<'a, (Vec<u8>, Vec<u8>)>,
}
impl<'a> Iterator for DynamicTableIter<'a> {
type Item = (&'a [u8], &'a [u8]);
fn next(&mut self) -> Option<(&'a [u8], &'a [u8])> {
match self.inner.next() {
Some(ref header) => Some((&header.0, &header.1)),
None => None,
}
}
}
struct DynamicTable {
table: VecDeque<(Vec<u8>, Vec<u8>)>,
size: usize,
max_size: usize,
}
impl DynamicTable {
fn new() -> DynamicTable {
DynamicTable::with_size(4096)
}
fn with_size(max_size: usize) -> DynamicTable {
DynamicTable {
table: VecDeque::new(),
size: 0,
max_size: max_size,
}
}
fn get_size(&self) -> usize {
self.size
}
fn iter(&self) -> DynamicTableIter {
DynamicTableIter {
inner: self.table.iter(),
}
}
fn set_max_table_size(&mut self, new_max_size: usize) {
self.max_size = new_max_size;
self.consolidate_table();
}
fn get_max_table_size(&self) -> usize {
self.max_size
}
fn add_header(&mut self, name: Vec<u8>, value: Vec<u8>) {
self.size += name.len() + value.len() + 32;
debug!("New dynamic table size {}", self.size);
self.table.push_front((name, value));
self.consolidate_table();
debug!("After consolidation dynamic table size {}", self.size);
}
fn consolidate_table(&mut self) {
while self.size > self.max_size {
{
let last_header = match self.table.back() {
Some(x) => x,
None => {
panic!("Size of table != 0, but no headers left!");
}
};
self.size -= last_header.0.len() + last_header.1.len() + 32;
}
self.table.pop_back();
}
}
fn len(&self) -> usize {
self.table.len()
}
fn to_vec(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut ret: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
for elem in self.table.iter() {
ret.push(elem.clone());
}
ret
}
fn get(&self, index: usize) -> Option<&(Vec<u8>, Vec<u8>)> {
self.table.get(index)
}
}
impl fmt::Debug for DynamicTable {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{:?}", self.table)
}
}
type StaticTable<'a> = &'a [(&'a [u8], &'a [u8])];
struct HeaderTableIter<'a> {
inner: iter::Chain<
iter::Map<
slice::Iter<'a, (&'a [u8], &'a [u8])>,
fn((&'a (&'a [u8], &'a [u8]))) -> (&'a [u8], &'a [u8])>,
DynamicTableIter<'a>>,
}
impl<'a> Iterator for HeaderTableIter<'a> {
type Item = (&'a [u8], &'a [u8]);
fn next(&mut self) -> Option<(&'a [u8], &'a [u8])> {
self.inner.next()
}
}
fn static_table_mapper<'a>(h: &'a (&'a [u8], &'a [u8]))
-> (&'a [u8], &'a [u8]) {
*h
}
struct HeaderTable<'a> {
static_table: StaticTable<'a>,
dynamic_table: DynamicTable,
}
impl<'a> HeaderTable<'a> {
pub fn with_static_table(static_table: StaticTable<'a>) -> HeaderTable<'a> {
HeaderTable {
static_table: static_table,
dynamic_table: DynamicTable::new(),
}
}
pub fn iter(&'a self) -> HeaderTableIter<'a> {
HeaderTableIter {
inner: self.static_table.iter()
.map(static_table_mapper as
fn((&'a (&'a [u8], &'a [u8]))) -> (&'a [u8], &'a [u8]))
.chain(self.dynamic_table.iter()),
}
}
#[inline]
pub fn add_header(&mut self, name: Vec<u8>, value: Vec<u8>) {
self.dynamic_table.add_header(name, value);
}
pub fn get_from_table(&self, index: usize)
-> Option<(&[u8], &[u8])> {
let real_index = if index > 0 {
index - 1
} else {
return None
};
if real_index < self.static_table.len() {
Some(self.static_table[real_index])
} else {
let dynamic_index = real_index - self.static_table.len();
if dynamic_index < self.dynamic_table.len() {
match self.dynamic_table.get(dynamic_index) {
Some(&(ref name, ref value)) => {
Some((name, value))
},
None => None
}
} else {
None
}
}
}
pub fn find_header(&self, header: (&[u8], &[u8])) -> Option<(usize, bool)> {
let mut matching_name: Option<usize> = None;
for (i, h) in self.iter().enumerate() {
if header.0 == h.0 {
if header.1 == h.1 {
return Some((i + 1, true));
}
matching_name = Some(i + 1);
}
}
match matching_name {
Some(i) => Some((i, false)),
None => None,
}
}
}
static STATIC_TABLE: &'static [(&'static [u8], &'static [u8])] = &[
(b":authority", b""),
(b":method", b"GET"),
(b":method", b"POST"),
(b":path", b"/"),
(b":path", b"/index.html"),
(b":scheme", b"http"),
(b":scheme", b"https"),
(b":status", b"200"),
(b":status", b"204"),
(b":status", b"206"),
(b":status", b"304"),
(b":status", b"400"),
(b":status", b"404"),
(b":status", b"500"),
(b"accept-", b""),
(b"accept-encoding", b"gzip, deflate"),
(b"accept-language", b""),
(b"accept-ranges", b""),
(b"accept", b""),
(b"access-control-allow-origin", b""),
(b"age", b""),
(b"allow", b""),
(b"authorization", b""),
(b"cache-control", b""),
(b"content-disposition", b""),
(b"content-encoding", b""),
(b"content-language", b""),
(b"content-length", b""),
(b"content-location", b""),
(b"content-range", b""),
(b"content-type", b""),
(b"cookie", b""),
(b"date", b""),
(b"etag", b""),
(b"expect", b""),
(b"expires", b""),
(b"from", b""),
(b"host", b""),
(b"if-match", b""),
(b"if-modified-since", b""),
(b"if-none-match", b""),
(b"if-range", b""),
(b"if-unmodified-since", b""),
(b"last-modified", b""),
(b"link", b""),
(b"location", b""),
(b"max-forwards", b""),
(b"proxy-authenticate", b""),
(b"proxy-authorization", b""),
(b"range", b""),
(b"referer", b""),
(b"refresh", b""),
(b"retry-after", b""),
(b"server", b""),
(b"set-cookie", b""),
(b"strict-transport-security", b""),
(b"transfer-encoding", b""),
(b"user-agent", b""),
(b"vary", b""),
(b"via", b""),
(b"www-authenticate", b""),
];
#[cfg(test)]
mod tests {
use super::DynamicTable;
use super::HeaderTable;
use super::STATIC_TABLE;
#[test]
fn test_dynamic_table_size_calculation_simple() {
let mut table = DynamicTable::new();
assert_eq!(0, table.get_size());
table.add_header(b"a".to_vec(), b"b".to_vec());
assert_eq!(32 + 2, table.get_size());
}
#[test]
fn test_dynamic_table_size_calculation() {
let mut table = DynamicTable::new();
table.add_header(b"a".to_vec(), b"b".to_vec());
table.add_header(b"123".to_vec(), b"456".to_vec());
table.add_header(b"a".to_vec(), b"b".to_vec());
assert_eq!(3 * 32 + 2 + 6 + 2, table.get_size());
}
#[test]
fn test_dynamic_table_auto_resize() {
let mut table = DynamicTable::with_size(38);
table.add_header(b"a".to_vec(), b"b".to_vec());
assert_eq!(32 + 2, table.get_size());
table.add_header(b"123".to_vec(), b"456".to_vec());
assert_eq!(32 + 6, table.get_size());
assert_eq!(table.to_vec(), vec![
(b"123".to_vec(), b"456".to_vec())]);
}
#[test]
fn test_dynamic_table_auto_resize_into_empty() {
let mut table = DynamicTable::with_size(38);
table.add_header(b"a".to_vec(), b"b".to_vec());
assert_eq!(32 + 2, table.get_size());
table.add_header(b"123".to_vec(), b"4567".to_vec());
assert_eq!(0, table.get_size());
assert_eq!(0, table.to_vec().len());
}
#[test]
fn test_dynamic_table_change_max_size() {
let mut table = DynamicTable::new();
table.add_header(b"a".to_vec(), b"b".to_vec());
table.add_header(b"123".to_vec(), b"456".to_vec());
table.add_header(b"c".to_vec(), b"d".to_vec());
assert_eq!(3 * 32 + 2 + 6 + 2, table.get_size());
table.set_max_table_size(38);
assert_eq!(32 + 2, table.get_size());
assert_eq!(table.to_vec(), vec![
(b"c".to_vec(), b"d".to_vec())]);
}
#[test]
fn test_dynamic_table_clear() {
let mut table = DynamicTable::new();
table.add_header(b"a".to_vec(), b"b".to_vec());
table.add_header(b"123".to_vec(), b"456".to_vec());
table.add_header(b"c".to_vec(), b"d".to_vec());
assert_eq!(3 * 32 + 2 + 6 + 2, table.get_size());
table.set_max_table_size(0);
assert_eq!(0, table.len());
assert_eq!(0, table.to_vec().len());
assert_eq!(0, table.get_size());
assert_eq!(0, table.get_max_table_size());
}
#[test]
fn test_dynamic_table_max_size_zero() {
let mut table = DynamicTable::with_size(0);
table.add_header(b"a".to_vec(), b"b".to_vec());
assert_eq!(0, table.len());
assert_eq!(0, table.to_vec().len());
assert_eq!(0, table.get_size());
assert_eq!(0, table.get_max_table_size());
}
#[test]
fn test_dynamic_table_iter_with_elems() {
let mut table = DynamicTable::new();
table.add_header(b"a".to_vec(), b"b".to_vec());
table.add_header(b"123".to_vec(), b"456".to_vec());
table.add_header(b"c".to_vec(), b"d".to_vec());
let iter_res: Vec<(&[u8], &[u8])> = table.iter().collect();
let expected: Vec<(&[u8], &[u8])> = vec![
(b"c", b"d"),
(b"123", b"456"),
(b"a", b"b"),
];
assert_eq!(iter_res, expected);
}
#[test]
fn test_dynamic_table_iter_no_elems() {
let table = DynamicTable::new();
let iter_res: Vec<(&[u8], &[u8])> = table.iter().collect();
let expected = vec![];
assert_eq!(iter_res, expected);
}
#[test]
fn test_header_table_index_static() {
let table = HeaderTable::with_static_table(STATIC_TABLE);
for (index, entry) in STATIC_TABLE.iter().enumerate() {
assert_eq!(table.get_from_table(index + 1).unwrap(), *entry);
}
}
#[test]
fn test_header_table_index_out_of_bounds() {
let table = HeaderTable::with_static_table(STATIC_TABLE);
assert!(table.get_from_table(0).is_none());
assert!(table.get_from_table(STATIC_TABLE.len() + 1).is_none());
}
#[test]
fn test_header_table_add_to_dynamic() {
let mut table = HeaderTable::with_static_table(STATIC_TABLE);
let header = (b"a".to_vec(), b"b".to_vec());
table.add_header(header.0.clone(), header.1.clone());
assert_eq!(table.dynamic_table.to_vec(), vec![header]);
}
#[test]
fn test_header_table_index_dynamic() {
let mut table = HeaderTable::with_static_table(STATIC_TABLE);
let header = (b"a".to_vec(), b"b".to_vec());
table.add_header(header.0.clone(), header.1.clone());
assert_eq!(table.get_from_table(STATIC_TABLE.len() + 1).unwrap(),
((&header.0[..], &header.1[..])));
}
#[test]
fn test_header_table_iter() {
let mut table = HeaderTable::with_static_table(STATIC_TABLE);
let headers: [(&[u8], &[u8]); 2] = [
(b"a", b"b"),
(b"c", b"d"),
];
for header in headers.iter() {
table.add_header(header.0.to_vec(), header.1.to_vec());
}
let iterated: Vec<(&[u8], &[u8])> = table.iter().collect();
assert_eq!(iterated.len(), headers.len() + STATIC_TABLE.len());
for (h1, h2) in iterated.iter().zip(STATIC_TABLE.iter()) {
assert_eq!(h1, h2);
}
for (h1, h2) in iterated.iter().skip(STATIC_TABLE.len())
.zip(headers.iter().rev()) {
assert_eq!(h1, h2);
}
}
#[test]
fn test_find_header_static_full() {
let table = HeaderTable::with_static_table(STATIC_TABLE);
for (i, h) in STATIC_TABLE.iter().enumerate() {
assert_eq!(table.find_header(*h).unwrap(),
(i + 1, true));
}
}
#[test]
fn test_find_header_static_partial() {
{
let table = HeaderTable::with_static_table(STATIC_TABLE);
let h: (&[u8], &[u8]) = (b":method", b"PUT");
if let (index, false) = table.find_header(h).unwrap() {
assert_eq!(h.0, STATIC_TABLE[index - 1].0);
assert_eq!(3, index);
} else {
panic!("The header should have matched only partially");
}
}
{
let table = HeaderTable::with_static_table(STATIC_TABLE);
let h: (&[u8], &[u8]) = (b":status", b"333");
if let (index, false) = table.find_header(h).unwrap() {
assert_eq!(h.0, STATIC_TABLE[index - 1].0);
assert_eq!(14, index);
} else {
panic!("The header should have matched only partially");
}
}
{
let table = HeaderTable::with_static_table(STATIC_TABLE);
let h: (&[u8], &[u8]) = (b":authority", b"example.com");
if let (index, false) = table.find_header(h).unwrap() {
assert_eq!(h.0, STATIC_TABLE[index - 1].0);
} else {
panic!("The header should have matched only partially");
}
}
{
let table = HeaderTable::with_static_table(STATIC_TABLE);
let h: (&[u8], &[u8]) = (b"www-authenticate", b"asdf");
if let (index, false) = table.find_header(h).unwrap() {
assert_eq!(h.0, STATIC_TABLE[index - 1].0);
} else {
panic!("The header should have matched only partially");
}
}
}
#[test]
fn test_find_header_dynamic_full() {
let mut table = HeaderTable::with_static_table(STATIC_TABLE);
let h: (&[u8], &[u8]) = (b":method", b"PUT");
table.add_header(h.0.to_vec(), h.1.to_vec());
if let (index, true) = table.find_header(h).unwrap() {
assert_eq!(index, STATIC_TABLE.len() + 1);
} else {
panic!("The header should have matched fully");
}
}
#[test]
fn test_find_header_dynamic_partial() {
let mut table = HeaderTable::with_static_table(STATIC_TABLE);
{
let h = (b"X-Custom-Header", b"stuff");
table.add_header(h.0.to_vec(), h.1.to_vec());
}
let h: (&[u8], &[u8]) = (b"X-Custom-Header", b"different-stuff");
if let (index, false) = table.find_header(h).unwrap() {
assert_eq!(index, STATIC_TABLE.len() + 1);
} else {
panic!("The header should have matched only partially");
}
}
}