diff --git a/src/hpack/decoder.rs b/src/hpack/decoder.rs index c2a7410..d121ca7 100644 --- a/src/hpack/decoder.rs +++ b/src/hpack/decoder.rs @@ -1,6 +1,6 @@ -use super::Entry; +use super::{huffman, Entry, Key}; -use tower::http::{HeaderName, StatusCode, Method, Str}; +use tower::http::{status, HeaderName, StatusCode, Method, Str, FromUtf8Error}; use bytes::{Buf, Bytes}; use std::io::Cursor; @@ -20,8 +20,12 @@ pub enum DecoderError { InvalidRepresentation, InvalidIntegerPrefix, InvalidTableIndex, + InvalidHuffmanCode, + InvalidUtf8, + InvalidStatusCode, IntegerUnderflow, IntegerOverflow, + StringUnderflow, } enum Representation { @@ -149,7 +153,11 @@ impl Decoder { f(try!(self.decode_indexed(&mut buf))); } LiteralWithIndexing => { - unimplemented!(); + let entry = try!(self.decode_literal(&mut buf, true)); + + // TODO: Add entry to the table + + f(entry); } LiteralWithoutIndexing => { unimplemented!(); @@ -166,12 +174,16 @@ impl Decoder { Ok(()) } - fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) -> Result { + fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) + -> Result + { let index = try!(decode_int(buf, 7)); self.table.get(index) } - fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool) -> Result { + fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool) + -> Result + { let prefix = if index { 6 } else { @@ -181,7 +193,17 @@ impl Decoder { // Extract the table index for the name, or 0 if not indexed let table_idx = try!(decode_int(buf, prefix)); - unimplemented!(); + // First, read the header name + if table_idx == 0 { + // Read the name as a literal + let name = try!(decode_string(buf)); + + unimplemented!(); + } else { + let e = try!(self.table.get(table_idx)); + let value = try!(decode_string(buf)); + e.key().into_entry(value) + } } } @@ -270,10 +292,37 @@ fn decode_int(buf: &mut B, prefix_size: u8) -> Result) -> Result { + const HUFF_FLAG: u8 = 0b10000000; + + let len = try!(decode_int(buf, 7)); + + if len > buf.remaining() { + return Err(DecoderError::StringUnderflow); + } + + { + let raw = &buf.bytes()[..len]; + + if raw[0] & HUFF_FLAG == HUFF_FLAG { + return huffman::decode(raw).map(Into::into); + } + } + + Ok(take(buf, len)) +} + fn peek_u8(buf: &mut B) -> u8 { buf.bytes()[0] } +fn take(buf: &mut Cursor<&Bytes>, n: usize) -> Bytes { + let pos = buf.position() as usize; + let ret = buf.get_ref().slice(pos, pos + n); + buf.set_position((pos + n) as u64); + ret +} + // ===== impl Table ===== impl Table { @@ -337,6 +386,14 @@ impl Table { } } +// ===== impl DecoderError ===== + +impl From for DecoderError { + fn from(src: FromUtf8Error) -> DecoderError { + DecoderError::InvalidUtf8 + } +} + /// Get an entry from the static table pub fn get_static(idx: usize) -> Entry { use tower::http::StandardHeader::*; @@ -349,13 +406,13 @@ pub fn get_static(idx: usize) -> Entry { 5 => Entry::Path(Str::from_static("/index.html")), 6 => Entry::Scheme(Str::from_static("http")), 7 => Entry::Scheme(Str::from_static("https")), - 8 => Entry::Status(StatusCode::Ok), - 9 => Entry::Status(StatusCode::NoContent), - 10 => Entry::Status(StatusCode::PartialContent), - 11 => Entry::Status(StatusCode::NotModified), - 12 => Entry::Status(StatusCode::BadRequest), - 13 => Entry::Status(StatusCode::NotFound), - 14 => Entry::Status(StatusCode::InternalServerError), + 8 => Entry::Status(status::Ok), + 9 => Entry::Status(status::NoContent), + 10 => Entry::Status(status::PartialContent), + 11 => Entry::Status(status::NotModified), + 12 => Entry::Status(status::BadRequest), + 13 => Entry::Status(status::NotFound), + 14 => Entry::Status(status::InternalServerError), 15 => Entry::Header { name: AcceptCharset.into(), value: Str::new(), diff --git a/src/hpack/entry.rs b/src/hpack/entry.rs index c945237..4b98957 100644 --- a/src/hpack/entry.rs +++ b/src/hpack/entry.rs @@ -1,4 +1,7 @@ +use super::DecoderError; + use tower::http::{HeaderName, Method, StatusCode, Str}; +use bytes::Bytes; /// HPack table entry #[derive(Debug, Clone)] @@ -14,6 +17,16 @@ pub enum Entry { Status(StatusCode), } +/// The name component of an Entry +pub enum Key<'a> { + Header(&'a HeaderName), + Authority, + Method, + Scheme, + Path, + Status, +} + impl Entry { pub fn len(&self) -> usize { match *self { @@ -38,4 +51,46 @@ impl Entry { } } } + + pub fn key(&self) -> Key { + match *self { + Entry::Header { ref name, .. } => Key::Header(name), + Entry::Authority(..) => Key::Authority, + Entry::Method(..) => Key::Method, + Entry::Scheme(..) => Key::Scheme, + Entry::Path(..) => Key::Path, + Entry::Status(..) => Key::Status, + } + } +} + +impl<'a> Key<'a> { + pub fn into_entry(self, value: Bytes) -> Result { + match self { + Key::Header(name) => { + Ok(Entry::Header { + name: name.clone(), + value: try!(Str::from_utf8(value)), + }) + } + Key::Authority => { + Ok(Entry::Authority(try!(Str::from_utf8(value)))) + } + Key::Method => { + Ok(Entry::Scheme(try!(Str::from_utf8(value)))) + } + Key::Scheme => { + Ok(Entry::Scheme(try!(Str::from_utf8(value)))) + } + Key::Path => { + Ok(Entry::Path(try!(Str::from_utf8(value)))) + } + Key::Status => { + match StatusCode::parse(&value) { + Some(status) => Ok(Entry::Status(status)), + None => Err(DecoderError::InvalidStatusCode), + } + } + } + } } diff --git a/src/hpack/huffman/mod.rs b/src/hpack/huffman/mod.rs index 1257a40..9b13e7e 100644 --- a/src/hpack/huffman/mod.rs +++ b/src/hpack/huffman/mod.rs @@ -33,9 +33,8 @@ pub fn decode(src: &[u8]) -> Result { } } - if !decoder.maybe_eos { - // TODO: handle error - unimplemented!(); + if !decoder.is_final() { + return Err(DecoderError::InvalidHuffmanCode); } Ok(dst) @@ -70,6 +69,10 @@ impl Decoder { Ok(ret) } + + fn is_final(&self) -> bool { + self.state == 0 || self.maybe_eos + } } #[cfg(test)] @@ -78,42 +81,21 @@ mod test { #[test] fn decode_single_byte() { - let buf = [0b00111111]; + assert_eq!("o", decode(&[0b00111111]).unwrap()); + assert_eq!("0", decode(&[0x0 + 7]).unwrap()); + assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap()); + } - let actual = decode(&buf).unwrap(); - assert_eq!(actual, "o"); + #[test] + fn single_char_multi_byte() { + assert_eq!("#", decode(&[255, 160 + 15]).unwrap()); + assert_eq!("$", decode(&[255, 200 + 7]).unwrap()); + assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap()); + } - /* - let mut decoder = HuffmanDecoder::new(); - // (The + (2^n - 1) at the final byte is to add the correct expected - // padding: 1s) - { - // We need to shift it by 3, since we need the top-order bytes to - // start the code point. - let hex_buffer = [(0x7 << 3) + 7]; - let expected_result = vec![b'o']; - - let result = decoder.decode(&hex_buffer).ok().unwrap(); - - assert_eq!(result, expected_result); - } - { - let hex_buffer = [0x0 + 7]; - let expected_result = vec![b'0']; - - let result = decoder.decode(&hex_buffer).ok().unwrap(); - - assert_eq!(result, expected_result); - } - { - // The length of the codepoint is 6, so we shift by two - let hex_buffer = [(0x21 << 2) + 3]; - let expected_result = vec![b'A']; - - let result = decoder.decode(&hex_buffer).ok().unwrap(); - - assert_eq!(result, expected_result); - } - */ + #[test] + fn multi_char() { + assert_eq!("!0", decode(&[254, 1]).unwrap()); + assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap()); } } diff --git a/src/hpack/mod.rs b/src/hpack/mod.rs index ec70dab..691d112 100644 --- a/src/hpack/mod.rs +++ b/src/hpack/mod.rs @@ -5,6 +5,6 @@ mod huffman; // mod table; pub use self::encoder::Encoder; -pub use self::entry::Entry; +pub use self::entry::{Entry, Key}; pub use self::decoder::{Decoder, DecoderError}; // pub use self::table::Entry;