From a7da819e457f67dc6d6b7180b0336759dad053eb Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 2 Jun 2017 14:33:09 -0700 Subject: [PATCH] More encoding work --- src/hpack/decoder.rs | 142 +++++++++++++++--------------- src/hpack/encoder.rs | 123 ++++++++++++++++++++++---- src/hpack/{entry.rs => header.rs} | 139 +++++++++++++++++------------ src/hpack/huffman/mod.rs | 3 +- src/hpack/mod.rs | 4 +- src/hpack/table.rs | 93 +++++++++---------- 6 files changed, 310 insertions(+), 194 deletions(-) rename src/hpack/{entry.rs => header.rs} (52%) diff --git a/src/hpack/decoder.rs b/src/hpack/decoder.rs index 6e6727f..aa1f548 100644 --- a/src/hpack/decoder.rs +++ b/src/hpack/decoder.rs @@ -1,4 +1,4 @@ -use super::{huffman, Entry, Key}; +use super::{huffman, header as h2_header, Header}; use util::byte_str::FromUtf8Error; use http::{method, header, status, StatusCode, Method}; @@ -124,7 +124,7 @@ enum Representation { } struct Table { - entries: VecDeque, + entries: VecDeque
, size: usize, max_size: usize, } @@ -152,7 +152,7 @@ impl Decoder { /// Decodes the headers found in the given buffer. pub fn decode(&mut self, src: &Bytes, mut f: F) -> Result<(), DecoderError> - where F: FnMut(Entry) + where F: FnMut(Header) { use self::Representation::*; @@ -227,14 +227,14 @@ impl Decoder { } fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) - -> Result + -> Result { let index = try!(decode_int(buf, 7)); self.table.get(index) } fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool) - -> Result + -> Result { let prefix = if index { 6 @@ -251,12 +251,12 @@ impl Decoder { let name = try!(decode_string(buf)); let value = try!(decode_string(buf)); - Entry::new(name, value) + Header::new(name, value) } else { let e = try!(self.table.get(table_idx)); 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.] /// (http://http2.github.io/http2-spec/compression.html#index.address.space) - pub fn get(&self, index: usize) -> Result { + pub fn get(&self, index: usize) -> Result { if index == 0 { 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(); self.reserve(len); @@ -518,211 +518,211 @@ impl From for DecoderError { } /// 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::header::HeaderValue; use util::byte_str::ByteStr; match idx { - 1 => Entry::Authority(ByteStr::from_static("")), - 2 => Entry::Method(method::GET), - 3 => Entry::Method(method::POST), - 4 => Entry::Path(ByteStr::from_static("/")), - 5 => Entry::Path(ByteStr::from_static("/index.html")), - 6 => Entry::Scheme(ByteStr::from_static("http")), - 7 => Entry::Scheme(ByteStr::from_static("https")), - 8 => Entry::Status(status::OK), - 9 => Entry::Status(status::NO_CONTENT), - 10 => Entry::Status(status::PARTIAL_CONTENT), - 11 => Entry::Status(status::NOT_MODIFIED), - 12 => Entry::Status(status::BAD_REQUEST), - 13 => Entry::Status(status::NOT_FOUND), - 14 => Entry::Status(status::INTERNAL_SERVER_ERROR), - 15 => Entry::Header { + 1 => Header::Authority(ByteStr::from_static("")), + 2 => Header::Method(method::GET), + 3 => Header::Method(method::POST), + 4 => Header::Path(ByteStr::from_static("/")), + 5 => Header::Path(ByteStr::from_static("/index.html")), + 6 => Header::Scheme(ByteStr::from_static("http")), + 7 => Header::Scheme(ByteStr::from_static("https")), + 8 => Header::Status(status::OK), + 9 => Header::Status(status::NO_CONTENT), + 10 => Header::Status(status::PARTIAL_CONTENT), + 11 => Header::Status(status::NOT_MODIFIED), + 12 => Header::Status(status::BAD_REQUEST), + 13 => Header::Status(status::NOT_FOUND), + 14 => Header::Status(status::INTERNAL_SERVER_ERROR), + 15 => Header::Field { name: header::ACCEPT_CHARSET, value: HeaderValue::from_static(""), }, - 16 => Entry::Header { + 16 => Header::Field { name: header::ACCEPT_ENCODING, value: HeaderValue::from_static("gzip, deflate"), }, - 17 => Entry::Header { + 17 => Header::Field { name: header::ACCEPT_LANGUAGE, value: HeaderValue::from_static(""), }, - 18 => Entry::Header { + 18 => Header::Field { name: header::ACCEPT_RANGES, value: HeaderValue::from_static(""), }, - 19 => Entry::Header { + 19 => Header::Field { name: header::ACCEPT, value: HeaderValue::from_static(""), }, - 20 => Entry::Header { + 20 => Header::Field { name: header::ACCESS_CONTROL_ALLOW_ORIGIN, value: HeaderValue::from_static(""), }, - 21 => Entry::Header { + 21 => Header::Field { name: header::AGE, value: HeaderValue::from_static(""), }, - 22 => Entry::Header { + 22 => Header::Field { name: header::ALLOW, value: HeaderValue::from_static(""), }, - 23 => Entry::Header { + 23 => Header::Field { name: header::AUTHORIZATION, value: HeaderValue::from_static(""), }, - 24 => Entry::Header { + 24 => Header::Field { name: header::CACHE_CONTROL, value: HeaderValue::from_static(""), }, - 25 => Entry::Header { + 25 => Header::Field { name: header::CONTENT_DISPOSITION, value: HeaderValue::from_static(""), }, - 26 => Entry::Header { + 26 => Header::Field { name: header::CONTENT_ENCODING, value: HeaderValue::from_static(""), }, - 27 => Entry::Header { + 27 => Header::Field { name: header::CONTENT_LANGUAGE, value: HeaderValue::from_static(""), }, - 28 => Entry::Header { + 28 => Header::Field { name: header::CONTENT_LENGTH, value: HeaderValue::from_static(""), }, - 29 => Entry::Header { + 29 => Header::Field { name: header::CONTENT_LOCATION, value: HeaderValue::from_static(""), }, - 30 => Entry::Header { + 30 => Header::Field { name: header::CONTENT_RANGE, value: HeaderValue::from_static(""), }, - 31 => Entry::Header { + 31 => Header::Field { name: header::CONTENT_TYPE, value: HeaderValue::from_static(""), }, - 32 => Entry::Header { + 32 => Header::Field { name: header::COOKIE, value: HeaderValue::from_static(""), }, - 33 => Entry::Header { + 33 => Header::Field { name: header::DATE, value: HeaderValue::from_static(""), }, - 34 => Entry::Header { + 34 => Header::Field { name: header::ETAG, value: HeaderValue::from_static(""), }, - 35 => Entry::Header { + 35 => Header::Field { name: header::EXPECT, value: HeaderValue::from_static(""), }, - 36 => Entry::Header { + 36 => Header::Field { name: header::EXPIRES, value: HeaderValue::from_static(""), }, - 37 => Entry::Header { + 37 => Header::Field { name: header::FROM, value: HeaderValue::from_static(""), }, - 38 => Entry::Header { + 38 => Header::Field { name: header::HOST, value: HeaderValue::from_static(""), }, - 39 => Entry::Header { + 39 => Header::Field { name: header::IF_MATCH, value: HeaderValue::from_static(""), }, - 40 => Entry::Header { + 40 => Header::Field { name: header::IF_MODIFIED_SINCE, value: HeaderValue::from_static(""), }, - 41 => Entry::Header { + 41 => Header::Field { name: header::IF_NONE_MATCH, value: HeaderValue::from_static(""), }, - 42 => Entry::Header { + 42 => Header::Field { name: header::IF_RANGE, value: HeaderValue::from_static(""), }, - 43 => Entry::Header { + 43 => Header::Field { name: header::IF_UNMODIFIED_SINCE, value: HeaderValue::from_static(""), }, - 44 => Entry::Header { + 44 => Header::Field { name: header::LAST_MODIFIED, value: HeaderValue::from_static(""), }, - 45 => Entry::Header { + 45 => Header::Field { name: header::LINK, value: HeaderValue::from_static(""), }, - 46 => Entry::Header { + 46 => Header::Field { name: header::LOCATION, value: HeaderValue::from_static(""), }, - 47 => Entry::Header { + 47 => Header::Field { name: header::MAX_FORWARDS, value: HeaderValue::from_static(""), }, - 48 => Entry::Header { + 48 => Header::Field { name: header::PROXY_AUTHENTICATE, value: HeaderValue::from_static(""), }, - 49 => Entry::Header { + 49 => Header::Field { name: header::PROXY_AUTHORIZATION, value: HeaderValue::from_static(""), }, - 50 => Entry::Header { + 50 => Header::Field { name: header::RANGE, value: HeaderValue::from_static(""), }, - 51 => Entry::Header { + 51 => Header::Field { name: header::REFERER, value: HeaderValue::from_static(""), }, - 52 => Entry::Header { + 52 => Header::Field { name: header::REFRESH, value: HeaderValue::from_static(""), }, - 53 => Entry::Header { + 53 => Header::Field { name: header::RETRY_AFTER, value: HeaderValue::from_static(""), }, - 54 => Entry::Header { + 54 => Header::Field { name: header::SERVER, value: HeaderValue::from_static(""), }, - 55 => Entry::Header { + 55 => Header::Field { name: header::SET_COOKIE, value: HeaderValue::from_static(""), }, - 56 => Entry::Header { + 56 => Header::Field { name: header::STRICT_TRANSPORT_SECURITY, value: HeaderValue::from_static(""), }, - 57 => Entry::Header { + 57 => Header::Field { name: header::TRANSFER_ENCODING, value: HeaderValue::from_static(""), }, - 58 => Entry::Header { + 58 => Header::Field { name: header::USER_AGENT, value: HeaderValue::from_static(""), }, - 59 => Entry::Header { + 59 => Header::Field { name: header::VARY, value: HeaderValue::from_static(""), }, - 60 => Entry::Header { + 60 => Header::Field { name: header::VIA, value: HeaderValue::from_static(""), }, - 61 => Entry::Header { + 61 => Header::Field { name: header::WWW_AUTHENTICATE, value: HeaderValue::from_static(""), }, diff --git a/src/hpack/encoder.rs b/src/hpack/encoder.rs index 9f75f3a..41c0dd1 100644 --- a/src/hpack/encoder.rs +++ b/src/hpack/encoder.rs @@ -1,8 +1,8 @@ -use super::entry; -use super::table::Table; +use super::{huffman, Header}; +use super::table::{Table, Index}; use http::header::{HeaderName, HeaderValue}; -use bytes::BytesMut; +use bytes::{BytesMut, BufMut}; pub struct Encoder { table: Table, @@ -24,7 +24,7 @@ impl Encoder { } pub fn encode<'a, I>(&mut self, headers: I, dst: &mut BytesMut) -> Result<(), EncoderError> - where I: IntoIterator, + where I: IntoIterator, { if let Some(max_size_update) = self.max_size_update.take() { // Write size update frame @@ -38,31 +38,118 @@ impl Encoder { Ok(()) } - fn encode_header(&mut self, entry: Entry, dst: &mut BytesMut) + fn encode_header(&mut self, header: Header, dst: &mut BytesMut) -> Result<(), EncoderError> { - if is_sensitive(&e) { + if header.is_sensitive() { unimplemented!(); } - /* - match self.table.entry(name, val) { - Entry::Indexed(idx) => { - unimplemented!(); + match self.table.index(header) { + Index::Indexed(idx, _header) => { + encode_int(idx, 7, 0x80, dst); } - Entry::Name(idx) => { - unimplemented!(); + Index::Name(idx, header) => { + encode_int(idx, 4, 0, dst); + encode_str(header.value_slice(), dst); } - Entry::NotIndexed => { - unimplemented!(); + Index::Inserted(header) => { + 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 { - false +fn encode_str(val: &[u8], dst: &mut BytesMut) { + 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( + 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 } diff --git a/src/hpack/entry.rs b/src/hpack/header.rs similarity index 52% rename from src/hpack/entry.rs rename to src/hpack/header.rs index 4e76f90..5a87892 100644 --- a/src/hpack/entry.rs +++ b/src/hpack/header.rs @@ -5,10 +5,10 @@ use http::{Method, StatusCode}; use http::header::{HeaderName, HeaderValue}; use bytes::Bytes; -/// HPack table entry +/// HTTP/2.0 Header #[derive(Debug, Clone)] -pub enum Entry { - Header { +pub enum Header { + Field { name: HeaderName, value: HeaderValue, }, @@ -19,10 +19,10 @@ pub enum Entry { Status(StatusCode), } -/// The name component of an Entry +/// The header field name #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum Key<'a> { - Header(&'a HeaderName), +pub enum Name<'a> { + Field(&'a HeaderName), Authority, Method, Scheme, @@ -35,29 +35,29 @@ pub fn len(name: &HeaderName, value: &HeaderValue) -> usize { 32 + n.len() + value.len() } -impl Entry { - pub fn new(name: Bytes, value: Bytes) -> Result { +impl Header { + pub fn new(name: Bytes, value: Bytes) -> Result { if name[0] == b':' { match &name[1..] { b"authority" => { let value = try!(ByteStr::from_utf8(value)); - Ok(Entry::Authority(value)) + Ok(Header::Authority(value)) } b"method" => { let method = try!(Method::from_bytes(&value)); - Ok(Entry::Method(method)) + Ok(Header::Method(method)) } b"scheme" => { let value = try!(ByteStr::from_utf8(value)); - Ok(Entry::Scheme(value)) + Ok(Header::Scheme(value)) } b"path" => { let value = try!(ByteStr::from_utf8(value)); - Ok(Entry::Path(value)) + Ok(Header::Path(value)) } b"status" => { let status = try!(StatusCode::from_slice(&value)); - Ok(Entry::Status(status)) + Ok(Header::Status(status)) } _ => { Err(DecoderError::InvalidPseudoheader) @@ -67,91 +67,107 @@ impl Entry { let name = try!(HeaderName::from_bytes(&name)); 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 { match *self { - Entry::Header { ref name, ref value } => { + Header::Field { ref name, ref value } => { len(name, value) } - Entry::Authority(ref v) => { + Header::Authority(ref v) => { 32 + 10 + v.len() } - Entry::Method(ref v) => { + Header::Method(ref v) => { 32 + 7 + v.as_ref().len() } - Entry::Scheme(ref v) => { + Header::Scheme(ref v) => { 32 + 7 + v.len() } - Entry::Path(ref v) => { + Header::Path(ref v) => { 32 + 5 + v.len() } - Entry::Status(ref v) => { + Header::Status(ref v) => { 32 + 7 + 3 } } } - pub fn key(&self) -> Key { + /// Returns the header name + pub fn name(&self) -> Name { 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, + Header::Field { ref name, .. } => Name::Field(name), + Header::Authority(..) => Name::Authority, + Header::Method(..) => Name::Method, + Header::Scheme(..) => Name::Scheme, + Header::Path(..) => Name::Path, + Header::Status(..) => Name::Status, } } - pub fn value_eq(&self, other: &Entry) -> bool { + pub fn value_slice(&self) -> &[u8] { 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; match *other { - Entry::Header { ref value, .. } => a == value, + Header::Field { ref value, .. } => a == value, _ => false, } } - Entry::Authority(ref a) => { + Header::Authority(ref a) => { match *other { - Entry::Authority(ref b) => a == b, + Header::Authority(ref b) => a == b, _ => false, } } - Entry::Method(ref a) => { + Header::Method(ref a) => { match *other { - Entry::Method(ref b) => a == b, + Header::Method(ref b) => a == b, _ => false, } } - Entry::Scheme(ref a) => { + Header::Scheme(ref a) => { match *other { - Entry::Scheme(ref b) => a == b, + Header::Scheme(ref b) => a == b, _ => false, } } - Entry::Path(ref a) => { + Header::Path(ref a) => { match *other { - Entry::Path(ref b) => a == b, + Header::Path(ref b) => a == b, _ => false, } } - Entry::Status(ref a) => { + Header::Status(ref a) => { match *other { - Entry::Status(ref b) => a == b, + Header::Status(ref b) => a == b, _ => false, } } } } + pub fn is_sensitive(&self) -> bool { + false + } + pub fn skip_value_index(&self) -> bool { use http::header; match *self { - Entry::Header { ref name, .. } => { + Header::Field { ref name, .. } => { match *name { header::AGE | header::AUTHORIZATION | @@ -165,40 +181,51 @@ impl Entry { _ => false, } } - Entry::Path(..) => true, + Header::Path(..) => true, _ => false, } } } -impl<'a> Key<'a> { - pub fn into_entry(self, value: Bytes) -> Result { +impl<'a> Name<'a> { + pub fn into_entry(self, value: Bytes) -> Result { match self { - Key::Header(name) => { - Ok(Entry::Header { + Name::Field(name) => { + Ok(Header::Field { name: name.clone(), value: try!(HeaderValue::try_from_slice(&*value)), }) } - Key::Authority => { - Ok(Entry::Authority(try!(ByteStr::from_utf8(value)))) + Name::Authority => { + Ok(Header::Authority(try!(ByteStr::from_utf8(value)))) } - Key::Method => { - Ok(Entry::Scheme(try!(ByteStr::from_utf8(value)))) + Name::Method => { + Ok(Header::Scheme(try!(ByteStr::from_utf8(value)))) } - Key::Scheme => { - Ok(Entry::Scheme(try!(ByteStr::from_utf8(value)))) + Name::Scheme => { + Ok(Header::Scheme(try!(ByteStr::from_utf8(value)))) } - Key::Path => { - Ok(Entry::Path(try!(ByteStr::from_utf8(value)))) + Name::Path => { + Ok(Header::Path(try!(ByteStr::from_utf8(value)))) } - Key::Status => { + Name::Status => { match StatusCode::from_slice(&value) { - Ok(status) => Ok(Entry::Status(status)), + Ok(status) => Ok(Header::Status(status)), // TODO: better error handling 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", + } + } } diff --git a/src/hpack/huffman/mod.rs b/src/hpack/huffman/mod.rs index 2c0ddb4..430b155 100644 --- a/src/hpack/huffman/mod.rs +++ b/src/hpack/huffman/mod.rs @@ -42,8 +42,7 @@ pub fn decode(src: &[u8]) -> Result { Ok(dst) } -// To avoid panics, the destination buffer must have src.len() remaining -// capacity. +// TODO: return error when there is not enough room to encode the value pub fn encode(src: &[u8], dst: &mut B) { let mut bits: u64 = 0; let mut bits_left = 40; diff --git a/src/hpack/mod.rs b/src/hpack/mod.rs index 4a66774..16aae13 100644 --- a/src/hpack/mod.rs +++ b/src/hpack/mod.rs @@ -1,6 +1,6 @@ mod encoder; mod decoder; -mod entry; +mod header; mod huffman; mod table; @@ -8,5 +8,5 @@ mod table; mod test; pub use self::encoder::Encoder; -pub use self::entry::{Entry, Key}; +pub use self::header::Header; pub use self::decoder::{Decoder, DecoderError}; diff --git a/src/hpack/table.rs b/src/hpack/table.rs index 9024f08..99cc62e 100644 --- a/src/hpack/table.rs +++ b/src/hpack/table.rs @@ -1,4 +1,4 @@ -use super::Entry; +use super::Header; use fnv::FnvHasher; use http::method; @@ -22,22 +22,25 @@ pub struct Table { #[derive(Debug)] pub enum Index<'a> { - // The entry is already fully indexed - Indexed(usize), + // The header is already fully indexed + Indexed(usize, Header), // The name is indexed, but not the value - Name(usize, Entry), + Name(usize, Header), - // The full entry has been inserted into the table. - Inserted(&'a Entry), + // The full header has been inserted into the table. + Inserted(&'a Header), - // The entry is not indexed by this table - NotIndexed(Entry), + // Only the value has been inserted + InsertedValue(usize, Header), + + // The header is not indexed by this table + NotIndexed(Header), } struct Slot { hash: HashValue, - entry: Entry, + header: Header, next: Option, } @@ -94,33 +97,33 @@ impl Table { usable_capacity(self.indices.len()) } - /// Index the entry in the HPACK table. - pub fn index(&mut self, entry: Entry) -> Index { + /// Index the header in the HPACK table. + pub fn index(&mut self, header: Header) -> Index { // 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. - if entry.skip_value_index() { - return Index::new(statik, entry); + if header.skip_value_index() { + return Index::new(statik, header); } // If the header is already indexed by the static table, return that if let Some((n, true)) = statik { - return Index::Indexed(n); + return Index::Indexed(n, header); } // Don't index large headers - if entry.len() * 4 > self.max_size * 3 { - return Index::new(statik, entry); + if header.len() * 4 > self.max_size * 3 { + 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(); - let hash = hash_entry(&entry); + let hash = hash_header(&header); let desired_pos = desired_pos(self.mask, hash); let mut probe = desired_pos; @@ -135,20 +138,20 @@ impl Table { if their_dist < dist { // Index robinhood - return self.index_vacant(entry, hash, desired_pos, probe); - } else if pos.hash == hash && self.slots[pos.index].entry.key() == entry.key() { - // Matching key, check values - return self.index_occupied(entry, pos.index, statik); + return self.index_vacant(header, hash, desired_pos, probe); + } else if pos.hash == hash && self.slots[pos.index].header.name() == header.name() { + // Matching name, check values + return self.index_occupied(header, pos.index, statik); } } else { - return self.index_vacant(entry, hash, desired_pos, probe); + return self.index_vacant(header, hash, desired_pos, probe); } 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 { // 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, - entry: Entry, + header: Header, hash: HashValue, desired: usize, probe: usize) -> Index { - if self.maybe_evict(entry.len()) { + if self.maybe_evict(header.len()) { // Maybe step back unimplemented!(); } @@ -175,7 +178,7 @@ impl Table { self.slots.push_back(Slot { hash: hash, - entry: entry, + header: header, 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 { @@ -217,7 +220,7 @@ impl Table { fn evict(&mut self) { debug_assert!(!self.slots.is_empty()); - // Remove the entry + // Remove the header let slot = self.slots.pop_front().unwrap(); let mut probe = desired_pos(self.mask, slot.hash); @@ -242,7 +245,7 @@ impl Table { 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. fn remove_phase_two(&mut self, probe: usize) { let mut last_probe = probe; @@ -326,10 +329,10 @@ impl Table { } 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 { None => Index::NotIndexed(e), - Some((n, true)) => Index::Indexed(n), + Some((n, true)) => Index::Indexed(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 } -fn hash_entry(entry: &Entry) -> HashValue { +fn hash_header(header: &Header) -> HashValue { const MASK: u64 = (MAX_SIZE as u64) - 1; let mut h = FnvHasher::default(); - entry.key().hash(&mut h); + header.name().hash(&mut h); 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. -fn index_static(entry: &Entry) -> Option<(usize, bool)> { - match *entry { - Entry::Header { ref name, ref value } => { +fn index_static(header: &Header) -> Option<(usize, bool)> { + match *header { + Header::Field { ref name, ref value } => { match *name { header::ACCEPT_CHARSET => Some((15, false)), header::ACCEPT_ENCODING => { @@ -425,29 +428,29 @@ fn index_static(entry: &Entry) -> Option<(usize, bool)> { _ => None, } } - Entry::Authority(ref v) => Some((1, false)), - Entry::Method(ref v) => { + Header::Authority(ref v) => Some((1, false)), + Header::Method(ref v) => { match *v { method::GET => Some((2, true)), method::POST => Some((3, true)), _ => Some((2, false)), } } - Entry::Scheme(ref v) => { + Header::Scheme(ref v) => { match &**v { "http" => Some((6, true)), "https" => Some((7, true)), _ => Some((6, false)), } } - Entry::Path(ref v) => { + Header::Path(ref v) => { match &**v { "/" => Some((4, true)), "/index.html" => Some((5, true)), _ => Some((4, false)), } } - Entry::Status(ref v) => { + Header::Status(ref v) => { match u16::from(*v) { 200 => Some((8, true)), 204 => Some((9, true)),