534 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			534 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use super::StreamId;
 | |
| use hpack;
 | |
| use frame::{self, Frame, Head, Kind, Error};
 | |
| use HeaderMap;
 | |
| 
 | |
| use http::{self, request, response, version, uri, Method, StatusCode, Uri};
 | |
| use http::{Request, Response};
 | |
| use http::header::{self, HeaderName, HeaderValue};
 | |
| 
 | |
| use bytes::{BytesMut, Bytes, Buf};
 | |
| use byteorder::{BigEndian, ByteOrder};
 | |
| use string::String;
 | |
| 
 | |
| use std::fmt;
 | |
| use std::io::Cursor;
 | |
| 
 | |
| /// Header frame
 | |
| ///
 | |
| /// This could be either a request or a response.
 | |
| #[derive(Debug)]
 | |
| pub struct Headers {
 | |
|     /// The ID of the stream with which this frame is associated.
 | |
|     stream_id: StreamId,
 | |
| 
 | |
|     /// The stream dependency information, if any.
 | |
|     stream_dep: Option<StreamDependency>,
 | |
| 
 | |
|     /// The decoded header fields
 | |
|     fields: HeaderMap,
 | |
| 
 | |
|     /// Pseudo headers, these are broken out as they must be sent as part of the
 | |
|     /// headers frame.
 | |
|     pseudo: Pseudo,
 | |
| 
 | |
|     /// The associated flags
 | |
|     flags: HeadersFlag,
 | |
| }
 | |
| 
 | |
| #[derive(Copy, Clone, Eq, PartialEq)]
 | |
| pub struct HeadersFlag(u8);
 | |
| 
 | |
| #[derive(Debug)]
 | |
| pub struct PushPromise {
 | |
|     /// The ID of the stream with which this frame is associated.
 | |
|     stream_id: StreamId,
 | |
| 
 | |
|     /// The ID of the stream being reserved by this PushPromise.
 | |
|     promised_id: StreamId,
 | |
| 
 | |
|     /// The associated flags
 | |
|     flags: PushPromiseFlag,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | |
| pub struct PushPromiseFlag(u8);
 | |
| 
 | |
| #[derive(Debug)]
 | |
| pub struct Continuation {
 | |
|     /// Stream ID of continuation frame
 | |
|     stream_id: StreamId,
 | |
| 
 | |
|     /// Argument to pass to the HPACK encoder to resume encoding
 | |
|     hpack: hpack::EncodeState,
 | |
| 
 | |
|     /// remaining headers to encode
 | |
|     headers: Iter,
 | |
| }
 | |
| 
 | |
| #[derive(Debug)]
 | |
| pub struct StreamDependency {
 | |
|     /// The ID of the stream dependency target
 | |
|     stream_id: StreamId,
 | |
| 
 | |
|     /// The weight for the stream. The value exposed (and set) here is always in
 | |
|     /// the range [0, 255], instead of [1, 256] (as defined in section 5.3.2.)
 | |
|     /// so that the value fits into a `u8`.
 | |
|     weight: u8,
 | |
| 
 | |
|     /// True if the stream dependency is exclusive.
 | |
|     is_exclusive: bool,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Default)]
 | |
| pub struct Pseudo {
 | |
|     // Request
 | |
|     method: Option<Method>,
 | |
|     scheme: Option<String<Bytes>>,
 | |
|     authority: Option<String<Bytes>>,
 | |
|     path: Option<String<Bytes>>,
 | |
| 
 | |
|     // Response
 | |
|     status: Option<StatusCode>,
 | |
| }
 | |
| 
 | |
| #[derive(Debug)]
 | |
| pub struct Iter {
 | |
|     /// Pseudo headers
 | |
|     pseudo: Option<Pseudo>,
 | |
| 
 | |
|     /// Header fields
 | |
|     fields: header::IntoIter<HeaderValue>,
 | |
| }
 | |
| 
 | |
| const END_STREAM: u8 = 0x1;
 | |
| const END_HEADERS: u8 = 0x4;
 | |
| const PADDED: u8 = 0x8;
 | |
| const PRIORITY: u8 = 0x20;
 | |
| const ALL: u8 = END_STREAM
 | |
|               | END_HEADERS
 | |
|               | PADDED
 | |
|               | PRIORITY;
 | |
| 
 | |
| // ===== impl Headers =====
 | |
| 
 | |
