More encoding work

This commit is contained in:
Carl Lerche
2017-06-02 14:33:09 -07:00
parent 2f8095e71a
commit a7da819e45
6 changed files with 310 additions and 194 deletions

View File

@@ -1,4 +1,4 @@
use super::{huffman, Entry, Key}; use super::{huffman, header as h2_header, Header};
use util::byte_str::FromUtf8Error; use util::byte_str::FromUtf8Error;
use http::{method, header, status, StatusCode, Method}; use http::{method, header, status, StatusCode, Method};
@@ -124,7 +124,7 @@ enum Representation {
} }
struct Table { struct Table {
entries: VecDeque<Entry>, entries: VecDeque<Header>,
size: usize, size: usize,
max_size: usize, max_size: usize,
} }
@@ -152,7 +152,7 @@ impl Decoder {
/// Decodes the headers found in the given buffer. /// Decodes the headers found in the given buffer.
pub fn decode<F>(&mut self, src: &Bytes, mut f: F) -> Result<(), DecoderError> pub fn decode<F>(&mut self, src: &Bytes, mut f: F) -> Result<(), DecoderError>
where F: FnMut(Entry) where F: FnMut(Header)
{ {
use self::Representation::*; use self::Representation::*;
@@ -227,14 +227,14 @@ impl Decoder {
} }
fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) fn decode_indexed(&self, buf: &mut Cursor<&Bytes>)
-> Result<Entry, DecoderError> -> Result<Header, 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) fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool)
-> Result<Entry, DecoderError> -> Result<Header, DecoderError>
{ {
let prefix = if index { let prefix = if index {
6 6
@@ -251,12 +251,12 @@ impl Decoder {
let name = try!(decode_string(buf)); let name = try!(decode_string(buf));
let value = try!(decode_string(buf)); let value = try!(decode_string(buf));
Entry::new(name, value) Header::new(name, value)
} else { } else {
let e = try!(self.table.get(table_idx)); let e = try!(self.table.get(table_idx));
let value = try!(decode_string(buf)); let value = try!(decode_string(buf));
e.key().into_entry(value) e.name().into_entry(value)
} }
} }
} }
@@ -419,7 +419,7 @@ impl Table {
/// ///
/// This is according to the [HPACK spec, section 2.3.3.] /// This is according to the [HPACK spec, section 2.3.3.]
/// (http://http2.github.io/http2-spec/compression.html#index.address.space) /// (http://http2.github.io/http2-spec/compression.html#index.address.space)
pub fn get(&self, index: usize) -> Result<Entry, DecoderError> { pub fn get(&self, index: usize) -> Result<Header, DecoderError> {
if index == 0 { if index == 0 {
return Err(DecoderError::InvalidTableIndex); return Err(DecoderError::InvalidTableIndex);
} }
@@ -435,7 +435,7 @@ impl Table {
} }
} }
fn insert(&mut self, entry: Entry) { fn insert(&mut self, entry: Header) {
let len = entry.len(); let len = entry.len();
self.reserve(len); self.reserve(len);
@@ -518,211 +518,211 @@ impl From<status::FromStrError> for DecoderError {
} }
/// 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) -> Header {
use http::{status, method, header}; use http::{status, method, header};
use http::header::HeaderValue; use http::header::HeaderValue;
use util::byte_str::ByteStr; use util::byte_str::ByteStr;
match idx { match idx {
1 => Entry::Authority(ByteStr::from_static("")), 1 => Header::Authority(ByteStr::from_static("")),
2 => Entry::Method(method::GET), 2 => Header::Method(method::GET),
3 => Entry::Method(method::POST), 3 => Header::Method(method::POST),
4 => Entry::Path(ByteStr::from_static("/")), 4 => Header::Path(ByteStr::from_static("/")),
5 => Entry::Path(ByteStr::from_static("/index.html")), 5 => Header::Path(ByteStr::from_static("/index.html")),
6 => Entry::Scheme(ByteStr::from_static("http")), 6 => Header::Scheme(ByteStr::from_static("http")),
7 => Entry::Scheme(ByteStr::from_static("https")), 7 => Header::Scheme(ByteStr::from_static("https")),
8 => Entry::Status(status::OK), 8 => Header::Status(status::OK),
9 => Entry::Status(status::NO_CONTENT), 9 => Header::Status(status::NO_CONTENT),
10 => Entry::Status(status::PARTIAL_CONTENT), 10 => Header::Status(status::PARTIAL_CONTENT),
11 => Entry::Status(status::NOT_MODIFIED), 11 => Header::Status(status::NOT_MODIFIED),
12 => Entry::Status(status::BAD_REQUEST), 12 => Header::Status(status::BAD_REQUEST),
13 => Entry::Status(status::NOT_FOUND), 13 => Header::Status(status::NOT_FOUND),
14 => Entry::Status(status::INTERNAL_SERVER_ERROR), 14 => Header::Status(status::INTERNAL_SERVER_ERROR),
15 => Entry::Header { 15 => Header::Field {
name: header::ACCEPT_CHARSET, name: header::ACCEPT_CHARSET,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
16 => Entry::Header { 16 => Header::Field {
name: header::ACCEPT_ENCODING, name: header::ACCEPT_ENCODING,
value: HeaderValue::from_static("gzip, deflate"), value: HeaderValue::from_static("gzip, deflate"),
}, },
17 => Entry::Header { 17 => Header::Field {
name: header::ACCEPT_LANGUAGE, name: header::ACCEPT_LANGUAGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
18 => Entry::Header { 18 => Header::Field {
name: header::ACCEPT_RANGES, name: header::ACCEPT_RANGES,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
19 => Entry::Header { 19 => Header::Field {
name: header::ACCEPT, name: header::ACCEPT,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
20 => Entry::Header { 20 => Header::Field {
name: header::ACCESS_CONTROL_ALLOW_ORIGIN, name: header::ACCESS_CONTROL_ALLOW_ORIGIN,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
21 => Entry::Header { 21 => Header::Field {
name: header::AGE, name: header::AGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
22 => Entry::Header { 22 => Header::Field {
name: header::ALLOW, name: header::ALLOW,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
23 => Entry::Header { 23 => Header::Field {
name: header::AUTHORIZATION, name: header::AUTHORIZATION,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
24 => Entry::Header { 24 => Header::Field {
name: header::CACHE_CONTROL, name: header::CACHE_CONTROL,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
25 => Entry::Header { 25 => Header::Field {
name: header::CONTENT_DISPOSITION, name: header::CONTENT_DISPOSITION,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
26 => Entry::Header { 26 => Header::Field {
name: header::CONTENT_ENCODING, name: header::CONTENT_ENCODING,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
27 => Entry::Header { 27 => Header::Field {
name: header::CONTENT_LANGUAGE, name: header::CONTENT_LANGUAGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
28 => Entry::Header { 28 => Header::Field {
name: header::CONTENT_LENGTH, name: header::CONTENT_LENGTH,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
29 => Entry::Header { 29 => Header::Field {
name: header::CONTENT_LOCATION, name: header::CONTENT_LOCATION,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
30 => Entry::Header { 30 => Header::Field {
name: header::CONTENT_RANGE, name: header::CONTENT_RANGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
31 => Entry::Header { 31 => Header::Field {
name: header::CONTENT_TYPE, name: header::CONTENT_TYPE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
32 => Entry::Header { 32 => Header::Field {
name: header::COOKIE, name: header::COOKIE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
33 => Entry::Header { 33 => Header::Field {
name: header::DATE, name: header::DATE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
34 => Entry::Header { 34 => Header::Field {
name: header::ETAG, name: header::ETAG,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
35 => Entry::Header { 35 => Header::Field {
name: header::EXPECT, name: header::EXPECT,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
36 => Entry::Header { 36 => Header::Field {
name: header::EXPIRES, name: header::EXPIRES,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
37 => Entry::Header { 37 => Header::Field {
name: header::FROM, name: header::FROM,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
38 => Entry::Header { 38 => Header::Field {
name: header::HOST, name: header::HOST,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
39 => Entry::Header { 39 => Header::Field {
name: header::IF_MATCH, name: header::IF_MATCH,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
40 => Entry::Header { 40 => Header::Field {
name: header::IF_MODIFIED_SINCE, name: header::IF_MODIFIED_SINCE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
41 => Entry::Header { 41 => Header::Field {
name: header::IF_NONE_MATCH, name: header::IF_NONE_MATCH,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
42 => Entry::Header { 42 => Header::Field {
name: header::IF_RANGE, name: header::IF_RANGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
43 => Entry::Header { 43 => Header::Field {
name: header::IF_UNMODIFIED_SINCE, name: header::IF_UNMODIFIED_SINCE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
44 => Entry::Header { 44 => Header::Field {
name: header::LAST_MODIFIED, name: header::LAST_MODIFIED,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
45 => Entry::Header { 45 => Header::Field {
name: header::LINK, name: header::LINK,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
46 => Entry::Header { 46 => Header::Field {
name: header::LOCATION, name: header::LOCATION,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
47 => Entry::Header { 47 => Header::Field {
name: header::MAX_FORWARDS, name: header::MAX_FORWARDS,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
48 => Entry::Header { 48 => Header::Field {
name: header::PROXY_AUTHENTICATE, name: header::PROXY_AUTHENTICATE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
49 => Entry::Header { 49 => Header::Field {
name: header::PROXY_AUTHORIZATION, name: header::PROXY_AUTHORIZATION,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
50 => Entry::Header { 50 => Header::Field {
name: header::RANGE, name: header::RANGE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
51 => Entry::Header { 51 => Header::Field {
name: header::REFERER, name: header::REFERER,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
52 => Entry::Header { 52 => Header::Field {
name: header::REFRESH, name: header::REFRESH,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
53 => Entry::Header { 53 => Header::Field {
name: header::RETRY_AFTER, name: header::RETRY_AFTER,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
54 => Entry::Header { 54 => Header::Field {
name: header::SERVER, name: header::SERVER,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
55 => Entry::Header { 55 => Header::Field {
name: header::SET_COOKIE, name: header::SET_COOKIE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
56 => Entry::Header { 56 => Header::Field {
name: header::STRICT_TRANSPORT_SECURITY, name: header::STRICT_TRANSPORT_SECURITY,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
57 => Entry::Header { 57 => Header::Field {
name: header::TRANSFER_ENCODING, name: header::TRANSFER_ENCODING,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
58 => Entry::Header { 58 => Header::Field {
name: header::USER_AGENT, name: header::USER_AGENT,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
59 => Entry::Header { 59 => Header::Field {
name: header::VARY, name: header::VARY,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
60 => Entry::Header { 60 => Header::Field {
name: header::VIA, name: header::VIA,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },
61 => Entry::Header { 61 => Header::Field {
name: header::WWW_AUTHENTICATE, name: header::WWW_AUTHENTICATE,
value: HeaderValue::from_static(""), value: HeaderValue::from_static(""),
}, },

View File

@@ -1,8 +1,8 @@
use super::entry; use super::{huffman, Header};
use super::table::Table; use super::table::{Table, Index};
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use bytes::BytesMut; use bytes::{BytesMut, BufMut};
pub struct Encoder { pub struct Encoder {
table: Table, table: Table,
@@ -24,7 +24,7 @@ impl Encoder {
} }
pub fn encode<'a, I>(&mut self, headers: I, dst: &mut BytesMut) -> Result<(), EncoderError> pub fn encode<'a, I>(&mut self, headers: I, dst: &mut BytesMut) -> Result<(), EncoderError>
where I: IntoIterator<Item=Entry>, where I: IntoIterator<Item=Header>,
{ {
if let Some(max_size_update) = self.max_size_update.take() { if let Some(max_size_update) = self.max_size_update.take() {
// Write size update frame // Write size update frame
@@ -38,31 +38,118 @@ impl Encoder {
Ok(()) Ok(())
} }
fn encode_header(&mut self, entry: Entry, dst: &mut BytesMut) fn encode_header(&mut self, header: Header, dst: &mut BytesMut)
-> Result<(), EncoderError> -> Result<(), EncoderError>
{ {
if is_sensitive(&e) { if header.is_sensitive() {
unimplemented!(); unimplemented!();
} }
/* match self.table.index(header) {
match self.table.entry(name, val) { Index::Indexed(idx, _header) => {
Entry::Indexed(idx) => { encode_int(idx, 7, 0x80, dst);
unimplemented!();
} }
Entry::Name(idx) => { Index::Name(idx, header) => {
unimplemented!(); encode_int(idx, 4, 0, dst);
encode_str(header.value_slice(), dst);
} }
Entry::NotIndexed => { Index::Inserted(header) => {
unimplemented!(); dst.put_u8(0b01000000);
encode_str(header.name().as_slice(), dst);
encode_str(header.value_slice(), dst);
}
Index::InsertedValue(idx, header) => {
encode_int(idx, 6, 0b01000000, dst);
encode_str(header.value_slice(), dst);
}
Index::NotIndexed(header) => {
dst.put_u8(0);
encode_str(header.name().as_slice(), dst);
encode_str(header.value_slice(), dst);
} }
} }
*/
unimplemented!(); Ok(())
} }
} }
fn is_sensitive(e: &Entry) -> bool { fn encode_str(val: &[u8], dst: &mut BytesMut) {
false use std::io::Cursor;
if val.len() != 0 {
let idx = dst.len();
// Push a placeholder byte for the length header
dst.put_u8(0);
// Encode with huffman
huffman::encode(val, dst);
let huff_len = dst.len() - (idx + 1);
if encode_int_one_byte(huff_len, 7) {
// Write the string head
dst[idx] = (0x80 | huff_len as u8);
} else {
// Write the head to a placeholer
let mut buf = [0; 8];
let head_len = {
let mut head_dst = Cursor::new(&mut buf);
encode_int(huff_len, 7, 0x80, &mut head_dst);
head_dst.position() as usize
};
// This is just done to reserve space in the destination
dst.put_slice(&buf[1..head_len]);
// Shift the header forward
for i in 0..huff_len {
dst[idx + head_len + (huff_len - i)] = dst[idx + 1 + (huff_len - 1)];
}
// Copy in the head
for i in 0..head_len {
dst[idx + i] = buf[i];
}
}
} else {
// Write an empty string
dst.put_u8(0);
}
}
/// Encode an integer into the given destination buffer
fn encode_int<B: BufMut>(
mut value: usize, // The integer to encode
prefix_bits: usize, // The number of bits in the prefix
first_byte: u8, // The base upon which to start encoding the int
dst: &mut B) // The destination buffer
{
if encode_int_one_byte(value, prefix_bits) {
dst.put_u8(first_byte | value as u8);
return;
}
let low = (1 << prefix_bits) - 1;
value -= low;
if value > 0x0fffffff {
panic!("value out of range");
}
dst.put_u8(first_byte | low as u8);
while value >= 128 {
dst.put_u8(0b10000000 | value as u8);
value = value >> 7;
}
dst.put_u8(value as u8);
}
/// Returns true if the in the int can be fully encoded in the first byte.
fn encode_int_one_byte(value: usize, prefix_bits: usize) -> bool {
value < (1 << prefix_bits) - 1
} }

View File

@@ -5,10 +5,10 @@ use http::{Method, StatusCode};
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use bytes::Bytes; use bytes::Bytes;
/// HPack table entry /// HTTP/2.0 Header
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Entry { pub enum Header {
Header { Field {
name: HeaderName, name: HeaderName,
value: HeaderValue, value: HeaderValue,
}, },
@@ -19,10 +19,10 @@ pub enum Entry {
Status(StatusCode), Status(StatusCode),
} }
/// The name component of an Entry /// The header field name
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Key<'a> { pub enum Name<'a> {
Header(&'a HeaderName), Field(&'a HeaderName),
Authority, Authority,
Method, Method,
Scheme, Scheme,
@@ -35,29 +35,29 @@ pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
32 + n.len() + value.len() 32 + n.len() + value.len()
} }
impl Entry { impl Header {
pub fn new(name: Bytes, value: Bytes) -> Result<Entry, DecoderError> { pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
if name[0] == b':' { if name[0] == b':' {
match &name[1..] { match &name[1..] {
b"authority" => { b"authority" => {
let value = try!(ByteStr::from_utf8(value)); let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Authority(value)) Ok(Header::Authority(value))
} }
b"method" => { b"method" => {
let method = try!(Method::from_bytes(&value)); let method = try!(Method::from_bytes(&value));
Ok(Entry::Method(method)) Ok(Header::Method(method))
} }
b"scheme" => { b"scheme" => {
let value = try!(ByteStr::from_utf8(value)); let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Scheme(value)) Ok(Header::Scheme(value))
} }
b"path" => { b"path" => {
let value = try!(ByteStr::from_utf8(value)); let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Path(value)) Ok(Header::Path(value))
} }
b"status" => { b"status" => {
let status = try!(StatusCode::from_slice(&value)); let status = try!(StatusCode::from_slice(&value));
Ok(Entry::Status(status)) Ok(Header::Status(status))
} }
_ => { _ => {
Err(DecoderError::InvalidPseudoheader) Err(DecoderError::InvalidPseudoheader)
@@ -67,91 +67,107 @@ impl Entry {
let name = try!(HeaderName::from_bytes(&name)); let name = try!(HeaderName::from_bytes(&name));
let value = try!(HeaderValue::try_from_slice(&value)); let value = try!(HeaderValue::try_from_slice(&value));
Ok(Entry::Header { name: name, value: value }) Ok(Header::Field { name: name, value: value })
} }
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match *self { match *self {
Entry::Header { ref name, ref value } => { Header::Field { ref name, ref value } => {
len(name, value) len(name, value)
} }
Entry::Authority(ref v) => { Header::Authority(ref v) => {
32 + 10 + v.len() 32 + 10 + v.len()
} }
Entry::Method(ref v) => { Header::Method(ref v) => {
32 + 7 + v.as_ref().len() 32 + 7 + v.as_ref().len()
} }
Entry::Scheme(ref v) => { Header::Scheme(ref v) => {
32 + 7 + v.len() 32 + 7 + v.len()
} }
Entry::Path(ref v) => { Header::Path(ref v) => {
32 + 5 + v.len() 32 + 5 + v.len()
} }
Entry::Status(ref v) => { Header::Status(ref v) => {
32 + 7 + 3 32 + 7 + 3
} }
} }
} }
pub fn key(&self) -> Key { /// Returns the header name
pub fn name(&self) -> Name {
match *self { match *self {
Entry::Header { ref name, .. } => Key::Header(name), Header::Field { ref name, .. } => Name::Field(name),
Entry::Authority(..) => Key::Authority, Header::Authority(..) => Name::Authority,
Entry::Method(..) => Key::Method, Header::Method(..) => Name::Method,
Entry::Scheme(..) => Key::Scheme, Header::Scheme(..) => Name::Scheme,
Entry::Path(..) => Key::Path, Header::Path(..) => Name::Path,
Entry::Status(..) => Key::Status, Header::Status(..) => Name::Status,
} }
} }
pub fn value_eq(&self, other: &Entry) -> bool { pub fn value_slice(&self) -> &[u8] {
match *self { match *self {
Entry::Header { ref value, .. } => { Header::Field { ref value, .. } => value.as_ref(),
Header::Authority(ref v) => v.as_ref(),
Header::Method(ref v) => v.as_ref().as_ref(),
Header::Scheme(ref v) => v.as_ref(),
Header::Path(ref v) => v.as_ref(),
Header::Status(ref v) => v.as_str().as_ref(),
}
}
pub fn value_eq(&self, other: &Header) -> bool {
match *self {
Header::Field { ref value, .. } => {
let a = value; let a = value;
match *other { match *other {
Entry::Header { ref value, .. } => a == value, Header::Field { ref value, .. } => a == value,
_ => false, _ => false,
} }
} }
Entry::Authority(ref a) => { Header::Authority(ref a) => {
match *other { match *other {
Entry::Authority(ref b) => a == b, Header::Authority(ref b) => a == b,
_ => false, _ => false,
} }
} }
Entry::Method(ref a) => { Header::Method(ref a) => {
match *other { match *other {
Entry::Method(ref b) => a == b, Header::Method(ref b) => a == b,
_ => false, _ => false,
} }
} }
Entry::Scheme(ref a) => { Header::Scheme(ref a) => {
match *other { match *other {
Entry::Scheme(ref b) => a == b, Header::Scheme(ref b) => a == b,
_ => false, _ => false,
} }
} }
Entry::Path(ref a) => { Header::Path(ref a) => {
match *other { match *other {
Entry::Path(ref b) => a == b, Header::Path(ref b) => a == b,
_ => false, _ => false,
} }
} }
Entry::Status(ref a) => { Header::Status(ref a) => {
match *other { match *other {
Entry::Status(ref b) => a == b, Header::Status(ref b) => a == b,
_ => false, _ => false,
} }
} }
} }
} }
pub fn is_sensitive(&self) -> bool {
false
}
pub fn skip_value_index(&self) -> bool { pub fn skip_value_index(&self) -> bool {
use http::header; use http::header;
match *self { match *self {
Entry::Header { ref name, .. } => { Header::Field { ref name, .. } => {
match *name { match *name {
header::AGE | header::AGE |
header::AUTHORIZATION | header::AUTHORIZATION |
@@ -165,40 +181,51 @@ impl Entry {
_ => false, _ => false,
} }
} }
Entry::Path(..) => true, Header::Path(..) => true,
_ => false, _ => false,
} }
} }
} }
impl<'a> Key<'a> { impl<'a> Name<'a> {
pub fn into_entry(self, value: Bytes) -> Result<Entry, DecoderError> { pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
match self { match self {
Key::Header(name) => { Name::Field(name) => {
Ok(Entry::Header { Ok(Header::Field {
name: name.clone(), name: name.clone(),
value: try!(HeaderValue::try_from_slice(&*value)), value: try!(HeaderValue::try_from_slice(&*value)),
}) })
} }
Key::Authority => { Name::Authority => {
Ok(Entry::Authority(try!(ByteStr::from_utf8(value)))) Ok(Header::Authority(try!(ByteStr::from_utf8(value))))
} }
Key::Method => { Name::Method => {
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value)))) Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
} }
Key::Scheme => { Name::Scheme => {
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value)))) Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
} }
Key::Path => { Name::Path => {
Ok(Entry::Path(try!(ByteStr::from_utf8(value)))) Ok(Header::Path(try!(ByteStr::from_utf8(value))))
} }
Key::Status => { Name::Status => {
match StatusCode::from_slice(&value) { match StatusCode::from_slice(&value) {
Ok(status) => Ok(Entry::Status(status)), Ok(status) => Ok(Header::Status(status)),
// TODO: better error handling // TODO: better error handling
Err(_) => Err(DecoderError::InvalidStatusCode), Err(_) => Err(DecoderError::InvalidStatusCode),
} }
} }
} }
} }
pub fn as_slice(&self) -> &[u8] {
match *self {
Name::Field(ref name) => name.as_ref(),
Name::Authority => b":authority",
Name::Method => b":method",
Name::Scheme => b":scheme",
Name::Path => b":path",
Name::Status => b":status",
}
}
} }

View File

@@ -42,8 +42,7 @@ pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
Ok(dst) Ok(dst)
} }
// To avoid panics, the destination buffer must have src.len() remaining // TODO: return error when there is not enough room to encode the value
// capacity.
pub fn encode<B: BufMut>(src: &[u8], dst: &mut B) { pub fn encode<B: BufMut>(src: &[u8], dst: &mut B) {
let mut bits: u64 = 0; let mut bits: u64 = 0;
let mut bits_left = 40; let mut bits_left = 40;

View File

@@ -1,6 +1,6 @@
mod encoder; mod encoder;
mod decoder; mod decoder;
mod entry; mod header;
mod huffman; mod huffman;
mod table; mod table;
@@ -8,5 +8,5 @@ mod table;
mod test; mod test;
pub use self::encoder::Encoder; pub use self::encoder::Encoder;
pub use self::entry::{Entry, Key}; pub use self::header::Header;
pub use self::decoder::{Decoder, DecoderError}; pub use self::decoder::{Decoder, DecoderError};

View File

@@ -1,4 +1,4 @@
use super::Entry; use super::Header;
use fnv::FnvHasher; use fnv::FnvHasher;
use http::method; use http::method;
@@ -22,22 +22,25 @@ pub struct Table {
#[derive(Debug)] #[derive(Debug)]
pub enum Index<'a> { pub enum Index<'a> {
// The entry is already fully indexed // The header is already fully indexed
Indexed(usize), Indexed(usize, Header),
// The name is indexed, but not the value // The name is indexed, but not the value
Name(usize, Entry), Name(usize, Header),
// The full entry has been inserted into the table. // The full header has been inserted into the table.
Inserted(&'a Entry), Inserted(&'a Header),
// The entry is not indexed by this table // Only the value has been inserted
NotIndexed(Entry), InsertedValue(usize, Header),
// The header is not indexed by this table
NotIndexed(Header),
} }
struct Slot { struct Slot {
hash: HashValue, hash: HashValue,
entry: Entry, header: Header,
next: Option<usize>, next: Option<usize>,
} }
@@ -94,33 +97,33 @@ impl Table {
usable_capacity(self.indices.len()) usable_capacity(self.indices.len())
} }
/// Index the entry in the HPACK table. /// Index the header in the HPACK table.
pub fn index(&mut self, entry: Entry) -> Index { pub fn index(&mut self, header: Header) -> Index {
// Check the static table // Check the static table
let statik = index_static(&entry); let statik = index_static(&header);
// Don't index certain headers. This logic is borrowed from nghttp2. // Don't index certain headers. This logic is borrowed from nghttp2.
if entry.skip_value_index() { if header.skip_value_index() {
return Index::new(statik, entry); return Index::new(statik, header);
} }
// If the header is already indexed by the static table, return that // If the header is already indexed by the static table, return that
if let Some((n, true)) = statik { if let Some((n, true)) = statik {
return Index::Indexed(n); return Index::Indexed(n, header);
} }
// Don't index large headers // Don't index large headers
if entry.len() * 4 > self.max_size * 3 { if header.len() * 4 > self.max_size * 3 {
return Index::new(statik, entry); return Index::new(statik, header);
} }
self.index_dynamic(entry, statik) self.index_dynamic(header, statik)
} }
fn index_dynamic(&mut self, entry: Entry, statik: Option<(usize, bool)>) -> Index { fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
self.reserve_one(); self.reserve_one();
let hash = hash_entry(&entry); let hash = hash_header(&header);
let desired_pos = desired_pos(self.mask, hash); let desired_pos = desired_pos(self.mask, hash);
let mut probe = desired_pos; let mut probe = desired_pos;
@@ -135,20 +138,20 @@ impl Table {
if their_dist < dist { if their_dist < dist {
// Index robinhood // Index robinhood
return self.index_vacant(entry, hash, desired_pos, probe); return self.index_vacant(header, hash, desired_pos, probe);
} else if pos.hash == hash && self.slots[pos.index].entry.key() == entry.key() { } else if pos.hash == hash && self.slots[pos.index].header.name() == header.name() {
// Matching key, check values // Matching name, check values
return self.index_occupied(entry, pos.index, statik); return self.index_occupied(header, pos.index, statik);
} }
} else { } else {
return self.index_vacant(entry, hash, desired_pos, probe); return self.index_vacant(header, hash, desired_pos, probe);
} }
dist += 1; dist += 1;
}); });
} }
fn index_occupied(&mut self, entry: Entry, mut index: usize, statik: Option<(usize, bool)>) fn index_occupied(&mut self, header: Header, mut index: usize, statik: Option<(usize, bool)>)
-> Index -> Index
{ {
// There already is a match for the given header name. Check if a value // There already is a match for the given header name. Check if a value
@@ -158,13 +161,13 @@ impl Table {
} }
fn index_vacant(&mut self, fn index_vacant(&mut self,
entry: Entry, header: Header,
hash: HashValue, hash: HashValue,
desired: usize, desired: usize,
probe: usize) probe: usize)
-> Index -> Index
{ {
if self.maybe_evict(entry.len()) { if self.maybe_evict(header.len()) {
// Maybe step back // Maybe step back
unimplemented!(); unimplemented!();
} }
@@ -175,7 +178,7 @@ impl Table {
self.slots.push_back(Slot { self.slots.push_back(Slot {
hash: hash, hash: hash,
entry: entry, header: header,
next: None, next: None,
}); });
@@ -199,7 +202,7 @@ impl Table {
}); });
} }
Index::Inserted(&self.slots[slot_idx].entry) Index::Inserted(&self.slots[slot_idx].header)
} }
fn maybe_evict(&mut self, len: usize) -> bool { fn maybe_evict(&mut self, len: usize) -> bool {
@@ -217,7 +220,7 @@ impl Table {
fn evict(&mut self) { fn evict(&mut self) {
debug_assert!(!self.slots.is_empty()); debug_assert!(!self.slots.is_empty());
// Remove the entry // Remove the header
let slot = self.slots.pop_front().unwrap(); let slot = self.slots.pop_front().unwrap();
let mut probe = desired_pos(self.mask, slot.hash); let mut probe = desired_pos(self.mask, slot.hash);
@@ -242,7 +245,7 @@ impl Table {
self.evicted = self.evicted.wrapping_add(1); self.evicted = self.evicted.wrapping_add(1);
} }
// Shifts all indices that were displaced by the entry that has just been // Shifts all indices that were displaced by the header that has just been
// removed. // removed.
fn remove_phase_two(&mut self, probe: usize) { fn remove_phase_two(&mut self, probe: usize) {
let mut last_probe = probe; let mut last_probe = probe;
@@ -326,10 +329,10 @@ impl Table {
} }
impl<'a> Index<'a> { impl<'a> Index<'a> {
fn new(v: Option<(usize, bool)>, e: Entry) -> Index<'a> { fn new(v: Option<(usize, bool)>, e: Header) -> Index<'a> {
match v { match v {
None => Index::NotIndexed(e), None => Index::NotIndexed(e),
Some((n, true)) => Index::Indexed(n), Some((n, true)) => Index::Indexed(n, e),
Some((n, false)) => Index::Name(n, e), Some((n, false)) => Index::Name(n, e),
} }
} }
@@ -355,19 +358,19 @@ fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
current.wrapping_sub(desired_pos(mask, hash)) & mask as usize current.wrapping_sub(desired_pos(mask, hash)) & mask as usize
} }
fn hash_entry(entry: &Entry) -> HashValue { fn hash_header(header: &Header) -> HashValue {
const MASK: u64 = (MAX_SIZE as u64) - 1; const MASK: u64 = (MAX_SIZE as u64) - 1;
let mut h = FnvHasher::default(); let mut h = FnvHasher::default();
entry.key().hash(&mut h); header.name().hash(&mut h);
HashValue((h.finish() & MASK) as usize) HashValue((h.finish() & MASK) as usize)
} }
/// Checks the static table for the entry. If found, returns the index and a /// Checks the static table for the header. If found, returns the index and a
/// boolean representing if the value matched as well. /// boolean representing if the value matched as well.
fn index_static(entry: &Entry) -> Option<(usize, bool)> { fn index_static(header: &Header) -> Option<(usize, bool)> {
match *entry { match *header {
Entry::Header { ref name, ref value } => { Header::Field { ref name, ref value } => {
match *name { match *name {
header::ACCEPT_CHARSET => Some((15, false)), header::ACCEPT_CHARSET => Some((15, false)),
header::ACCEPT_ENCODING => { header::ACCEPT_ENCODING => {
@@ -425,29 +428,29 @@ fn index_static(entry: &Entry) -> Option<(usize, bool)> {
_ => None, _ => None,
} }
} }
Entry::Authority(ref v) => Some((1, false)), Header::Authority(ref v) => Some((1, false)),
Entry::Method(ref v) => { Header::Method(ref v) => {
match *v { match *v {
method::GET => Some((2, true)), method::GET => Some((2, true)),
method::POST => Some((3, true)), method::POST => Some((3, true)),
_ => Some((2, false)), _ => Some((2, false)),
} }
} }
Entry::Scheme(ref v) => { Header::Scheme(ref v) => {
match &**v { match &**v {
"http" => Some((6, true)), "http" => Some((6, true)),
"https" => Some((7, true)), "https" => Some((7, true)),
_ => Some((6, false)), _ => Some((6, false)),
} }
} }
Entry::Path(ref v) => { Header::Path(ref v) => {
match &**v { match &**v {
"/" => Some((4, true)), "/" => Some((4, true)),
"/index.html" => Some((5, true)), "/index.html" => Some((5, true)),
_ => Some((4, false)), _ => Some((4, false)),
} }
} }
Entry::Status(ref v) => { Header::Status(ref v) => {
match u16::from(*v) { match u16::from(*v) {
200 => Some((8, true)), 200 => Some((8, true)),
204 => Some((9, true)), 204 => Some((9, true)),