More hpack work
This commit is contained in:
@@ -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(),
|
||||||
|
|||||||
@@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user