| impl Headers {
 | |
|     pub fn new(stream_id: StreamId, pseudo: Pseudo, fields: HeaderMap) -> Self {
 | |
|         Headers {
 | |
|             stream_id: stream_id,
 | |
|             stream_dep: None,
 | |
|             fields: fields,
 | |
|             pseudo: pseudo,
 | |
|             flags: HeadersFlag::default(),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn load(head: Head, src: &mut Cursor<Bytes>, decoder: &mut hpack::Decoder)
 | |
|         -> Result<Self, Error>
 | |
|     {
 | |
|         let flags = HeadersFlag(head.flag());
 | |
| 
 | |
|         trace!("loading headers; flags={:?}", flags);
 | |
| 
 | |
|         // Read the padding length
 | |
|         if flags.is_padded() {
 | |
|             let pad = src.get_u8() as usize;
 | |
|             let len = src.get_ref().len();
 | |
| 
 | |
|             if pad >= len {
 | |
|                 trace!("too much padding");
 | |
|                 return Err(Error::TooMuchPadding);
 | |
|             }
 | |
| 
 | |
|             // Truncate the last `pad` bytes.
 | |
|             let len = src.get_ref().len() - pad;
 | |
|             src.get_mut().truncate(len);
 | |
|         }
 | |
| 
 | |
|         // Read the stream dependency
 | |
|         let stream_dep = if flags.is_priority() {
 | |
|             let mut buf = [0u8; 4];
 | |
| 
 | |
|             // Read the next 4 bytes
 | |
|             src.copy_to_slice(&mut buf);
 | |
| 
 | |
|             // Parse the stream ID and exclusive flag
 | |
|             let (stream_id, is_exclusive) = StreamId::parse(&buf);
 | |
| 
 | |
|             // Read the weight
 | |
|             let weight = src.get_u8();
 | |
| 
 | |
|             Some(StreamDependency {
 | |
|                 stream_id,
 | |
|                 weight,
 | |
|                 is_exclusive,
 | |
|             })
 | |
|         } else {
 | |
|             None
 | |
|         };
 | |
| 
 | |
|         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.
 | |
|         let res = 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 let Err(e) = res {
 | |
|             trace!("hpack decoding error; err={:?}", e);
 | |
|             return Err(e.into());
 | |
|         }
 | |
| 
 | |
|         if err {
 | |
|             trace!("repeated pseudo");
 | |
|             return Err(hpack::DecoderError::RepeatedPseudo.into());
 | |
|         }
 | |
| 
 | |
|         Ok(Headers {
 | |
|             stream_id: head.stream_id(),
 | |
|             stream_dep: stream_dep,
 | |
|             fields: fields,
 | |
|             pseudo: pseudo,
 | |
|             flags: flags,
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     /// Returns `true` if the frame represents trailers
 | |
|     ///
 | |
|     /// Trailers are header frames that contain no pseudo headers.
 | |
|     pub fn is_trailers(&self) -> bool {
 | |
|         self.pseudo.method.is_none() &&
 | |
|             self.pseudo.status.is_none()
 | |
|     }
 | |
| 
 | |
|     pub fn stream_id(&self) -> StreamId {
 | |
|         self.stream_id
 | |
|     }
 | |
| 
 | |
|     pub fn is_end_headers(&self) -> bool {
 | |
|         self.flags.is_end_headers()
 | |
|     }
 | |
| 
 | |
|     pub fn is_end_stream(&self) -> bool {
 | |
|         self.flags.is_end_stream()
 | |
|     }
 | |
| 
 | |
|     pub fn set_end_stream(&mut self) {
 | |
|         self.flags.set_end_stream()
 | |
|     }
 | |
| 
 | |
|     pub fn into_response(self) -> http::Result<Response<()>> {
 | |
|         let mut b = Response::builder();
 | |
| 
 | |
|         if let Some(status) = self.pseudo.status {
 | |
|             b.status(status);
 | |
|         }
 | |
| 
 | |
|         let mut response = try!(b.body(()));
 | |
|         *response.headers_mut() = self.fields;
 | |
| 
 | |
|         Ok(response)
 | |
|     }
 | |
| 
 | |
|     pub fn into_request(self) -> http::Result<Request<()>> {
 | |
|         let mut b = Request::builder();
 | |
| 
 | |
|         // TODO: should we distinguish between HTTP_2 and HTTP_2C?
 | |
|         // carllerche/http#42
 | |
|         b.version(version::HTTP_2);
 | |
| 
 | |
|         if let Some(method) = self.pseudo.method {
 | |
|             b.method(method);
 | |
|         }
 | |
| 
 | |
|         // Convert the URI
 | |
|         let mut parts = uri::Parts::default();
 | |
| 
 | |
|         if let Some(scheme) = self.pseudo.scheme {
 | |
|             // TODO: Don't unwrap
 | |
|             parts.scheme = Some(uri::Scheme::try_from_shared(scheme.into_inner()).unwrap());
 | |
|         }
 | |
| 
 | |
|         if let Some(authority) = self.pseudo.authority {
 | |
|             // TODO: Don't unwrap
 | |
|             parts.authority = Some(uri::Authority::try_from_shared(authority.into_inner()).unwrap());
 | |
|         }
 | |
| 
 | |
|         if let Some(path) = self.pseudo.path {
 | |
|             // TODO: Don't unwrap
 | |
|             parts.origin_form = Some(uri::OriginForm::try_from_shared(path.into_inner()).unwrap());
 | |
|         }
 | |
| 
 | |
|         b.uri(parts);
 | |
| 
 | |
|         let mut request = try!(b.body(()));
 | |
|         *request.headers_mut() = self.fields;
 | |
| 
 | |
|         Ok(request)
 | |
|     }
 | |
| 
 | |
|     pub fn into_fields(self) -> HeaderMap {
 | |
|         self.fields
 | |
|     }
 | |
| 
 | |
|     pub fn encode(self, encoder: &mut hpack::Encoder, dst: &mut BytesMut)
 | |
|         -> Option<Continuation>
 | |
|     {
 | |
|         let head = self.head();
 | |
|         let pos = dst.len();
 | |
| 
 | |
|         // At this point, we don't know how big the h2 frame will be.
 | |
|         // So, we write the head with length 0, then write the body, and
 | |
|         // finally write the length once we know the size.
 | |
|         head.encode(0, dst);
 | |
| 
 | |
|         // Encode the frame
 | |
|         let mut headers = Iter {
 | |
|             pseudo: Some(self.pseudo),
 | |
|             fields: self.fields.into_iter(),
 | |
|         };
 | |
| 
 | |
|         let ret = match encoder.encode(None, &mut headers, dst) {
 | |
|             hpack::Encode::Full => None,
 | |
|             hpack::Encode::Partial(state) => {
 | |
|                 Some(Continuation {
 | |
|                     stream_id: self.stream_id,
 | |
|                     hpack: state,
 | |
|                     headers: headers,
 | |
|                 })
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         // Compute the frame length
 | |
|         let len = (dst.len() - pos) - frame::HEADER_LEN;
 | |
| 
 | |
|         // Write the frame length
 | |
|         BigEndian::write_uint(&mut dst[pos..pos+3], len as u64, 3);
 | |
| 
 | |
|         ret
 | |
|     }
 | |
| 
 | |
|     fn head(&self) -> Head {
 | |
|         Head::new(Kind::Headers, self.flags.into(), self.stream_id)
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T> From<Headers> for Frame<T> {
 | |
|     fn from(src: Headers) -> Self {
 | |
|         Frame::Headers(src)
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ===== impl PushPromise =====
 | |
| 
 | |
| impl PushPromise {
 | |
|     pub fn load(head: Head, payload: &[u8])
 | |
|         -> Result<Self, Error>
 | |
|     {
 | |
|         let flags = PushPromiseFlag(head.flag());
 | |
| 
 | |
|         // TODO: Handle padding
 | |
| 
 | |
|         let (promised_id, _) = StreamId::parse(&payload[..4]);
 | |
| 
 | |
|         Ok(PushPromise {
 | |
|             stream_id: head.stream_id(),
 | |
|             promised_id: promised_id,
 | |
|             flags: flags,
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     pub fn stream_id(&self) -> StreamId {
 | |
|         self.stream_id
 | |
|     }
 | |
| 
 | |
|     pub fn promised_id(&self) -> StreamId {
 | |
|         self.promised_id
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T> From<PushPromise> for Frame<T> {
 | |
|     fn from(src: PushPromise) -> Self {
 | |
|         Frame::PushPromise(src)
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ===== impl Pseudo =====
 | |
| 
 | |
| impl Pseudo {
 | |
|     pub fn request(method: Method, uri: Uri) -> Self {
 | |
|         let parts = uri::Parts::from(uri);
 | |
| 
 | |
|         fn to_string(src: Bytes) -> String<Bytes> {
 | |
|             unsafe { String::from_utf8_unchecked(src) }
 | |
|         }
 | |
| 
 | |
|         let path = parts.origin_form
 | |
|             .map(|v| v.into())
 | |
|             .unwrap_or_else(|| Bytes::from_static(b"/"));
 | |
| 
 | |
|         let mut pseudo = Pseudo {
 | |
|             method: Some(method),
 | |
|             scheme: None,
 | |
|             authority: None,
 | |
|             path: Some(to_string(path)),
 | |
|             status: None,
 | |
|         };
 | |
| 
 | |
|         // If the URI includes a scheme component, add it to the pseudo headers
 | |
|         //
 | |
|         // TODO: Scheme must be set...
 | |
|         if let Some(scheme) = parts.scheme {
 | |
|             pseudo.set_scheme(to_string(scheme.into()));
 | |
|         }
 | |
| 
 | |
|         // If the URI includes an authority component, add it to the pseudo
 | |
|         // headers
 | |
|         if let Some(authority) = parts.authority {
 | |
|             pseudo.set_authority(to_string(authority.into()));
 | |
|         }
 | |
| 
 | |
|         pseudo
 | |
|     }
 | |
| 
 | |
|     pub fn response(status: StatusCode) -> Self {
 | |
|         Pseudo {
 | |
|             method: None,
 | |
|             scheme: None,
 | |
|             authority: None,
 | |
|             path: None,
 | |
|             status: Some(status),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn set_scheme(&mut self, scheme: String<Bytes>) {
 | |
|         self.scheme = Some(scheme);
 | |
|     }
 | |
| 
 | |
|     pub fn set_authority(&mut self, authority: String<Bytes>) {
 | |
|         self.authority = Some(authority);
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ===== impl Iter =====
 | |
| 
 | |
| impl Iterator for Iter {
 | |
|     type Item = hpack::Header<Option<HeaderName>>;
 | |
| 
 | |
|     fn next(&mut self) -> Option<Self::Item> {
 | |
|         use hpack::Header::*;
 | |
| 
 | |
|         if let Some(ref mut pseudo) = self.pseudo {
 | |
|             if let Some(method) = pseudo.method.take() {
 | |
|                 return Some(Method(method));
 | |
|             }
 | |
| 
 | |
|             if let Some(scheme) = pseudo.scheme.take() {
 | |
|                 return Some(Scheme(scheme));
 | |
|             }
 | |
| 
 | |
|             if let Some(authority) = pseudo.authority.take() {
 | |
|                 return Some(Authority(authority));
 | |
|             }
 | |
| 
 | |
|             if let Some(path) = pseudo.path.take() {
 | |
|                 return Some(Path(path));
 | |
|             }
 | |
| 
 | |
|             if let Some(status) = pseudo.status.take() {
 | |
|                 return Some(Status(status));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         self.pseudo = None;
 | |
| 
 | |
|         self.fields.next()
 | |
|             .map(|(name, value)| {
 | |
|                 Field { name: name, value: value}
 | |
|             })
 | |
|     }
 | |
| }
 | |
| 
 | |
| // ===== impl HeadersFlag =====
 | |
| 
 | |
| impl HeadersFlag {
 | |
|     pub fn empty() -> HeadersFlag {
 | |
|         HeadersFlag(0)
 | |
|     }
 | |
| 
 | |
|     pub fn load(bits: u8) -> HeadersFlag {
 | |
|         HeadersFlag(bits & ALL)
 | |
|     }
 | |
| 
 | |
|     pub fn is_end_stream(&self) -> bool {
 | |
|         self.0 & END_STREAM == END_STREAM
 | |
|     }
 | |
| 
 | |
|     pub fn set_end_stream(&mut self) {
 | |
|         self.0 |= END_STREAM
 | |
|     }
 | |
| 
 | |
|     pub fn is_end_headers(&self) -> bool {
 | |
|         self.0 & END_HEADERS == END_HEADERS
 | |
|     }
 | |
| 
 | |
|     pub fn is_padded(&self) -> bool {
 | |
|         self.0 & PADDED == PADDED
 | |
|     }
 | |
| 
 | |
|     pub fn is_priority(&self) -> bool {
 | |
|         self.0 & PRIORITY == PRIORITY
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Default for HeadersFlag {
 | |
|     /// Returns a `HeadersFlag` value with `END_HEADERS` set.
 | |
|     fn default() -> Self {
 | |
|         HeadersFlag(END_HEADERS)
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl From<HeadersFlag> for u8 {
 | |
|     fn from(src: HeadersFlag) -> u8 {
 | |
|         src.0
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl fmt::Debug for HeadersFlag {
 | |
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 | |
|         fmt.debug_struct("HeadersFlag")
 | |
|             .field("end_stream", &self.is_end_stream())
 | |
|             .field("end_headers", &self.is_end_headers())
 | |
|             .field("padded", &self.is_padded())
 | |
|             .field("priority", &self.is_priority())
 | |
|             .finish()
 | |
|     }
 | |
| }
 |