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