Finish hpack decoding
This commit is contained in:
11
Cargo.toml
11
Cargo.toml
@@ -5,10 +5,9 @@ authors = ["Carl Lerche <me@carllerche.com>"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
tokio-io = { git = "https://github.com/alexcrichton/tokio-io" }
|
tokio-io = "0.1"
|
||||||
tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" }
|
tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" }
|
||||||
bytes = { git = "https://github.com/carllerche/bytes" }
|
bytes = "0.4"
|
||||||
tower = { path = "/Users/carllerche/Code/Oss/Tokio/tower/tower-http" }
|
http = { path = "/Users/carllerche/Code/Oss/Tokio/tower/http" }
|
||||||
|
log = "0.3.8"
|
||||||
[replace]
|
# tower = { path = "/Users/carllerche/Code/Oss/Tokio/tower/tower-http" }
|
||||||
"futures:0.1.10" = { git = "https://github.com/alexcrichton/futures-rs", branch = "close" }
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::{huffman, Entry, Key};
|
use super::{huffman, Entry, Key};
|
||||||
|
use util::byte_str::FromUtf8Error;
|
||||||
|
|
||||||
use tower::http::{status, HeaderName, StatusCode, Method, Str, FromUtf8Error};
|
use http::{method, header, StatusCode, Method};
|
||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
@@ -23,6 +24,8 @@ pub enum DecoderError {
|
|||||||
InvalidHuffmanCode,
|
InvalidHuffmanCode,
|
||||||
InvalidUtf8,
|
InvalidUtf8,
|
||||||
InvalidStatusCode,
|
InvalidStatusCode,
|
||||||
|
InvalidPseudoheader,
|
||||||
|
InvalidMaxDynamicSize,
|
||||||
IntegerUnderflow,
|
IntegerUnderflow,
|
||||||
IntegerOverflow,
|
IntegerOverflow,
|
||||||
StringUnderflow,
|
StringUnderflow,
|
||||||
@@ -143,6 +146,7 @@ impl Decoder {
|
|||||||
use self::Representation::*;
|
use self::Representation::*;
|
||||||
|
|
||||||
let mut buf = Cursor::new(src);
|
let mut buf = Cursor::new(src);
|
||||||
|
let mut can_resize = true;
|
||||||
|
|
||||||
while buf.has_remaining() {
|
while buf.has_remaining() {
|
||||||
// At this point we are always at the beginning of the next block
|
// At this point we are always at the beginning of the next block
|
||||||
@@ -150,23 +154,43 @@ impl Decoder {
|
|||||||
// determined from the first byte.
|
// determined from the first byte.
|
||||||
match try!(Representation::load(peek_u8(&mut buf))) {
|
match try!(Representation::load(peek_u8(&mut buf))) {
|
||||||
Indexed => {
|
Indexed => {
|
||||||
|
can_resize = false;
|
||||||
f(try!(self.decode_indexed(&mut buf)));
|
f(try!(self.decode_indexed(&mut buf)));
|
||||||
}
|
}
|
||||||
LiteralWithIndexing => {
|
LiteralWithIndexing => {
|
||||||
|
can_resize = false;
|
||||||
let entry = try!(self.decode_literal(&mut buf, true));
|
let entry = try!(self.decode_literal(&mut buf, true));
|
||||||
|
|
||||||
// TODO: Add entry to the table
|
// Insert the header into the table
|
||||||
|
self.table.insert(entry.clone());
|
||||||
|
|
||||||
f(entry);
|
f(entry);
|
||||||
}
|
}
|
||||||
LiteralWithoutIndexing => {
|
LiteralWithoutIndexing => {
|
||||||
unimplemented!();
|
can_resize = false;
|
||||||
|
let entry = try!(self.decode_literal(&mut buf, false));
|
||||||
|
f(entry);
|
||||||
}
|
}
|
||||||
LiteralNeverIndexed => {
|
LiteralNeverIndexed => {
|
||||||
unimplemented!();
|
can_resize = false;
|
||||||
|
let entry = try!(self.decode_literal(&mut buf, false));
|
||||||
|
|
||||||
|
// TODO: Track that this should never be indexed
|
||||||
|
|
||||||
|
f(entry);
|
||||||
}
|
}
|
||||||
SizeUpdate => {
|
SizeUpdate => {
|
||||||
unimplemented!();
|
let max = match self.max_size_update {
|
||||||
|
Some(max) if can_resize => max,
|
||||||
|
_ => {
|
||||||
|
// Resize is too big or other frames have been read
|
||||||
|
// before the resize.
|
||||||
|
return Err(DecoderError::InvalidMaxDynamicSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle the dynamic table size update...
|
||||||
|
try!(self.process_size_update(&mut buf, max))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,6 +198,23 @@ impl Decoder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_size_update(&mut self, buf: &mut Cursor<&Bytes>, max: usize)
|
||||||
|
-> Result<(), DecoderError>
|
||||||
|
{
|
||||||
|
let new_size = try!(decode_int(buf, 5));
|
||||||
|
|
||||||
|
if new_size > max {
|
||||||
|
return Err(DecoderError::InvalidMaxDynamicSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Decoder changed max table size from {} to {}",
|
||||||
|
self.table.size(), new_size);
|
||||||
|
|
||||||
|
self.table.set_max_size(new_size);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn decode_indexed(&self, buf: &mut Cursor<&Bytes>)
|
fn decode_indexed(&self, buf: &mut Cursor<&Bytes>)
|
||||||
-> Result<Entry, DecoderError>
|
-> Result<Entry, DecoderError>
|
||||||
{
|
{
|
||||||
@@ -197,8 +238,9 @@ impl Decoder {
|
|||||||
if table_idx == 0 {
|
if table_idx == 0 {
|
||||||
// Read the name as a literal
|
// Read the name as a literal
|
||||||
let name = try!(decode_string(buf));
|
let name = try!(decode_string(buf));
|
||||||
|
let value = try!(decode_string(buf));
|
||||||
|
|
||||||
unimplemented!();
|
Entry::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));
|
||||||
@@ -338,6 +380,9 @@ impl Table {
|
|||||||
self.max_size
|
self.max_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the entry located at the given index.
|
/// Returns the entry located at the given index.
|
||||||
///
|
///
|
||||||
@@ -374,6 +419,12 @@ impl Table {
|
|||||||
self.entries.push_front(entry);
|
self.entries.push_front(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_max_size(&mut self, size: usize) {
|
||||||
|
self.max_size = size;
|
||||||
|
// Make the table size fit within the new constraints.
|
||||||
|
self.consolidate();
|
||||||
|
}
|
||||||
|
|
||||||
fn reserve(&mut self, size: usize) {
|
fn reserve(&mut self, size: usize) {
|
||||||
debug_assert!(size <= self.max_size);
|
debug_assert!(size <= self.max_size);
|
||||||
|
|
||||||
@@ -384,6 +435,25 @@ impl Table {
|
|||||||
self.size -= last.len();
|
self.size -= last.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn consolidate(&mut self) {
|
||||||
|
while self.size > self.max_size {
|
||||||
|
{
|
||||||
|
let last = match self.entries.back() {
|
||||||
|
Some(x) => x,
|
||||||
|
None => {
|
||||||
|
// Can never happen as the size of the table must reach
|
||||||
|
// 0 by the time we've exhausted all elements.
|
||||||
|
panic!("Size of table != 0, but no headers left!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.size -= last.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.entries.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl DecoderError =====
|
// ===== impl DecoderError =====
|
||||||
@@ -394,212 +464,234 @@ impl From<FromUtf8Error> for DecoderError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<header::InvalidValueError> for DecoderError {
|
||||||
|
fn from(src: header::InvalidValueError) -> DecoderError {
|
||||||
|
// TODO: Better error?
|
||||||
|
DecoderError::InvalidUtf8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<method::FromBytesError> for DecoderError {
|
||||||
|
fn from(src: method::FromBytesError) -> DecoderError {
|
||||||
|
// TODO: Better error
|
||||||
|
DecoderError::InvalidUtf8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<header::FromBytesError> for DecoderError {
|
||||||
|
fn from(src: header::FromBytesError) -> 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 http::{status, method, header};
|
||||||
|
use http::header::HeaderValue;
|
||||||
|
use util::byte_str::ByteStr;
|
||||||
|
|
||||||
match idx {
|
match idx {
|
||||||
1 => Entry::Authority(Str::new()),
|
1 => Entry::Authority(ByteStr::from_static("")),
|
||||||
2 => Entry::Method(Method::Get),
|
2 => Entry::Method(method::GET),
|
||||||
3 => Entry::Method(Method::Post),
|
3 => Entry::Method(method::POST),
|
||||||
4 => Entry::Path(Str::from_static("/")),
|
4 => Entry::Path(ByteStr::from_static("/")),
|
||||||
5 => Entry::Path(Str::from_static("/index.html")),
|
5 => Entry::Path(ByteStr::from_static("/index.html")),
|
||||||
6 => Entry::Scheme(Str::from_static("http")),
|
6 => Entry::Scheme(ByteStr::from_static("http")),
|
||||||
7 => Entry::Scheme(Str::from_static("https")),
|
7 => Entry::Scheme(ByteStr::from_static("https")),
|
||||||
8 => Entry::Status(status::Ok),
|
8 => Entry::Status(status::OK),
|
||||||
9 => Entry::Status(status::NoContent),
|
9 => Entry::Status(status::NO_CONTENT),
|
||||||
10 => Entry::Status(status::PartialContent),
|
10 => Entry::Status(status::PARTIAL_CONTENT),
|
||||||
11 => Entry::Status(status::NotModified),
|
11 => Entry::Status(status::NOT_MODIFIED),
|
||||||
12 => Entry::Status(status::BadRequest),
|
12 => Entry::Status(status::BAD_REQUEST),
|
||||||
13 => Entry::Status(status::NotFound),
|
13 => Entry::Status(status::NOT_FOUND),
|
||||||
14 => Entry::Status(status::InternalServerError),
|
14 => Entry::Status(status::INTERNAL_SERVER_ERROR),
|
||||||
15 => Entry::Header {
|
15 => Entry::Header {
|
||||||
name: AcceptCharset.into(),
|
name: header::ACCEPT_CHARSET,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
16 => Entry::Header {
|
16 => Entry::Header {
|
||||||
name: AcceptEncoding.into(),
|
name: header::ACCEPT_ENCODING,
|
||||||
value: Str::from_static("gzip, deflate"),
|
value: HeaderValue::from_static("gzip, deflate"),
|
||||||
},
|
},
|
||||||
17 => Entry::Header {
|
17 => Entry::Header {
|
||||||
name: AcceptLanguage.into(),
|
name: header::ACCEPT_LANGUAGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
18 => Entry::Header {
|
18 => Entry::Header {
|
||||||
name: AcceptRanges.into(),
|
name: header::ACCEPT_RANGES,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
19 => Entry::Header {
|
19 => Entry::Header {
|
||||||
name: Accept.into(),
|
name: header::ACCEPT,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
20 => Entry::Header {
|
20 => Entry::Header {
|
||||||
name: AccessControlAllowOrigin.into(),
|
name: header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
21 => Entry::Header {
|
21 => Entry::Header {
|
||||||
name: Age.into(),
|
name: header::AGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
22 => Entry::Header {
|
22 => Entry::Header {
|
||||||
name: Allow.into(),
|
name: header::ALLOW,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
23 => Entry::Header {
|
23 => Entry::Header {
|
||||||
name: Authorization.into(),
|
name: header::AUTHORIZATION,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
24 => Entry::Header {
|
24 => Entry::Header {
|
||||||
name: CacheControl.into(),
|
name: header::CACHE_CONTROL,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
25 => Entry::Header {
|
25 => Entry::Header {
|
||||||
name: ContentDisposition.into(),
|
name: header::CONTENT_DISPOSITION,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
26 => Entry::Header {
|
26 => Entry::Header {
|
||||||
name: ContentEncoding.into(),
|
name: header::CONTENT_ENCODING,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
27 => Entry::Header {
|
27 => Entry::Header {
|
||||||
name: ContentLanguage.into(),
|
name: header::CONTENT_LANGUAGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
28 => Entry::Header {
|
28 => Entry::Header {
|
||||||
name: ContentLength.into(),
|
name: header::CONTENT_LENGTH,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
29 => Entry::Header {
|
29 => Entry::Header {
|
||||||
name: ContentLocation.into(),
|
name: header::CONTENT_LOCATION,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
30 => Entry::Header {
|
30 => Entry::Header {
|
||||||
name: ContentRange.into(),
|
name: header::CONTENT_RANGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
31 => Entry::Header {
|
31 => Entry::Header {
|
||||||
name: ContentType.into(),
|
name: header::CONTENT_TYPE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
32 => Entry::Header {
|
32 => Entry::Header {
|
||||||
name: Cookie.into(),
|
name: header::COOKIE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
33 => Entry::Header {
|
33 => Entry::Header {
|
||||||
name: Date.into(),
|
name: header::DATE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
34 => Entry::Header {
|
34 => Entry::Header {
|
||||||
name: Etag.into(),
|
name: header::ETAG,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
35 => Entry::Header {
|
35 => Entry::Header {
|
||||||
name: Expect.into(),
|
name: header::EXPECT,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
36 => Entry::Header {
|
36 => Entry::Header {
|
||||||
name: Expires.into(),
|
name: header::EXPIRES,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
37 => Entry::Header {
|
37 => Entry::Header {
|
||||||
name: From.into(),
|
name: header::FROM,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
38 => Entry::Header {
|
38 => Entry::Header {
|
||||||
name: Host.into(),
|
name: header::HOST,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
39 => Entry::Header {
|
39 => Entry::Header {
|
||||||
name: IfMatch.into(),
|
name: header::IF_MATCH,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
40 => Entry::Header {
|
40 => Entry::Header {
|
||||||
name: IfModifiedSince.into(),
|
name: header::IF_MODIFIED_SINCE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
41 => Entry::Header {
|
41 => Entry::Header {
|
||||||
name: IfNoneMatch.into(),
|
name: header::IF_NONE_MATCH,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
42 => Entry::Header {
|
42 => Entry::Header {
|
||||||
name: IfRange.into(),
|
name: header::IF_RANGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
43 => Entry::Header {
|
43 => Entry::Header {
|
||||||
name: IfUnmodifiedSince.into(),
|
name: header::IF_UNMODIFIED_SINCE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
44 => Entry::Header {
|
44 => Entry::Header {
|
||||||
name: LastModified.into(),
|
name: header::LAST_MODIFIED,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
45 => Entry::Header {
|
45 => Entry::Header {
|
||||||
name: Link.into(),
|
name: header::LINK,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
46 => Entry::Header {
|
46 => Entry::Header {
|
||||||
name: Location.into(),
|
name: header::LOCATION,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
47 => Entry::Header {
|
47 => Entry::Header {
|
||||||
name: MaxForwards.into(),
|
name: header::MAX_FORWARDS,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
48 => Entry::Header {
|
48 => Entry::Header {
|
||||||
name: ProxyAuthenticate.into(),
|
name: header::PROXY_AUTHENTICATE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
49 => Entry::Header {
|
49 => Entry::Header {
|
||||||
name: ProxyAuthorization.into(),
|
name: header::PROXY_AUTHORIZATION,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
50 => Entry::Header {
|
50 => Entry::Header {
|
||||||
name: Range.into(),
|
name: header::RANGE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
51 => Entry::Header {
|
51 => Entry::Header {
|
||||||
name: Referer.into(),
|
name: header::REFERER,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
52 => Entry::Header {
|
52 => Entry::Header {
|
||||||
name: Refresh.into(),
|
name: header::REFRESH,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
53 => Entry::Header {
|
53 => Entry::Header {
|
||||||
name: RetryAfter.into(),
|
name: header::RETRY_AFTER,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
54 => Entry::Header {
|
54 => Entry::Header {
|
||||||
name: Server.into(),
|
name: header::SERVER,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
55 => Entry::Header {
|
55 => Entry::Header {
|
||||||
name: SetCookie.into(),
|
name: header::SET_COOKIE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
56 => Entry::Header {
|
56 => Entry::Header {
|
||||||
name: StrictTransportSecurity.into(),
|
name: header::STRICT_TRANSPORT_SECURITY,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
57 => Entry::Header {
|
57 => Entry::Header {
|
||||||
name: TransferEncoding.into(),
|
name: header::TRANSFER_ENCODING,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
58 => Entry::Header {
|
58 => Entry::Header {
|
||||||
name: UserAgent.into(),
|
name: header::USER_AGENT,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
59 => Entry::Header {
|
59 => Entry::Header {
|
||||||
name: Vary.into(),
|
name: header::VARY,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
60 => Entry::Header {
|
60 => Entry::Header {
|
||||||
name: Via.into(),
|
name: header::VIA,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
61 => Entry::Header {
|
61 => Entry::Header {
|
||||||
name: WwwAuthenticate.into(),
|
name: header::WWW_AUTHENTICATE,
|
||||||
value: Str::new(),
|
value: HeaderValue::from_static(""),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use super::DecoderError;
|
use super::DecoderError;
|
||||||
|
use util::byte_str::{ByteStr, FromUtf8Error};
|
||||||
|
|
||||||
use tower::http::{HeaderName, Method, StatusCode, Str};
|
use http::{Method, StatusCode};
|
||||||
|
use http::header::{HeaderName, HeaderValue};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
/// HPack table entry
|
/// HPack table entry
|
||||||
@@ -8,12 +10,12 @@ use bytes::Bytes;
|
|||||||
pub enum Entry {
|
pub enum Entry {
|
||||||
Header {
|
Header {
|
||||||
name: HeaderName,
|
name: HeaderName,
|
||||||
value: Str,
|
value: HeaderValue,
|
||||||
},
|
},
|
||||||
Authority(Str),
|
Authority(ByteStr),
|
||||||
Method(Method),
|
Method(Method),
|
||||||
Scheme(Str),
|
Scheme(ByteStr),
|
||||||
Path(Str),
|
Path(ByteStr),
|
||||||
Status(StatusCode),
|
Status(StatusCode),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +30,38 @@ pub enum Key<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
|
pub fn new(name: Bytes, value: Bytes) -> Result<Entry, DecoderError> {
|
||||||
|
if name[0] == b':' {
|
||||||
|
match &name[1..] {
|
||||||
|
b"authority" => {
|
||||||
|
let value = try!(ByteStr::from_utf8(value));
|
||||||
|
Ok(Entry::Authority(value))
|
||||||
|
}
|
||||||
|
b"method" => {
|
||||||
|
let method = try!(Method::from_bytes(&value));
|
||||||
|
Ok(Entry::Method(method))
|
||||||
|
}
|
||||||
|
b"scheme" => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
b"path" => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
b"status" => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err(DecoderError::InvalidPseudoheader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let name = try!(HeaderName::from_bytes(&name));
|
||||||
|
let value = try!(HeaderValue::try_from_slice(&value));
|
||||||
|
|
||||||
|
Ok(Entry::Header { name: name, value: value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Entry::Header { ref name, ref value } => {
|
Entry::Header { ref name, ref value } => {
|
||||||
@@ -70,25 +104,26 @@ impl<'a> Key<'a> {
|
|||||||
Key::Header(name) => {
|
Key::Header(name) => {
|
||||||
Ok(Entry::Header {
|
Ok(Entry::Header {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
value: try!(Str::from_utf8(value)),
|
value: try!(HeaderValue::try_from_slice(&*value)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Key::Authority => {
|
Key::Authority => {
|
||||||
Ok(Entry::Authority(try!(Str::from_utf8(value))))
|
Ok(Entry::Authority(try!(ByteStr::from_utf8(value))))
|
||||||
}
|
}
|
||||||
Key::Method => {
|
Key::Method => {
|
||||||
Ok(Entry::Scheme(try!(Str::from_utf8(value))))
|
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value))))
|
||||||
}
|
}
|
||||||
Key::Scheme => {
|
Key::Scheme => {
|
||||||
Ok(Entry::Scheme(try!(Str::from_utf8(value))))
|
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value))))
|
||||||
}
|
}
|
||||||
Key::Path => {
|
Key::Path => {
|
||||||
Ok(Entry::Path(try!(Str::from_utf8(value))))
|
Ok(Entry::Path(try!(ByteStr::from_utf8(value))))
|
||||||
}
|
}
|
||||||
Key::Status => {
|
Key::Status => {
|
||||||
match StatusCode::parse(&value) {
|
match StatusCode::from_slice(&value) {
|
||||||
Some(status) => Ok(Entry::Status(status)),
|
Ok(status) => Ok(Entry::Status(status)),
|
||||||
None => Err(DecoderError::InvalidStatusCode),
|
// TODO: better error handling
|
||||||
|
Err(_) => Err(DecoderError::InvalidStatusCode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,20 @@ extern crate tokio_io;
|
|||||||
extern crate tokio_timer;
|
extern crate tokio_timer;
|
||||||
|
|
||||||
// HTTP types
|
// HTTP types
|
||||||
extern crate tower;
|
extern crate http;
|
||||||
|
|
||||||
// Buffer utilities
|
// Buffer utilities
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod hpack;
|
pub mod hpack;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
|
|
||||||
|
mod util;
|
||||||
|
|
||||||
pub use error::{ConnectionError, StreamError, Reason};
|
pub use error::{ConnectionError, StreamError, Reason};
|
||||||
pub use proto::Connection;
|
pub use proto::Connection;
|
||||||
|
|||||||
Reference in New Issue
Block a user