More encoding work
This commit is contained in:
@@ -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<Entry>,
|
||||
entries: VecDeque<Header>,
|
||||
size: usize,
|
||||
max_size: usize,
|
||||
}
|
||||
@@ -152,7 +152,7 @@ impl Decoder {
|
||||
|
||||
/// Decodes the headers found in the given buffer.
|
||||
pub fn decode<F>(&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<Entry, DecoderError>
|
||||
-> Result<Header, DecoderError>
|
||||
{
|
||||
let index = try!(decode_int(buf, 7));
|
||||
self.table.get(index)
|
||||
}
|
||||
|
||||
fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool)
|
||||
-> Result<Entry, DecoderError>
|
||||
-> Result<Header, DecoderError>
|
||||
{
|
||||
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<Entry, DecoderError> {
|
||||
pub fn get(&self, index: usize) -> Result<Header, DecoderError> {
|
||||
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<status::FromStrError> 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(""),
|
||||
},
|
||||
|
||||
@@ -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<Item=Entry>,
|
||||
where I: IntoIterator<Item=Header>,
|
||||
{
|
||||
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<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
|
||||
}
|
||||
|
||||
@@ -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<Entry, DecoderError> {
|
||||
impl Header {
|
||||
pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
|
||||
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<Entry, DecoderError> {
|
||||
impl<'a> Name<'a> {
|
||||
pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
|
||||
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",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,7 @@ pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
||||
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<B: BufMut>(src: &[u8], dst: &mut B) {
|
||||
let mut bits: u64 = 0;
|
||||
let mut bits_left = 40;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<usize>,
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user