use std::num::Wrapping;
use std::borrow::Cow;
use super::huffman::HuffmanDecoder;
use super::huffman::HuffmanDecoderError;
use super::STATIC_TABLE;
use super::{StaticTable, HeaderTable};
fn decode_integer(buf: &[u8], prefix_size: u8)
-> Result<(usize, usize), DecoderError> {
if prefix_size < 1 || prefix_size > 8 {
return Err(
DecoderError::IntegerDecodingError(
IntegerDecodingError::InvalidPrefix));
}
if buf.len() < 1 {
return Err(
DecoderError::IntegerDecodingError(
IntegerDecodingError::NotEnoughOctets));
}
let Wrapping(mask) = if prefix_size == 8 {
Wrapping(0xFF)
} else {
Wrapping(1u8 << prefix_size) - Wrapping(1)
};
let mut value = (buf[0] & mask) as usize;
if value < (mask as usize) {
return Ok((value, 1));
}
let mut total = 1;
let mut m = 0;
let octet_limit = 5;
for &b in buf[1..].iter() {
total += 1;
value += ((b & 127) as usize) * (1 << m);
m += 7;
if b & 128 != 128 {
return Ok((value, total));
}
if total == octet_limit {
return Err(
DecoderError::IntegerDecodingError(
IntegerDecodingError::TooManyOctets))
}
}
Err(DecoderError::IntegerDecodingError(
IntegerDecodingError::NotEnoughOctets))
}
fn decode_string<'a>(buf: &'a [u8]) -> Result<(Cow<'a, [u8]>, usize), DecoderError> {
let (len, consumed) = try!(decode_integer(buf, 7));
debug!("decode_string: Consumed = {}, len = {}", consumed, len);
if consumed + len > buf.len() {
return Err(
DecoderError::StringDecodingError(
StringDecodingError::NotEnoughOctets));
}
let raw_string = &buf[consumed..consumed + len];
if buf[0] & 128 == 128 {
debug!("decode_string: Using the Huffman code");
let mut decoder = HuffmanDecoder::new();
let decoded = match decoder.decode(raw_string) {
Err(e) => {
return Err(DecoderError::StringDecodingError(
StringDecodingError::HuffmanDecoderError(e)));
},
Ok(res) => res,
};
Ok((Cow::Owned(decoded), consumed + len))
} else {
debug!("decode_string: Raw octet string received");
Ok((Cow::Borrowed(raw_string), consumed + len))
}
}
enum FieldRepresentation {
Indexed,
LiteralWithIncrementalIndexing,
SizeUpdate,
LiteralNeverIndexed,
LiteralWithoutIndexing,
}
impl FieldRepresentation {
fn new(octet: u8) -> FieldRepresentation {
if octet & 128 == 128 {
FieldRepresentation::Indexed
} else if octet & 64 == 64 {
FieldRepresentation::LiteralWithIncrementalIndexing
} else if octet & 32 == 32 {
FieldRepresentation::SizeUpdate
} else if octet & 16 == 16 {
FieldRepresentation::LiteralNeverIndexed
} else {
FieldRepresentation::LiteralWithoutIndexing
}
}
}
#[derive(PartialEq)]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
pub enum IntegerDecodingError {
TooManyOctets,
ValueTooLarge,
NotEnoughOctets,
InvalidPrefix,
}
#[derive(PartialEq)]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
pub enum StringDecodingError {
NotEnoughOctets,
HuffmanDecoderError(HuffmanDecoderError),
}
#[derive(PartialEq)]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
pub enum DecoderError {
HeaderIndexOutOfBounds,
IntegerDecodingError(IntegerDecodingError),
StringDecodingError(StringDecodingError),
InvalidMaxDynamicSize,
}
pub type DecoderResult = Result<Vec<(Vec<u8>, Vec<u8>)>, DecoderError>;
pub struct Decoder<'a> {
header_table: HeaderTable<'a>,
}
impl<'a> Decoder<'a> {
pub fn new() -> Decoder<'a> {
Decoder::with_static_table(STATIC_TABLE)
}
fn with_static_table(static_table: StaticTable<'a>) -> Decoder<'a> {
Decoder {
header_table: HeaderTable::with_static_table(static_table)
}
}
pub fn set_max_table_size(&mut self, new_max_size: usize) {
self.header_table.dynamic_table.set_max_table_size(new_max_size);
}
pub fn decode_with_cb<F>(&mut self, buf: &[u8], mut cb: F) -> Result<(), DecoderError>
where F: FnMut(Cow<[u8]>, Cow<[u8]>) {
let mut current_octet_index = 0;
while current_octet_index < buf.len() {
let initial_octet = buf[current_octet_index];
let buffer_leftover = &buf[current_octet_index..];
let consumed = match FieldRepresentation::new(initial_octet) {
FieldRepresentation::Indexed => {
let ((name, value), consumed) =
try!(self.decode_indexed(buffer_leftover));
cb(Cow::Borrowed(name), Cow::Borrowed(value));
consumed
},
FieldRepresentation::LiteralWithIncrementalIndexing => {
let ((name, value), consumed) = {
let ((name, value), consumed) = try!(
self.decode_literal(buffer_leftover, true));
cb(Cow::Borrowed(&name), Cow::Borrowed(&value));
let name = name.into_owned();
let value = value.into_owned();
((name, value), consumed)
};
self.header_table.add_header(name, value);
consumed
},
FieldRepresentation::LiteralWithoutIndexing => {
let ((name, value), consumed) =
try!(self.decode_literal(buffer_leftover, false));
cb(name, value);
consumed
},
FieldRepresentation::LiteralNeverIndexed => {
let ((name, value), consumed) =
try!(self.decode_literal(buffer_leftover, false));
cb(name, value);
consumed
},
FieldRepresentation::SizeUpdate => {
self.update_max_dynamic_size(buffer_leftover)
}
};
current_octet_index += consumed;
}
Ok(())
}
pub fn decode(&mut self, buf: &[u8]) -> DecoderResult {
let mut header_list = Vec::new();
try!(self.decode_with_cb(buf, |n, v| header_list.push((n.into_owned(), v.into_owned()))));
Ok(header_list)
}
fn decode_indexed(&self, buf: &[u8])
-> Result<((&[u8], &[u8]), usize), DecoderError> {
let (index, consumed) = try!(decode_integer(buf, 7));
debug!("Decoding indexed: index = {}, consumed = {}", index, consumed);
let (name, value) = try!(self.get_from_table(index));
Ok(((name, value), consumed))
}
fn get_from_table(&self, index: usize)
-> Result<(&[u8], &[u8]), DecoderError> {
self.header_table.get_from_table(index).ok_or(
DecoderError::HeaderIndexOutOfBounds)
}
fn decode_literal<'b>(&'b self, buf: &'b [u8], index: bool)
-> Result<((Cow<[u8]>, Cow<[u8]>), usize), DecoderError> {
let prefix = if index {
6
} else {
4
};
let (table_index, mut consumed) = try!(decode_integer(buf, prefix));
let name = if table_index == 0 {
let (name, name_len) = try!(decode_string(&buf[consumed..]));
consumed += name_len;
name
} else {
let (name, _) = try!(self.get_from_table(table_index));
Cow::Borrowed(name)
};
let (value, value_len) = try!(decode_string(&buf[consumed..]));
consumed += value_len;
Ok(((name, value), consumed))
}
fn update_max_dynamic_size(&mut self, buf: &[u8]) -> usize {
let (new_size, consumed) = decode_integer(buf, 5).ok().unwrap();
self.header_table.dynamic_table.set_max_table_size(new_size);
info!("Decoder changed max table size from {} to {}",
self.header_table.dynamic_table.get_size(),
new_size);
consumed
}
}
#[cfg(test)]
mod tests {
use super::{decode_integer};
use std::borrow::Cow;
use super::super::encoder::encode_integer;
use super::FieldRepresentation;
use super::decode_string;
use super::Decoder;
use super::{DecoderError, DecoderResult};
use super::{IntegerDecodingError, StringDecodingError};
use super::super::huffman::HuffmanDecoderError;
#[test]
fn test_decode_integer() {
assert_eq!((10, 1),
decode_integer(&[10], 5).ok().unwrap());
assert_eq!((1337, 3),
decode_integer(&[31, 154, 10], 5).ok().unwrap());
assert_eq!((1337, 3),
decode_integer(&[31 + 32, 154, 10], 5).ok().unwrap());
assert_eq!((1337, 3),
decode_integer(&[31 + 64, 154, 10], 5).ok().unwrap());
assert_eq!((1337, 3),
decode_integer(&[31, 154, 10, 111, 22], 5).ok().unwrap());
assert_eq!((127, 2), decode_integer(&[255, 0], 7).ok().unwrap());
assert_eq!((127, 2), decode_integer(&[127, 0], 7).ok().unwrap());
assert_eq!((255, 3), decode_integer(&[127, 128, 1], 7).ok().unwrap());
assert_eq!((255, 2), decode_integer(&[255, 0], 8).unwrap());
assert_eq!((254, 1), decode_integer(&[254], 8).unwrap());
assert_eq!((1, 1), decode_integer(&[1], 8).unwrap());
assert_eq!((0, 1), decode_integer(&[0], 8).unwrap());
assert_eq!(
(268435710, 5),
decode_integer(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF - 128], 8).ok().unwrap());
}
macro_rules! assert_integer_err (
($err_type:expr, $decoder_result:expr) => (
assert_eq!($err_type, match $decoder_result {
Err(DecoderError::IntegerDecodingError(e)) => e,
_ => panic!("Expected a decoding error"),
});
);
);
#[test]
fn test_decode_integer_errors() {
assert_integer_err!(IntegerDecodingError::NotEnoughOctets,
decode_integer(&[], 5));
assert_integer_err!(IntegerDecodingError::NotEnoughOctets,
decode_integer(&[0xFF, 0xFF], 5));
assert_integer_err!(IntegerDecodingError::TooManyOctets,
decode_integer(&[0xFF, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80], 1));
assert_integer_err!(IntegerDecodingError::TooManyOctets,
decode_integer(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0], 8));
assert_integer_err!(IntegerDecodingError::InvalidPrefix,
decode_integer(&[10], 0));
assert_integer_err!(IntegerDecodingError::InvalidPrefix,
decode_integer(&[10], 9));
}
#[test]
fn test_detect_literal_without_indexing() {
assert!(match FieldRepresentation::new(0) {
FieldRepresentation::LiteralWithoutIndexing => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 4) - 1) {
FieldRepresentation::LiteralWithoutIndexing => true,
_ => false,
});
assert!(match FieldRepresentation::new(2) {
FieldRepresentation::LiteralWithoutIndexing => true,
_ => false,
});
}
#[test]
fn test_detect_literal_never_indexed() {
assert!(match FieldRepresentation::new(1 << 4) {
FieldRepresentation::LiteralNeverIndexed => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 4) + 15) {
FieldRepresentation::LiteralNeverIndexed => true,
_ => false,
});
}
#[test]
fn test_detect_literal_incremental_indexing() {
assert!(match FieldRepresentation::new(1 << 6) {
FieldRepresentation::LiteralWithIncrementalIndexing => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 6) + (1 << 4)) {
FieldRepresentation::LiteralWithIncrementalIndexing => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 7) - 1) {
FieldRepresentation::LiteralWithIncrementalIndexing => true,
_ => false,
});
}
#[test]
fn test_detect_indexed() {
assert!(match FieldRepresentation::new(1 << 7) {
FieldRepresentation::Indexed => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 7) + (1 << 4)) {
FieldRepresentation::Indexed => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 7) + (1 << 5)) {
FieldRepresentation::Indexed => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 7) + (1 << 6)) {
FieldRepresentation::Indexed => true,
_ => false,
});
assert!(match FieldRepresentation::new(255) {
FieldRepresentation::Indexed => true,
_ => false,
});
}
#[test]
fn test_detect_dynamic_table_size_update() {
assert!(match FieldRepresentation::new(1 << 5) {
FieldRepresentation::SizeUpdate => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 5) + (1 << 4)) {
FieldRepresentation::SizeUpdate => true,
_ => false,
});
assert!(match FieldRepresentation::new((1 << 6) - 1) {
FieldRepresentation::SizeUpdate => true,
_ => false,
});
}
#[test]
fn test_decode_string_no_huffman() {
fn assert_borrowed_eq<'a>(expected: (&[u8], usize), result: (Cow<'a, [u8]>, usize)) {
let (expected_str, expected_len) = expected;
let (actual_str, actual_len) = result;
assert_eq!(expected_len, actual_len);
match actual_str {
Cow::Borrowed(actual) => assert_eq!(actual, expected_str),
_ => panic!("Expected the result to be borrowed!"),
};
}
assert_eq!((Cow::Borrowed(&b"abc"[..]), 4),
decode_string(&[3, b'a', b'b', b'c']).ok().unwrap());
assert_eq!((Cow::Borrowed(&b"a"[..]), 2),
decode_string(&[1, b'a']).ok().unwrap());
assert_eq!((Cow::Borrowed(&b""[..]), 1),
decode_string(&[0, b'a']).ok().unwrap());
assert_borrowed_eq((&b"abc"[..], 4),
decode_string(&[3, b'a', b'b', b'c']).ok().unwrap());
assert_borrowed_eq((&b"a"[..], 2),
decode_string(&[1, b'a']).ok().unwrap());
assert_borrowed_eq((&b""[..], 1),
decode_string(&[0, b'a']).ok().unwrap());
assert_eq!(StringDecodingError::NotEnoughOctets,
match decode_string(&[3, b'a', b'b']) {
Err(DecoderError::StringDecodingError(e)) => e,
_ => panic!("Expected NotEnoughOctets error!"),
}
);
}
#[test]
fn test_decode_string_no_huffman_long() {
{
let full_string: Vec<u8> = (0u8..200).collect();
let mut encoded = encode_integer(full_string.len(), 7);
encoded.extend(full_string.clone().into_iter());
assert_eq!(
(Cow::Owned(full_string), encoded.len()),
decode_string(&encoded).ok().unwrap());
}
{
let full_string: Vec<u8> = (0u8..127).collect();
let mut encoded = encode_integer(full_string.len(), 7);
encoded.extend(full_string.clone().into_iter());
assert_eq!(
(Cow::Owned(full_string), encoded.len()),
decode_string(&encoded).ok().unwrap());
}
}
#[test]
fn test_decode_fully_in_static_table() {
let mut decoder = Decoder::new();
let header_list = decoder.decode(&[0x82]).ok().unwrap();
assert_eq!(vec![(b":method".to_vec(), b"GET".to_vec())], header_list);
}
#[test]
fn test_decode_multiple_fully_in_static_table() {
let mut decoder = Decoder::new();
let header_list = decoder.decode(&[0x82, 0x86, 0x84]).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
]);
}
#[test]
fn test_decode_literal_indexed_name() {
let mut decoder = Decoder::new();
let hex_dump = [
0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":path".to_vec(), b"/sample/path".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 0);
}
#[test]
fn test_decode_literal_both() {
let mut decoder = Decoder::new();
let hex_dump = [
0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65,
0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b"custom-key".to_vec(), b"custom-header".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 1);
let expected_table = vec![
(b"custom-key".to_vec(), b"custom-header".to_vec())
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
#[test]
fn test_decode_literal_name_in_dynamic() {
let mut decoder = Decoder::new();
{
let hex_dump = [
0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b,
0x65, 0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b"custom-key".to_vec(), b"custom-header".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 1);
let expected_table = vec![
(b"custom-key".to_vec(), b"custom-header".to_vec())
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x40 + 62,
0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x2d,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b"custom-key".to_vec(), b"custom-header-".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 2);
let expected_table = vec![
(b"custom-key".to_vec(), b"custom-header-".to_vec()),
(b"custom-key".to_vec(), b"custom-header".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
}
#[test]
fn test_decode_literal_field_never_indexed() {
let mut decoder = Decoder::new();
let hex_dump = [
0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06,
0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b"password".to_vec(), b"secret".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 0);
}
#[test]
fn test_request_sequence_no_huffman() {
let mut decoder = Decoder::new();
{
let hex_dump = [
0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 1);
let expected_table = vec![
(b":authority".to_vec(), b"www.example.com".to_vec())
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63,
0x61, 0x63, 0x68, 0x65,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
(b"cache-control".to_vec(), b"no-cache".to_vec()),
]);
let expected_table = vec![
(b"cache-control".to_vec(), b"no-cache".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74,
0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73,
0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"https".to_vec()),
(b":path".to_vec(), b"/index.html".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
(b"custom-key".to_vec(), b"custom-value".to_vec()),
]);
let expected_table = vec![
(b"custom-key".to_vec(), b"custom-value".to_vec()),
(b"cache-control".to_vec(), b"no-cache".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
}
#[test]
fn response_sequence_no_huffman() {
let mut decoder = Decoder::new();
decoder.set_max_table_size(256);
{
let hex_dump = [
0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69,
0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c,
0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30,
0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32,
0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74,
0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"302".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b":status".to_vec(), b"302".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"307".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![
(b":status".to_vec(), b"307".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32,
0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33,
0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20,
0x47, 0x4d, 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70,
0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a,
0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57,
0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51, 0x57, 0x45,
0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61,
0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
let expected_header_list = [
(b":status".to_vec(), b"200".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:22 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"content-encoding".to_vec(), b"gzip".to_vec()),
(
b"set-cookie".to_vec(),
b"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".to_vec()
),
];
assert_eq!(header_list, expected_header_list);
let expected_table = vec![
(
b"set-cookie".to_vec(),
b"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".to_vec()
),
(b"content-encoding".to_vec(), b"gzip".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:22 GMT".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
}
#[test]
fn test_decoder_clear_dynamic_table() {
let mut decoder = Decoder::new();
{
let hex_dump = [
0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69,
0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c,
0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30,
0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32,
0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74,
0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"302".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b":status".to_vec(), b"302".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf,
0x20,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"307".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
assert_eq!(0, decoder.header_table.dynamic_table.get_max_table_size());
}
}
#[test]
fn request_sequence_huffman() {
let mut decoder = Decoder::new();
{
let hex_dump = [
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2,
0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
]);
assert_eq!(decoder.header_table.dynamic_table.len(), 1);
let expected_table = vec![
(b":authority".to_vec(), b"www.example.com".to_vec())
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64,
0x9c, 0xbf,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
(b"cache-control".to_vec(), b"no-cache".to_vec()),
]);
let expected_table = vec![
(b"cache-control".to_vec(), b"no-cache".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9,
0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b,
0xb8, 0xe8, 0xb4, 0xbf,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"https".to_vec()),
(b":path".to_vec(), b"/index.html".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
(b"custom-key".to_vec(), b"custom-value".to_vec()),
]);
let expected_table = vec![
(b"custom-key".to_vec(), b"custom-value".to_vec()),
(b"cache-control".to_vec(), b"no-cache".to_vec()),
(b":authority".to_vec(), b"www.example.com".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
}
#[test]
fn response_sequence_huffman() {
let mut decoder = Decoder::new();
decoder.set_max_table_size(256);
{
let hex_dump = [
0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a,
0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4,
0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0,
0x82, 0xa6, 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad,
0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae,
0x82, 0xae, 0x43, 0xd3,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"302".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b":status".to_vec(), b"302".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
assert_eq!(header_list, [
(b":status".to_vec(), b"307".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
]);
let expected_table = vec![
(b":status".to_vec(), b"307".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:21 GMT".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
{
let hex_dump = [
0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54,
0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66,
0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b,
0xd9, 0xab, 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2,
0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60,
0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27,
0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0,
0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07,
];
let header_list = decoder.decode(&hex_dump).ok().unwrap();
let expected_header_list = [
(b":status".to_vec(), b"200".to_vec()),
(b"cache-control".to_vec(), b"private".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:22 GMT".to_vec()),
(b"location".to_vec(), b"https://www.example.com".to_vec()),
(b"content-encoding".to_vec(), b"gzip".to_vec()),
(
b"set-cookie".to_vec(),
b"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".to_vec()
),
];
assert_eq!(header_list, expected_header_list);
let expected_table = vec![
(
b"set-cookie".to_vec(),
b"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".to_vec()
),
(b"content-encoding".to_vec(), b"gzip".to_vec()),
(b"date".to_vec(), b"Mon, 21 Oct 2013 20:13:22 GMT".to_vec()),
];
let actual = decoder.header_table.dynamic_table.to_vec();
assert_eq!(actual, expected_table);
}
}
fn is_decoder_error(err: &DecoderError, result: &DecoderResult) -> bool {
match *result {
Err(ref e) => e == err,
_ => false
}
}
#[test]
fn test_index_out_of_bounds() {
let mut decoder = Decoder::new();
let raw_messages = [
vec![0x80],
vec![0xbe],
vec![126, 1, 65],
];
for raw_message in raw_messages.iter() {
assert!(
is_decoder_error(&DecoderError::HeaderIndexOutOfBounds,
&decoder.decode(&raw_message)),
"Expected index out of bounds");
}
}
#[test]
fn test_invalid_literal_huffman_string() {
let mut decoder = Decoder::new();
let hex_dump = [
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2,
0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xfe,
];
assert!(match decoder.decode(&hex_dump) {
Err(DecoderError::StringDecodingError(
StringDecodingError::HuffmanDecoderError(
HuffmanDecoderError::InvalidPadding))) => true,
_ => false,
});
}
#[test]
fn test_literal_header_key_incomplete() {
let mut decoder = Decoder::new();
let hex_dump = [
0x40, 0x0a, b'c', b'u', b's', b't', b'o', b'm', b'-', b'k', b'e',
];
let result = decoder.decode(&hex_dump);
assert!(match result {
Err(DecoderError::StringDecodingError(
StringDecodingError::NotEnoughOctets)) => true,
_ => false,
});
}
#[test]
fn test_literal_header_missing_value() {
let mut decoder = Decoder::new();
let hex_dump = [
0x40, 0x0a, b'c', b'u', b's', b't', b'o', b'm', b'-', b'k', b'e',
b'y',
];
let result = decoder.decode(&hex_dump);
assert!(match result {
Err(DecoderError::IntegerDecodingError(
IntegerDecodingError::NotEnoughOctets)) => true,
_ => false,
});
}
}
#[cfg(feature="interop_tests")]
#[cfg(test)]
mod interop_tests {
use std::io::Read;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use rustc_serialize::Decoder as JsonDecoder;
use rustc_serialize::{Decodable, json};
use rustc_serialize::hex::FromHex;
use super::Decoder;
struct TestFixture {
wire_bytes: Vec<u8>,
headers: Vec<(Vec<u8>, Vec<u8>)>,
}
#[derive(RustcDecodable)]
struct TestStory {
cases: Vec<TestFixture>,
}
impl Decodable for TestFixture {
fn decode<D: JsonDecoder>(d: &mut D) -> Result<Self, D::Error> {
d.read_struct("root", 0, |d| Ok(TestFixture {
wire_bytes: try!(d.read_struct_field("wire", 0, |d| {
Decodable::decode(d).and_then(|res: String| {
Ok(res.from_hex().unwrap())
})
})),
headers: try!(d.read_struct_field("headers", 0, |d| {
d.read_seq(|d, len| {
let mut ret: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
for i in (0..len) {
let header: HashMap<String, String> = try!(
d.read_seq_elt(i, |d| Decodable::decode(d)));
for (name, value) in header.into_iter() {
ret.push((
name.as_bytes().to_vec(),
value.as_bytes().to_vec()
));
}
}
Ok(ret)
})
})),
}))
}
}
#[test]
fn test_story_parser_sanity_check() {
let raw_json = stringify!(
{
"cases": [
{
"seqno": 0,
"wire": "82864188f439ce75c875fa5784",
"headers": [
{
":method": "GET"
},
{
":scheme": "http"
},
{
":authority": "yahoo.co.jp"
},
{
":path": "/"
}
]
},
{
"seqno": 1,
"wire": "8286418cf1e3c2fe8739ceb90ebf4aff84",
"headers": [
{
":method": "GET"
},
{
":scheme": "http"
},
{
":authority": "www.yahoo.co.jp"
},
{
":path": "/"
}
]
}
],
"draft": 9
}
);
let decoded: TestStory = json::decode(raw_json).unwrap();
assert_eq!(decoded.cases.len(), 2);
assert_eq!(decoded.cases[0].wire_bytes, vec![
0x82, 0x86, 0x41, 0x88, 0xf4, 0x39, 0xce, 0x75, 0xc8, 0x75, 0xfa,
0x57, 0x84
]);
assert_eq!(decoded.cases[0].headers, vec![
(b":method".to_vec(), b"GET".to_vec()),
(b":scheme".to_vec(), b"http".to_vec()),
(b":authority".to_vec(), b"yahoo.co.jp".to_vec()),
(b":path".to_vec(), b"/".to_vec()),
]);
}
fn test_story(story_file_name: PathBuf) {
let story: TestStory = {
let mut file = File::open(&story_file_name).unwrap();
let mut raw_story = String::new();
file.read_to_string(&mut raw_story).unwrap();
json::decode(&raw_story).unwrap()
};
let mut decoder = Decoder::new();
for case in story.cases.iter() {
let decoded = decoder.decode(&case.wire_bytes).unwrap();
assert_eq!(decoded, case.headers);
}
}
fn test_fixture_set(fixture_dir: &str) {
let files = fs::read_dir(&Path::new(fixture_dir)).unwrap();
for fixture in files {
let file_name = fixture.unwrap().path();
debug!("Testing fixture: {:?}", file_name);
test_story(file_name);
}
}
#[test]
fn test_nghttp2_interop() {
test_fixture_set("fixtures/hpack/interop/nghttp2");
}
#[test]
fn test_nghttp2_change_table_size_interop() {
test_fixture_set("fixtures/hpack/interop/nghttp2-change-table-size");
}
#[test]
fn test_go_hpack_interop() {
test_fixture_set("fixtures/hpack/interop/go-hpack");
}
#[test]
fn test_node_http2_hpack_interop() {
test_fixture_set("fixtures/hpack/interop/node-http2-hpack");
}
#[test]
fn test_haskell_http2_linear_huffman() {
test_fixture_set("fixtures/hpack/interop/haskell-http2-linear-huffman");
}
}