More hpack work

This commit is contained in:
Carl Lerche
2017-03-15 17:16:00 -07:00
parent 1deac1f16e
commit eb00f71caf
4 changed files with 146 additions and 52 deletions

View File

@@ -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 bytes::{Buf, Bytes};
use std::io::Cursor; use std::io::Cursor;
@@ -20,8 +20,12 @@ pub enum DecoderError {
InvalidRepresentation, InvalidRepresentation,
InvalidIntegerPrefix, InvalidIntegerPrefix,
InvalidTableIndex, InvalidTableIndex,
InvalidHuffmanCode,
InvalidUtf8,
InvalidStatusCode,
IntegerUnderflow, IntegerUnderflow,
IntegerOverflow, IntegerOverflow,
StringUnderflow,
} }
enum Representation { enum Representation {
@@ -149,7 +153,11 @@ impl Decoder {
f(try!(self.decode_indexed(&mut buf))); f(try!(self.decode_indexed(&mut buf)));
} }
LiteralWithIndexing => { LiteralWithIndexing => {
unimplemented!(); let entry = try!(self.decode_literal(&mut buf, true));
// TODO: Add entry to the table
f(entry);
} }
LiteralWithoutIndexing => { LiteralWithoutIndexing => {
unimplemented!(); unimplemented!();
@@ -166,12 +174,16 @@ impl Decoder {
Ok(()) Ok(())
} }
fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) -> Result<Entry, DecoderError> { fn decode_indexed(&self, buf: &mut Cursor<&Bytes>)
-> Result<Entry, DecoderError>
{
let index = try!(decode_int(buf, 7)); let index = try!(decode_int(buf, 7));
self.table.get(index) self.table.get(index)
} }
fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool) -> Result<Entry, DecoderError> { fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool)
-> Result<Entry, DecoderError>
{
let prefix = if index { let prefix = if index {
6 6
} else { } else {
@@ -181,7 +193,17 @@ impl Decoder {
// Extract the table index for the name, or 0 if not indexed // Extract the table index for the name, or 0 if not indexed
let table_idx = try!(decode_int(buf, prefix)); 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<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderErro
Err(DecoderError::IntegerUnderflow) Err(DecoderError::IntegerUnderflow)
} }
fn decode_string(buf: &mut Cursor<&Bytes>) -> Result<Bytes, DecoderError> {
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<B: Buf>(buf: &mut B) -> u8 { fn peek_u8<B: Buf>(buf: &mut B) -> u8 {
buf.bytes()[0] 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 =====
impl Table { impl Table {
@@ -337,6 +386,14 @@ impl Table {
} }
} }
// ===== impl DecoderError =====
impl From<FromUtf8Error> for DecoderError {
fn from(src: FromUtf8Error) -> DecoderError {
DecoderError::InvalidUtf8
}
}
/// Get an entry from the static table /// Get an entry from the static table
pub fn get_static(idx: usize) -> Entry { pub fn get_static(idx: usize) -> Entry {
use tower::http::StandardHeader::*; use tower::http::StandardHeader::*;
@@ -349,13 +406,13 @@ pub fn get_static(idx: usize) -> Entry {
5 => Entry::Path(Str::from_static("/index.html")), 5 => Entry::Path(Str::from_static("/index.html")),
6 => Entry::Scheme(Str::from_static("http")), 6 => Entry::Scheme(Str::from_static("http")),
7 => Entry::Scheme(Str::from_static("https")), 7 => Entry::Scheme(Str::from_static("https")),
8 => Entry::Status(StatusCode::Ok), 8 => Entry::Status(status::Ok),
9 => Entry::Status(StatusCode::NoContent), 9 => Entry::Status(status::NoContent),
10 => Entry::Status(StatusCode::PartialContent), 10 => Entry::Status(status::PartialContent),
11 => Entry::Status(StatusCode::NotModified), 11 => Entry::Status(status::NotModified),
12 => Entry::Status(StatusCode::BadRequest), 12 => Entry::Status(status::BadRequest),
13 => Entry::Status(StatusCode::NotFound), 13 => Entry::Status(status::NotFound),
14 => Entry::Status(StatusCode::InternalServerError), 14 => Entry::Status(status::InternalServerError),
15 => Entry::Header { 15 => Entry::Header {
name: AcceptCharset.into(), name: AcceptCharset.into(),
value: Str::new(), value: Str::new(),

View File

@@ -1,4 +1,7 @@
use super::DecoderError;
use tower::http::{HeaderName, Method, StatusCode, Str}; use tower::http::{HeaderName, Method, StatusCode, Str};
use bytes::Bytes;
/// HPack table entry /// HPack table entry
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -14,6 +17,16 @@ pub enum Entry {
Status(StatusCode), Status(StatusCode),
} }
/// The name component of an Entry
pub enum Key<'a> {
Header(&'a HeaderName),
Authority,
Method,
Scheme,
Path,
Status,
}
impl Entry { impl Entry {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match *self { 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<Entry, DecoderError> {
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),
}
}
}
}
} }

View File

@@ -33,9 +33,8 @@ pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
} }
} }
if !decoder.maybe_eos { if !decoder.is_final() {
// TODO: handle error return Err(DecoderError::InvalidHuffmanCode);
unimplemented!();
} }
Ok(dst) Ok(dst)
@@ -70,6 +69,10 @@ impl Decoder {
Ok(ret) Ok(ret)
} }
fn is_final(&self) -> bool {
self.state == 0 || self.maybe_eos
}
} }
#[cfg(test)] #[cfg(test)]
@@ -78,42 +81,21 @@ mod test {
#[test] #[test]
fn decode_single_byte() { 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(); #[test]
assert_eq!(actual, "o"); 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());
}
/* #[test]
let mut decoder = HuffmanDecoder::new(); fn multi_char() {
// (The + (2^n - 1) at the final byte is to add the correct expected assert_eq!("!0", decode(&[254, 1]).unwrap());
// padding: 1s) assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
{
// 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);
}
*/
} }
} }

View File

@@ -5,6 +5,6 @@ mod huffman;
// mod table; // mod table;
pub use self::encoder::Encoder; 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::decoder::{Decoder, DecoderError};
// pub use self::table::Entry; // pub use self::table::Entry;