Header frame decoding
This commit is contained in:
		| @@ -1,14 +1,17 @@ | |||||||
| use super::StreamId; | use super::StreamId; | ||||||
| use {frame, hpack}; | use hpack; | ||||||
| use frame::{Head, Kind}; | use error::Reason; | ||||||
|  | use frame::{self, Frame, Head, Kind, Error}; | ||||||
| use util::byte_str::ByteStr; | use util::byte_str::ByteStr; | ||||||
|  |  | ||||||
| use http::{Method, StatusCode}; | use http::{Method, StatusCode}; | ||||||
| use http::header::{self, HeaderMap, HeaderName, HeaderValue}; | use http::header::{self, HeaderMap, HeaderName, HeaderValue}; | ||||||
|  |  | ||||||
| use bytes::BytesMut; | use bytes::{BytesMut, Bytes}; | ||||||
| use byteorder::{BigEndian, ByteOrder}; | use byteorder::{BigEndian, ByteOrder}; | ||||||
|  |  | ||||||
|  | use std::io::Cursor; | ||||||
|  |  | ||||||
| /// Header frame | /// Header frame | ||||||
| /// | /// | ||||||
| /// This could be either a request or a response. | /// This could be either a request or a response. | ||||||
| @@ -20,8 +23,8 @@ pub struct Headers { | |||||||
|     /// The stream dependency information, if any. |     /// The stream dependency information, if any. | ||||||
|     stream_dep: Option<StreamDependency>, |     stream_dep: Option<StreamDependency>, | ||||||
|  |  | ||||||
|     /// The decoded headers |     /// The decoded header fields | ||||||
|     headers: HeaderMap<HeaderValue>, |     fields: HeaderMap<HeaderValue>, | ||||||
|  |  | ||||||
|     /// Pseudo headers, these are broken out as they must be sent as part of the |     /// Pseudo headers, these are broken out as they must be sent as part of the | ||||||
|     /// headers frame. |     /// headers frame. | ||||||
| @@ -72,7 +75,7 @@ pub struct StreamDependency { | |||||||
|     is_exclusive: bool, |     is_exclusive: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug, Default)] | ||||||
| pub struct Pseudo { | pub struct Pseudo { | ||||||
|     // Request |     // Request | ||||||
|     method: Option<Method>, |     method: Option<Method>, | ||||||
| @@ -89,8 +92,8 @@ pub struct Iter { | |||||||
|     /// Pseudo headers |     /// Pseudo headers | ||||||
|     pseudo: Option<Pseudo>, |     pseudo: Option<Pseudo>, | ||||||
|  |  | ||||||
|     /// Headers |     /// Header fields | ||||||
|     headers: header::IntoIter<HeaderValue>, |     fields: header::IntoIter<HeaderValue>, | ||||||
| } | } | ||||||
|  |  | ||||||
| const END_STREAM: u8 = 0x1; | const END_STREAM: u8 = 0x1; | ||||||
| @@ -105,6 +108,60 @@ const ALL: u8 = END_STREAM | |||||||
| // ===== impl Headers ===== | // ===== impl Headers ===== | ||||||
|  |  | ||||||
| impl Headers { | impl Headers { | ||||||
|  |     pub fn load(head: Head, src: &mut Cursor<Bytes>, decoder: &mut hpack::Decoder) | ||||||
|  |         -> Result<Self, Error> | ||||||
|  |     { | ||||||
|  |         let flags = HeadersFlag(head.flag()); | ||||||
|  |  | ||||||
|  |         assert!(!flags.is_priority(), "unimplemented stream priority"); | ||||||
|  |  | ||||||
|  |         let mut pseudo = Pseudo::default(); | ||||||
|  |         let mut fields = HeaderMap::new(); | ||||||
|  |         let mut err = false; | ||||||
|  |  | ||||||
|  |         macro_rules! set_pseudo { | ||||||
|  |             ($field:ident, $val:expr) => {{ | ||||||
|  |                 if pseudo.$field.is_some() { | ||||||
|  |                     err = true; | ||||||
|  |                 } else { | ||||||
|  |                     pseudo.$field = Some($val); | ||||||
|  |                 } | ||||||
|  |             }} | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // At this point, we're going to assume that the hpack encoded headers | ||||||
|  |         // contain the entire payload. Later, we need to check for stream | ||||||
|  |         // priority. | ||||||
|  |         // | ||||||
|  |         // TODO: Provide a way to abort decoding if an error is hit. | ||||||
|  |         try!(decoder.decode(src, |header| { | ||||||
|  |             use hpack::Header::*; | ||||||
|  |  | ||||||
|  |             match header { | ||||||
|  |                 Field { name, value } => { | ||||||
|  |                     fields.append(name, value); | ||||||
|  |                 } | ||||||
|  |                 Authority(v) => set_pseudo!(authority, v), | ||||||
|  |                 Method(v) => set_pseudo!(method, v), | ||||||
|  |                 Scheme(v) => set_pseudo!(scheme, v), | ||||||
|  |                 Path(v) => set_pseudo!(path, v), | ||||||
|  |                 Status(v) => set_pseudo!(status, v), | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  |  | ||||||
|  |         if err { | ||||||
|  |             return Err(hpack::DecoderError::RepeatedPseudo.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(Headers { | ||||||
|  |             stream_id: head.stream_id(), | ||||||
|  |             stream_dep: None, | ||||||
|  |             fields: fields, | ||||||
|  |             pseudo: pseudo, | ||||||
|  |             flags: flags, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn encode(self, encoder: &mut hpack::Encoder, dst: &mut BytesMut) |     pub fn encode(self, encoder: &mut hpack::Encoder, dst: &mut BytesMut) | ||||||
|         -> Option<Continuation> |         -> Option<Continuation> | ||||||
|     { |     { | ||||||
| @@ -119,7 +176,7 @@ impl Headers { | |||||||
|         // Encode the frame |         // Encode the frame | ||||||
|         let mut headers = Iter { |         let mut headers = Iter { | ||||||
|             pseudo: Some(self.pseudo), |             pseudo: Some(self.pseudo), | ||||||
|             headers: self.headers.into_iter(), |             fields: self.fields.into_iter(), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let ret = match encoder.encode(None, &mut headers, dst) { |         let ret = match encoder.encode(None, &mut headers, dst) { | ||||||
| @@ -147,6 +204,12 @@ impl Headers { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl From<Headers> for Frame { | ||||||
|  |     fn from(src: Headers) -> Frame { | ||||||
|  |         Frame::Headers(src) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| // ===== impl Iter ===== | // ===== impl Iter ===== | ||||||
|  |  | ||||||
| impl Iterator for Iter { | impl Iterator for Iter { | ||||||
| @@ -179,7 +242,7 @@ impl Iterator for Iter { | |||||||
|  |  | ||||||
|         self.pseudo = None; |         self.pseudo = None; | ||||||
|  |  | ||||||
|         self.headers.next() |         self.fields.next() | ||||||
|             .map(|(name, value)| { |             .map(|(name, value)| { | ||||||
|                 Field { name: name, value: value} |                 Field { name: name, value: value} | ||||||
|             }) |             }) | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
|  | use hpack; | ||||||
| use error::{ConnectionError, Reason}; | use error::{ConnectionError, Reason}; | ||||||
|  |  | ||||||
| use bytes::{Bytes, BytesMut, BufMut}; | use bytes::{Bytes, BytesMut, BufMut}; | ||||||
|  |  | ||||||
| use std::io; | use std::io; | ||||||
| @@ -92,6 +94,9 @@ pub enum Error { | |||||||
|     /// This is returned if a settings frame is received with a stream |     /// This is returned if a settings frame is received with a stream | ||||||
|     /// identifier other than zero. |     /// identifier other than zero. | ||||||
|     InvalidStreamId, |     InvalidStreamId, | ||||||
|  |  | ||||||
|  |     /// Failed to perform HPACK decoding | ||||||
|  |     Hpack(hpack::DecoderError), | ||||||
| } | } | ||||||
|  |  | ||||||
| // ===== impl Frame ====== | // ===== impl Frame ====== | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| use super::{huffman, header as h2_header, Header}; | use super::{huffman, header as h2_header, Header}; | ||||||
|  | use frame; | ||||||
| use util::byte_str::FromUtf8Error; | use util::byte_str::FromUtf8Error; | ||||||
|  |  | ||||||
| use http::{method, header, status, StatusCode, Method}; | use http::{method, header, status, StatusCode, Method}; | ||||||
| @@ -19,7 +20,7 @@ pub struct Decoder { | |||||||
|  |  | ||||||
| /// Represents all errors that can be encountered while performing the decoding | /// Represents all errors that can be encountered while performing the decoding | ||||||
| /// of an HPACK header set. | /// of an HPACK header set. | ||||||
| #[derive(Debug, Copy, Clone, PartialEq)] | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||||
| pub enum DecoderError { | pub enum DecoderError { | ||||||
|     InvalidRepresentation, |     InvalidRepresentation, | ||||||
|     InvalidIntegerPrefix, |     InvalidIntegerPrefix, | ||||||
| @@ -32,6 +33,7 @@ pub enum DecoderError { | |||||||
|     IntegerUnderflow, |     IntegerUnderflow, | ||||||
|     IntegerOverflow, |     IntegerOverflow, | ||||||
|     StringUnderflow, |     StringUnderflow, | ||||||
|  |     RepeatedPseudo, | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Representation { | enum Representation { | ||||||
| @@ -155,30 +157,29 @@ impl Decoder { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Decodes the headers found in the given buffer. |     /// Decodes the headers found in the given buffer. | ||||||
|     pub fn decode<F>(&mut self, src: &Bytes, mut f: F) -> Result<(), DecoderError> |     pub fn decode<F>(&mut self, src: &mut Cursor<Bytes>, mut f: F) -> Result<(), DecoderError> | ||||||
|         where F: FnMut(Header) |         where F: FnMut(Header) | ||||||
|     { |     { | ||||||
|         use self::Representation::*; |         use self::Representation::*; | ||||||
|  |  | ||||||
|         let mut buf = Cursor::new(src); |  | ||||||
|         let mut can_resize = true; |         let mut can_resize = true; | ||||||
|  |  | ||||||
|         if let Some(size) = self.max_size_update.take() { |         if let Some(size) = self.max_size_update.take() { | ||||||
|             self.last_max_update = size; |             self.last_max_update = size; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         while buf.has_remaining() { |         while src.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 | ||||||
|             // within the HPACK data. The type of the block can always be |             // within the HPACK data. The type of the block can always be | ||||||
|             // determined from the first byte. |             // determined from the first byte. | ||||||
|             match try!(Representation::load(peek_u8(&mut buf))) { |             match try!(Representation::load(peek_u8(src))) { | ||||||
|                 Indexed => { |                 Indexed => { | ||||||
|                     can_resize = false; |                     can_resize = false; | ||||||
|                     f(try!(self.decode_indexed(&mut buf))); |                     f(try!(self.decode_indexed(src))); | ||||||
|                 } |                 } | ||||||
|                 LiteralWithIndexing => { |                 LiteralWithIndexing => { | ||||||
|                     can_resize = false; |                     can_resize = false; | ||||||
|                     let entry = try!(self.decode_literal(&mut buf, true)); |                     let entry = try!(self.decode_literal(src, true)); | ||||||
|  |  | ||||||
|                     // Insert the header into the table |                     // Insert the header into the table | ||||||
|                     self.table.insert(entry.clone()); |                     self.table.insert(entry.clone()); | ||||||
| @@ -187,12 +188,12 @@ impl Decoder { | |||||||
|                 } |                 } | ||||||
|                 LiteralWithoutIndexing => { |                 LiteralWithoutIndexing => { | ||||||
|                     can_resize = false; |                     can_resize = false; | ||||||
|                     let entry = try!(self.decode_literal(&mut buf, false)); |                     let entry = try!(self.decode_literal(src, false)); | ||||||
|                     f(entry); |                     f(entry); | ||||||
|                 } |                 } | ||||||
|                 LiteralNeverIndexed => { |                 LiteralNeverIndexed => { | ||||||
|                     can_resize = false; |                     can_resize = false; | ||||||
|                     let entry = try!(self.decode_literal(&mut buf, false)); |                     let entry = try!(self.decode_literal(src, false)); | ||||||
|  |  | ||||||
|                     // TODO: Track that this should never be indexed |                     // TODO: Track that this should never be indexed | ||||||
|  |  | ||||||
| @@ -204,7 +205,7 @@ impl Decoder { | |||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     // Handle the dynamic table size update |                     // Handle the dynamic table size update | ||||||
|                     try!(self.process_size_update(&mut buf)); |                     try!(self.process_size_update(src)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -212,7 +213,7 @@ impl Decoder { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn process_size_update(&mut self, buf: &mut Cursor<&Bytes>) |     fn process_size_update(&mut self, buf: &mut Cursor<Bytes>) | ||||||
|         -> Result<(), DecoderError> |         -> Result<(), DecoderError> | ||||||
|     { |     { | ||||||
|         let new_size = try!(decode_int(buf, 5)); |         let new_size = try!(decode_int(buf, 5)); | ||||||
| @@ -229,14 +230,14 @@ impl Decoder { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn decode_indexed(&self, buf: &mut Cursor<&Bytes>) |     fn decode_indexed(&self, buf: &mut Cursor<Bytes>) | ||||||
|         -> Result<Header, DecoderError> |         -> Result<Header, DecoderError> | ||||||
|     { |     { | ||||||
|         let index = try!(decode_int(buf, 7)); |         let index = try!(decode_int(buf, 7)); | ||||||
|         self.table.get(index) |         self.table.get(index) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn decode_literal(&mut self, buf: &mut Cursor<&Bytes>, index: bool) |     fn decode_literal(&mut self, buf: &mut Cursor<Bytes>, index: bool) | ||||||
|         -> Result<Header, DecoderError> |         -> Result<Header, DecoderError> | ||||||
|     { |     { | ||||||
|         let prefix = if index { |         let prefix = if index { | ||||||
| @@ -263,7 +264,7 @@ impl Decoder { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn decode_string(&mut self, buf: &mut Cursor<&Bytes>) -> Result<Bytes, DecoderError> { |     fn decode_string(&mut self, buf: &mut Cursor<Bytes>) -> Result<Bytes, DecoderError> { | ||||||
|         const HUFF_FLAG: u8 = 0b10000000; |         const HUFF_FLAG: u8 = 0b10000000; | ||||||
|  |  | ||||||
|         // The first bit in the first byte contains the huffman encoded flag. |         // The first bit in the first byte contains the huffman encoded flag. | ||||||
| @@ -388,7 +389,7 @@ fn peek_u8<B: Buf>(buf: &mut B) -> u8 { | |||||||
|     buf.bytes()[0] |     buf.bytes()[0] | ||||||
| } | } | ||||||
|  |  | ||||||
| fn take(buf: &mut Cursor<&Bytes>, n: usize) -> Bytes { | fn take(buf: &mut Cursor<Bytes>, n: usize) -> Bytes { | ||||||
|     let pos = buf.position() as usize; |     let pos = buf.position() as usize; | ||||||
|     let ret = buf.get_ref().slice(pos, pos + n); |     let ret = buf.get_ref().slice(pos, pos + n); | ||||||
|     buf.set_position((pos + n) as u64); |     buf.set_position((pos + n) as u64); | ||||||
| @@ -520,6 +521,12 @@ impl From<status::FromStrError> for DecoderError { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl From<DecoderError> for frame::Error { | ||||||
|  |     fn from(src: DecoderError) -> Self { | ||||||
|  |         frame::Error::Hpack(src) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Get an entry from the static table | /// Get an entry from the static table | ||||||
| pub fn get_static(idx: usize) -> Header { | pub fn get_static(idx: usize) -> Header { | ||||||
|     use http::{status, method, header}; |     use http::{status, method, header}; | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ use self::rand::{StdRng, Rng, SeedableRng}; | |||||||
| use std::env; | use std::env; | ||||||
| use std::fs::File; | use std::fs::File; | ||||||
| use std::io::prelude::*; | use std::io::prelude::*; | ||||||
|  | use std::io::Cursor; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| use std::str; | use std::str; | ||||||
|  |  | ||||||
| @@ -220,7 +221,7 @@ impl FuzzHpack { | |||||||
|                         index = Some(i); |                         index = Some(i); | ||||||
|  |  | ||||||
|                         // Decode the chunk! |                         // Decode the chunk! | ||||||
|                         decoder.decode(&buf.into(), |e| { |                         decoder.decode(&mut Cursor::new(buf.into()), |e| { | ||||||
|                             assert_eq!(e, expect.remove(0).reify().unwrap()); |                             assert_eq!(e, expect.remove(0).reify().unwrap()); | ||||||
|                         }).unwrap(); |                         }).unwrap(); | ||||||
|  |  | ||||||
| @@ -231,7 +232,7 @@ impl FuzzHpack { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             // Decode the chunk! |             // Decode the chunk! | ||||||
|             decoder.decode(&buf.into(), |e| { |             decoder.decode(&mut Cursor::new(buf.into()), |e| { | ||||||
|                 assert_eq!(e, expect.remove(0).reify().unwrap()); |                 assert_eq!(e, expect.remove(0).reify().unwrap()); | ||||||
|             }).unwrap(); |             }).unwrap(); | ||||||
|         } |         } | ||||||
| @@ -506,7 +507,7 @@ fn test_story(story: Value) { | |||||||
|                 decoder.queue_size_update(size); |                 decoder.queue_size_update(size); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             decoder.decode(&case.wire.clone().into(), |e| { |             decoder.decode(&mut Cursor::new(case.wire.clone().into()), |e| { | ||||||
|                 let (name, value) = expect.remove(0); |                 let (name, value) = expect.remove(0); | ||||||
|                 assert_eq!(name, key_str(&e)); |                 assert_eq!(name, key_str(&e)); | ||||||
|                 assert_eq!(value, value_str(&e)); |                 assert_eq!(value, value_str(&e)); | ||||||
| @@ -533,7 +534,7 @@ fn test_story(story: Value) { | |||||||
|  |  | ||||||
|             encoder.encode(None, &mut input.clone().into_iter(), &mut buf); |             encoder.encode(None, &mut input.clone().into_iter(), &mut buf); | ||||||
|  |  | ||||||
|             decoder.decode(&buf.into(), |e| { |             decoder.decode(&mut Cursor::new(buf.into()), |e| { | ||||||
|                 assert_eq!(e, input.remove(0).reify().unwrap()); |                 assert_eq!(e, input.remove(0).reify().unwrap()); | ||||||
|             }).unwrap(); |             }).unwrap(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ use tokio_io::AsyncWrite; | |||||||
| use futures::*; | use futures::*; | ||||||
| use bytes::{Bytes, BytesMut, Buf}; | use bytes::{Bytes, BytesMut, Buf}; | ||||||
|  |  | ||||||
| use std::io::{self, Write}; | use std::io::{self, Write, Cursor}; | ||||||
|  |  | ||||||
| pub struct FramedRead<T> { | pub struct FramedRead<T> { | ||||||
|     inner: T, |     inner: T, | ||||||
| @@ -15,6 +15,13 @@ pub struct FramedRead<T> { | |||||||
|     // hpack decoder state |     // hpack decoder state | ||||||
|     hpack: hpack::Decoder, |     hpack: hpack::Decoder, | ||||||
|  |  | ||||||
|  |     partial: Option<Partial>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Partially loaded headers frame | ||||||
|  | enum Partial { | ||||||
|  |     Headers(frame::Headers), | ||||||
|  |     PushPromise(frame::PushPromise), | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T> FramedRead<T> | impl<T> FramedRead<T> | ||||||
| @@ -25,6 +32,7 @@ impl<T> FramedRead<T> | |||||||
|         FramedRead { |         FramedRead { | ||||||
|             inner: inner, |             inner: inner, | ||||||
|             hpack: hpack::Decoder::new(DEFAULT_SETTINGS_HEADER_TABLE_SIZE), |             hpack: hpack::Decoder::new(DEFAULT_SETTINGS_HEADER_TABLE_SIZE), | ||||||
|  |             partial: None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -34,9 +42,20 @@ impl<T> FramedRead<T> { | |||||||
|         // Parse the head |         // Parse the head | ||||||
|         let head = frame::Head::parse(&bytes); |         let head = frame::Head::parse(&bytes); | ||||||
|  |  | ||||||
|  |         if self.partial.is_some() && head.kind() != Kind::Continuation { | ||||||
|  |             unimplemented!(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         let frame = match head.kind() { |         let frame = match head.kind() { | ||||||
|             Kind::Data => unimplemented!(), |             Kind::Data => unimplemented!(), | ||||||
|             Kind::Headers => unimplemented!(), |             Kind::Headers => { | ||||||
|  |                 let mut buf = Cursor::new(bytes); | ||||||
|  |                 buf.set_position(frame::HEADER_LEN as u64); | ||||||
|  |  | ||||||
|  |                 // TODO: Change to drain: carllerche/bytes#130 | ||||||
|  |                 let frame = try!(frame::Headers::load(head, &mut buf, &mut self.hpack)); | ||||||
|  |                 frame.into() | ||||||
|  |             } | ||||||
|             Kind::Priority => unimplemented!(), |             Kind::Priority => unimplemented!(), | ||||||
|             Kind::Reset => unimplemented!(), |             Kind::Reset => unimplemented!(), | ||||||
|             Kind::Settings => { |             Kind::Settings => { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user