├── .gitignore ├── src ├── lib.rs ├── client │ ├── mod.rs │ ├── simple.rs │ └── async.rs └── http │ ├── frame │ ├── test.rs │ ├── mod.rs │ ├── continuationframe.rs │ ├── pingframe.rs │ ├── frames.rs │ ├── dataframe.rs │ ├── settingsframe.rs │ └── headersframe.rs │ ├── transport.rs │ ├── mod.rs │ ├── session.rs │ └── connection.rs ├── Cargo.toml ├── .travis.yml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate log; 2 | extern crate hpack; 3 | extern crate openssl; 4 | 5 | pub mod http; 6 | pub mod client; 7 | 8 | mod tests { 9 | } 10 | -------------------------------------------------------------------------------- /src/client/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module contains implementations of HTTP/2 clients that could be 2 | //! directly used to access HTTP/2 servers, i.e. send requests and read 3 | //! responses. 4 | 5 | pub use self::simple::SimpleClient; 6 | pub use self::async::Client; 7 | 8 | mod simple; 9 | mod async; 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "solicit" 4 | version = "0.0.1" 5 | authors = ["Marko Lalic "] 6 | 7 | [dependencies] 8 | log = "^0.3" 9 | 10 | [dependencies.hpack] 11 | git = "https://github.com/mlalic/hpack-rs.git" 12 | 13 | [dependencies.openssl] 14 | version = "*" 15 | features = ["tlsv1_2", "npn"] 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | script: cargo test 3 | 4 | after_success: | 5 | [ $TRAVIS_BRANCH = master ] && 6 | [ $TRAVIS_PULL_REQUEST = false ] && 7 | cargo doc && 8 | echo "" > target/doc/index.html && 9 | sudo pip install ghp-import && 10 | ghp-import -n target/doc && 11 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 12 | 13 | env: 14 | global: 15 | secure: eSYRUJ2wTq1g6AiPp0zvtxVJFn/3FnrCRAJmGCN1TBYpnl11ZvLZfhUA9IC4S48/YVmdeP1pywpIjY3ZGk7gWuaRLpTrwBxgm01RbOglQS1if6Pryc01FcwCSGb1fJKY4qR0v6iQRb23jaFfSELHfThf4rmG4QiKiNviHJRzb0c= 16 | -------------------------------------------------------------------------------- /src/http/frame/test.rs: -------------------------------------------------------------------------------- 1 | use super::{Frame, RawFrame, FrameHeader}; 2 | 3 | /// Builds a test frame of the given type with the given header and 4 | /// payload, by using the `Frame::from_raw` method. 5 | pub fn build_test_frame(header: &FrameHeader, payload: &[u8]) -> F { 6 | let raw = RawFrame::with_payload(header.clone(), payload.to_vec()); 7 | Frame::from_raw(raw).unwrap() 8 | } 9 | 10 | /// Builds a `Vec` containing the given data as a padded HTTP/2 frame. 11 | /// 12 | /// It first places the length of the padding, followed by the data, 13 | /// followed by `pad_len` zero bytes. 14 | pub fn build_padded_frame_payload(data: &[u8], pad_len: u8) -> Vec { 15 | let sz = 1 + data.len() + pad_len as usize; 16 | let mut payload: Vec = Vec::with_capacity(sz); 17 | payload.push(pad_len); 18 | payload.extend(data.to_vec().into_iter()); 19 | for _ in 0..pad_len { payload.push(0); } 20 | 21 | payload 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Marko Lalic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/http/frame/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module contains the implementation of HTTP/2 frames. 2 | 3 | /// A helper macro that unpacks a sequence of 4 bytes found in the buffer with 4 | /// the given identifier, starting at the given offset, into the given integer 5 | /// type. Obviously, the integer type should be able to support at least 4 6 | /// bytes. 7 | /// 8 | /// # Examples 9 | /// 10 | /// ```rust 11 | /// let buf: [u8; 4] = [0, 0, 0, 1]; 12 | /// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32)); 13 | /// ``` 14 | macro_rules! unpack_octets_4 { 15 | ($buf:ident, $offset:expr, $tip:ty) => ( 16 | (($buf[$offset + 0] as $tip) << 24) | 17 | (($buf[$offset + 1] as $tip) << 16) | 18 | (($buf[$offset + 2] as $tip) << 8) | 19 | (($buf[$offset + 3] as $tip) << 0) 20 | ); 21 | } 22 | 23 | pub use self::frames::{ 24 | Frame, 25 | Flag, 26 | parse_padded_payload, 27 | unpack_header, 28 | pack_header, 29 | RawFrame, 30 | FrameHeader 31 | }; 32 | 33 | pub use self::dataframe::{ 34 | DataFlag, 35 | DataFrame, 36 | }; 37 | 38 | pub use self::settingsframe::{ 39 | HttpSetting, 40 | SettingsFlag, 41 | SettingsFrame, 42 | }; 43 | 44 | pub use self::headersframe::{ 45 | HeadersFlag, 46 | StreamDependency, 47 | HeadersFrame, 48 | }; 49 | 50 | pub use self::pingframe::{ 51 | PingFlag, 52 | PingFrame, 53 | }; 54 | 55 | pub use self::continuationframe::{ 56 | ContinuationFlag, 57 | ContinuationFrame, 58 | }; 59 | 60 | pub mod frames; 61 | mod test; 62 | pub mod dataframe; 63 | pub mod settingsframe; 64 | pub mod headersframe; 65 | pub mod pingframe; 66 | pub mod continuationframe; 67 | -------------------------------------------------------------------------------- /src/http/transport.rs: -------------------------------------------------------------------------------- 1 | //! The module contains implementations of the transport layer functionality 2 | //! that HTTP/2 requires. It exposes APIs that allow the HTTP/2 connection to 3 | //! use the transport layer without requiring it to know which exact 4 | //! implementation they are using (i.e. a clear-text TCP connection, a TLS 5 | //! protected connection, or even a mock implementation). 6 | 7 | use std::io; 8 | use std::io::{Read, Write}; 9 | use std::net::TcpStream; 10 | use openssl::ssl::SslStream; 11 | 12 | /// A trait that any struct that wants to provide the transport layer for 13 | /// HTTP/2 needs to implement. 14 | /// 15 | /// It provides default implementations for some convenience methods, backed 16 | /// by the `Read` and `Write` implementations. 17 | pub trait TransportStream: Read + Write { 18 | /// A convenience method that performs as many `read` calls on the 19 | /// underlying `Read` implementation as it takes to fill the given buffer. 20 | /// 21 | /// The implementation simply calls the `read` in a loop until the 22 | /// buffer is filled or an aparent end of file is reached, upon which 23 | /// an error is returned. 24 | /// 25 | /// However, no particular care is taken to limit the number of loop 26 | /// iterations and it could theoretically be possible to end up reading 27 | /// a single byte at a time into a large buffer, taking a long time to 28 | /// return. 29 | /// 30 | /// Any errors raised by the underlying `Read` implementations are 31 | /// propagated. 32 | /// 33 | /// When an error is raised, the given buffer is only partially filled, 34 | /// but there is no way to know how many bytes were actually written to 35 | /// the underlying buffer, which means that, effectively, all read bytes 36 | /// are lost on any error. 37 | fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { 38 | let mut total = 0; 39 | while total < buf.len() { 40 | let read = try!(self.read(&mut buf[total..])); 41 | if read == 0 { 42 | // We consider this an unexpected end of file and return an 43 | // error since we were unable to read the minimum amount of 44 | // bytes. 45 | return Err(io::Error::new(io::ErrorKind::Other, 46 | "Not enough bytes")); 47 | } 48 | total += read; 49 | } 50 | 51 | Ok(()) 52 | } 53 | } 54 | 55 | /// Since `TcpStream` already implements `Read` and `Write` we do not define any 56 | /// additional required methods on `TransportStream`, we get this for free. 57 | impl TransportStream for TcpStream {} 58 | 59 | impl TransportStream for SslStream {} 60 | -------------------------------------------------------------------------------- /src/http/frame/continuationframe.rs: -------------------------------------------------------------------------------- 1 | use super::super::StreamId; 2 | use super::frames::{ 3 | Frame, 4 | Flag, 5 | parse_padded_payload, 6 | pack_header, 7 | RawFrame, 8 | FrameHeader 9 | }; 10 | 11 | /// An enum representing the flags that a `ContinuationFrame` can have. 12 | /// The integer representation associated to each variant is that flag's 13 | /// bitmask. 14 | /// 15 | /// HTTP/2 spec, section 6.10. 16 | #[derive(Clone)] 17 | #[derive(PartialEq)] 18 | #[derive(Debug)] 19 | #[derive(Copy)] 20 | pub enum ContinuationFlag { 21 | EndHeaders = 0x4, 22 | } 23 | 24 | impl Flag for ContinuationFlag { 25 | #[inline] 26 | fn bitmask(&self) -> u8 { 27 | *self as u8 28 | } 29 | } 30 | 31 | /// A struct representing the CONTINUATION frame for HTTP/2, as defined in the 32 | /// HTTP/2 spec, section 6.10. 33 | pub struct ContinuationFrame { 34 | /// The header block fragment bytes stored within the frame. 35 | pub header_fragment: Vec, 36 | /// The ID of the stream with which this frame is associated 37 | pub stream_id: StreamId, 38 | /// The set of flags for the frame, packed into a single byte. 39 | flags: u8, 40 | } 41 | 42 | impl ContinuationFrame { 43 | /// Creates a new `ContinuationFrame` with the given header fragment and stream 44 | /// ID. No flags are set. 45 | pub fn new(fragment: Vec, stream_id: StreamId) -> ContinuationFrame { 46 | ContinuationFrame { 47 | header_fragment: fragment, 48 | stream_id: stream_id, 49 | flags: 0, 50 | } 51 | } 52 | 53 | /// Creates a new `ContinuationFrame` with the given header fragment and stream 54 | /// ID. End header is set as true. 55 | pub fn with_end(fragment: Vec, stream_id: StreamId) -> ContinuationFrame { 56 | ContinuationFrame { 57 | header_fragment: fragment, 58 | stream_id: stream_id, 59 | flags: 0x4, 60 | } 61 | } 62 | /// Returns whether this frame ends the headers. If not, there MUST be a 63 | /// number of follow up CONTINUATION frames that send the rest of the 64 | /// header data. 65 | pub fn is_headers_end(&self) -> bool { 66 | self.is_set(ContinuationFlag::EndHeaders) 67 | } 68 | 69 | /// Returns the length of the payload of the current frame, including any 70 | /// possible padding in the number of bytes. 71 | fn payload_len(&self) -> u32 { 72 | self.header_fragment.len() as u32 73 | } 74 | } 75 | 76 | impl Frame for ContinuationFrame { 77 | /// The type that represents the flags that the particular `Frame` can take. 78 | /// This makes sure that only valid `Flag`s are used with each `Frame`. 79 | type FlagType = ContinuationFlag; 80 | 81 | /// Creates a new `ContinuationFrame` with the given `RawFrame` (i.e. header and 82 | /// payload), if possible. 83 | /// 84 | /// # Returns 85 | /// 86 | /// `None` if a valid `ContinuationFrame` cannot be constructed from the given 87 | /// `RawFrame`. The stream ID *must not* be 0. 88 | /// 89 | /// Otherwise, returns a newly constructed `ContinuationFrame`. 90 | fn from_raw(raw_frame: RawFrame) -> Option { 91 | // Unpack the header 92 | let (len, frame_type, flags, stream_id) = raw_frame.header; 93 | // Check that the frame type is correct for this frame implementation 94 | if frame_type != 0x9 { 95 | return None; 96 | } 97 | // Check that the length given in the header matches the payload 98 | // length; if not, something went wrong and we do not consider this a 99 | // valid frame. 100 | if (len as usize) != raw_frame.payload.len() { 101 | return None; 102 | } 103 | // Check that the CONTINUATION frame is not associated to stream 0 104 | if stream_id == 0 { 105 | return None; 106 | } 107 | 108 | // let payload = &raw_frame.payload[..]; 109 | 110 | Some(ContinuationFrame { 111 | header_fragment: raw_frame.payload[..].to_vec(), 112 | stream_id: stream_id, 113 | flags: flags 114 | }) 115 | } 116 | 117 | /// Tests if the given flag is set for the frame. 118 | fn is_set(&self, flag: ContinuationFlag) -> bool { 119 | (self.flags & flag.bitmask()) != 0 120 | } 121 | 122 | // Returns the `StreamId` of the stream to which the frame is associated. 123 | /// 124 | /// A `SettingsFrame` always has to be associated to stream `0`. 125 | fn get_stream_id(&self) -> StreamId { 126 | self.stream_id 127 | } 128 | 129 | /// Returns a `FrameHeader` based on the current state of the `Frame`. 130 | fn get_header(&self) -> FrameHeader { 131 | (self.payload_len(), 0x9, self.flags, self.stream_id) 132 | } 133 | 134 | /// Sets the given flag for the frame. 135 | fn set_flag(&mut self, flag: ContinuationFlag) { 136 | self.flags |= flag.bitmask(); 137 | } 138 | 139 | /// Returns a `Vec` with the serialized representation of the frame. 140 | fn serialize(&self) -> Vec { 141 | let mut buf = Vec::with_capacity(self.payload_len() as usize); 142 | // First the header... 143 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter()); 144 | // Now the actual headers fragment 145 | buf.extend(self.header_fragment.clone().into_iter()); 146 | 147 | buf 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/http/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module implements the client side of the HTTP/2 protocol and exposes 2 | //! an API for using it. 3 | use std::io; 4 | use std::convert::From; 5 | use std::error::Error; 6 | 7 | use hpack::decoder::DecoderError; 8 | 9 | pub mod frame; 10 | pub mod transport; 11 | pub mod connection; 12 | pub mod session; 13 | 14 | /// An alias for the type that represents the ID of an HTTP/2 stream 15 | pub type StreamId = u32; 16 | /// An alias for the type that represents HTTP/2 haders. For now we only alias 17 | /// the tuple of byte vectors instead of going with a full struct representation. 18 | pub type Header = (Vec, Vec); 19 | 20 | /// A set of protocol names that the library should use to indicate that HTTP/2 21 | /// is supported during protocol negotiation (NPN or ALPN). 22 | /// We include some of the drafts' protocol names, since there is basically no 23 | /// difference for all intents and purposes (and some servers out there still 24 | /// only officially advertise draft support). 25 | /// TODO: Eventually only use "h2". 26 | pub const ALPN_PROTOCOLS: &'static [&'static [u8]] = &[ 27 | b"h2", 28 | b"h2-16", 29 | b"h2-15", 30 | b"h2-14", 31 | ]; 32 | 33 | /// An enum representing errors that can arise when performing operations 34 | /// involving an HTTP/2 connection. 35 | #[derive(Debug)] 36 | pub enum HttpError { 37 | IoError(io::Error), 38 | UnknownFrameType, 39 | InvalidFrame, 40 | CompressionError(DecoderError), 41 | UnknownStreamId, 42 | UnableToConnect, 43 | MalformedResponse, 44 | } 45 | 46 | /// Implement the trait that allows us to automatically convert `io::Error`s 47 | /// into an `HttpError` by wrapping the given `io::Error` into an `HttpError::IoError` variant. 48 | impl From for HttpError { 49 | fn from(err: io::Error) -> HttpError { 50 | HttpError::IoError(err) 51 | } 52 | } 53 | 54 | /// Implementation of the `PartialEq` trait as a convenience for tests. 55 | #[cfg(test)] 56 | impl PartialEq for HttpError { 57 | fn eq(&self, other: &HttpError) -> bool { 58 | match (self, other) { 59 | (&HttpError::IoError(ref e1), &HttpError::IoError(ref e2)) => { 60 | e1.kind() == e2.kind() && e1.description() == e2.description() 61 | }, 62 | (&HttpError::UnknownFrameType, &HttpError::UnknownFrameType) => true, 63 | (&HttpError::InvalidFrame, &HttpError::InvalidFrame) => true, 64 | (&HttpError::CompressionError(ref e1), &HttpError::CompressionError(ref e2)) => { 65 | e1 == e2 66 | }, 67 | (&HttpError::UnknownStreamId, &HttpError::UnknownStreamId) => true, 68 | (&HttpError::UnableToConnect, &HttpError::UnableToConnect) => true, 69 | (&HttpError::MalformedResponse, &HttpError::MalformedResponse) => true, 70 | _ => false, 71 | } 72 | } 73 | } 74 | 75 | /// A convenience `Result` type that has the `HttpError` type as the error 76 | /// type and a generic Ok result type. 77 | pub type HttpResult = Result; 78 | 79 | /// An enum representing the two possible HTTP schemes. 80 | #[derive(Debug, Copy, Clone, PartialEq)] 81 | pub enum HttpScheme { 82 | /// The variant corresponding to `http://` 83 | Http, 84 | /// The variant corresponding to `https://` 85 | Https, 86 | } 87 | 88 | impl HttpScheme { 89 | /// Returns a byte string representing the scheme. 90 | #[inline] 91 | pub fn as_bytes(&self) -> &'static [u8] { 92 | match *self { 93 | HttpScheme::Http => b"http", 94 | HttpScheme::Https => b"https", 95 | } 96 | } 97 | } 98 | 99 | /// A struct representing the full raw response received on an HTTP/2 connection. 100 | /// 101 | /// The full body of the response is included, regardless how large it may be. 102 | /// The headers contain both the meta-headers, as well as the actual headers. 103 | #[derive(Clone)] 104 | pub struct Response { 105 | /// The ID of the stream to which the response is associated. HTTP/1.1 does 106 | /// not really have an equivalent to this. 107 | pub stream_id: StreamId, 108 | /// Exposes *all* the raw response headers, including the meta-headers. 109 | /// (For now the only meta header allowed in HTTP/2 responses is the 110 | /// `:status`.) 111 | pub headers: Vec
, 112 | /// The full body of the response as an uninterpreted sequence of bytes. 113 | pub body: Vec, 114 | } 115 | 116 | impl Response { 117 | /// Creates a new `Response` with all the components already provided. 118 | pub fn new(stream_id: StreamId, headers: Vec
, body: Vec) 119 | -> Response { 120 | Response { 121 | stream_id: stream_id, 122 | headers: headers, 123 | body: body, 124 | } 125 | } 126 | 127 | /// Gets the response status code from the pseudo-header. If the response 128 | /// does not contain the response as the first pseuo-header, an error is 129 | /// returned as such a response is malformed. 130 | pub fn status_code(&self) -> HttpResult { 131 | // Since pseudo-headers MUST be found before any regular header fields 132 | // and the *only* pseudo-header defined for responses is the `:status` 133 | // field, the `:status` MUST be the first header; otherwise, the 134 | // response is malformed. 135 | if self.headers.len() < 1 { 136 | return Err(HttpError::MalformedResponse) 137 | } 138 | if &self.headers[0].0 != &b":status" { 139 | Err(HttpError::MalformedResponse) 140 | } else { 141 | Ok(try!(Response::parse_status_code(&self.headers[0].1))) 142 | } 143 | } 144 | 145 | /// A helper function that parses a given buffer as a status code and 146 | /// returns it as a `u16`, if it is valid. 147 | fn parse_status_code(buf: &[u8]) -> HttpResult { 148 | // "The status-code element is a three-digit integer code [...]" 149 | if buf.len() != 3 { 150 | return Err(HttpError::MalformedResponse); 151 | } 152 | 153 | // "There are five values for the first digit" 154 | if buf[0] < b'1' || buf[0] > b'5' { 155 | return Err(HttpError::MalformedResponse); 156 | } 157 | 158 | // The rest of them just have to be digits 159 | if buf[1] < b'0' || buf[1] > b'9' || buf[2] < b'0' || buf[2] > b'9' { 160 | return Err(HttpError::MalformedResponse); 161 | } 162 | 163 | // Finally, we can merge them into an integer 164 | Ok(100 * ((buf[0] - b'0') as u16) + 165 | 10 * ((buf[1] - b'0') as u16) + 166 | 1 * ((buf[2] - b'0') as u16)) 167 | } 168 | } 169 | 170 | /// A struct representing a full HTTP/2 request, along with the full body, as a 171 | /// sequence of bytes. 172 | #[derive(Clone)] 173 | pub struct Request { 174 | pub stream_id: u32, 175 | pub headers: Vec
, 176 | pub body: Vec, 177 | } 178 | 179 | #[cfg(test)] 180 | mod tests { 181 | use super::{Response, HttpError, HttpScheme}; 182 | 183 | /// Tests that the `Response` struct correctly parses a status code from 184 | /// its headers list. 185 | #[test] 186 | fn test_parse_status_code_response() { 187 | { 188 | // Only status => Ok 189 | let resp = Response::new( 190 | 1, 191 | vec![(b":status".to_vec(), b"200".to_vec())], 192 | vec![]); 193 | assert_eq!(resp.status_code().ok().unwrap(), 200); 194 | } 195 | { 196 | // Extra headers => still works 197 | let resp = Response::new( 198 | 1, 199 | vec![(b":status".to_vec(), b"200".to_vec()), 200 | (b"key".to_vec(), b"val".to_vec())], 201 | vec![]); 202 | assert_eq!(resp.status_code().ok().unwrap(), 200); 203 | } 204 | { 205 | // Status is not the first header => malformed 206 | let resp = Response::new( 207 | 1, 208 | vec![(b"key".to_vec(), b"val".to_vec()), 209 | (b":status".to_vec(), b"200".to_vec())], 210 | vec![]); 211 | assert_eq!(resp.status_code().err().unwrap(), 212 | HttpError::MalformedResponse); 213 | } 214 | { 215 | // No headers at all => Malformed 216 | let resp = Response::new(1, vec![], vec![]); 217 | assert_eq!(resp.status_code().err().unwrap(), 218 | HttpError::MalformedResponse); 219 | } 220 | } 221 | 222 | /// Tests that the `HttpScheme` enum returns the correct scheme strings for 223 | /// the two variants. 224 | #[test] 225 | fn test_scheme_string() { 226 | assert_eq!(HttpScheme::Http.as_bytes(), b"http"); 227 | assert_eq!(HttpScheme::Https.as_bytes(), b"https"); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/http/frame/pingframe.rs: -------------------------------------------------------------------------------- 1 | use super::super::StreamId; 2 | use super::frames::{ 3 | Frame, 4 | Flag, 5 | RawFrame, 6 | FrameHeader, 7 | pack_header 8 | }; 9 | 10 | /// An enum representing the flags that a `PingFrame` can have. 11 | /// The integer representation associated to each variant is that flag's 12 | /// bitmask. 13 | /// 14 | /// HTTP/2 spec, section 6. 15 | #[derive(Clone)] 16 | #[derive(PartialEq)] 17 | #[derive(Debug)] 18 | #[derive(Copy)] 19 | pub enum PingFlag { 20 | Ack = 0x1, 21 | } 22 | 23 | impl Flag for PingFlag { 24 | #[inline] 25 | fn bitmask(&self) -> u8 { 26 | *self as u8 27 | } 28 | } 29 | 30 | /// A struct representing the DATA frames of HTTP/2, as defined in the HTTP/2 31 | /// spec, section 6.1. 32 | #[derive(PartialEq)] 33 | #[derive(Debug)] 34 | pub struct PingFrame { 35 | /// The data found in the frame as an opaque byte sequence. It never 36 | /// includes padding bytes. 37 | pub data: Vec, 38 | /// Represents the flags currently set on the `DataFrame`, packed into a 39 | /// single byte. 40 | flags: u8, 41 | } 42 | 43 | impl PingFrame { 44 | /// Creates a new empty `PingFrame`, associated to the connection 45 | pub fn new() -> PingFrame { 46 | PingFrame { 47 | // No data stored in the frame yet 48 | data: Vec::new(), 49 | // All flags unset by default 50 | flags: 0, 51 | } 52 | } 53 | 54 | /// A convenience constructor that returns a `PingFrame` with the ACK 55 | /// flag already set and no data. 56 | pub fn new_ack() -> PingFrame { 57 | PingFrame { 58 | data: Vec::new(), 59 | flags: PingFlag::Ack.bitmask(), 60 | } 61 | } 62 | 63 | /// Sets the ACK flag for the frame. This method is just a convenience 64 | /// method for calling `frame.set_flag(PingFlag::Ack)`. 65 | pub fn set_ack(&mut self) { 66 | self.set_flag(PingFlag::Ack) 67 | } 68 | 69 | /// Checks whether the `PingFrame` has an ACK flag attached to it. 70 | pub fn is_ack(&self) -> bool { 71 | self.is_set(PingFlag::Ack) 72 | } 73 | 74 | /// Returns the total length of the payload in bytes 75 | fn payload_len(&self) -> u32 { 76 | self.data.len() as u32 77 | } 78 | 79 | /// Parses the given slice as a PING frame's payload. 80 | /// 81 | /// # Returns 82 | /// 83 | /// A `Vec` of opaque data 84 | /// 85 | /// If the payload was invalid (i.e. the length of the payload is 86 | /// not 8 octets long, returns `None` 87 | fn parse_payload(payload: &[u8]) -> Option<(Vec)> { 88 | if payload.len() != 8 { 89 | return None; 90 | } 91 | let data = payload; 92 | 93 | Some(data.to_vec()) 94 | } 95 | } 96 | 97 | impl Frame for PingFrame { 98 | /// The type that represents the flags that the particular `Frame` can take. 99 | /// This makes sure that only valid `Flag`s are used with each `Frame`. 100 | type FlagType = PingFlag; 101 | 102 | /// Creates a new `PingFrame` with the given `RawFrame` (i.e. header and payload), 103 | /// if possible. 104 | /// 105 | /// # Returns 106 | /// 107 | /// `None` if a valid `PingFrame` cannot be contructed from the given 108 | /// `RawFrame`. The stream ID *MUST* be 0 in order for the frame to be 109 | /// valid. If the `ACK` flag is set, there *MUST NOT* be a payload. The total 110 | /// payload length must be 8 bytes long. 111 | /// 112 | /// Otherwise, returns a newly constructed `PingFrame`. 113 | fn from_raw(raw_frame: RawFrame) -> Option { 114 | // Unpack the header 115 | let (len, frame_type, flags, stream_id) = raw_frame.header; 116 | // Check that the frame type is correct for this fram implementation 117 | if frame_type != 0x6 { 118 | return None; 119 | } 120 | // Check that the length given in the header mathes the payload 121 | // length; if not, something went wrong and we do not consider this a 122 | // valid frame. 123 | if (len as usize) != raw_frame.payload.len() { 124 | return None; 125 | } 126 | // Check that the PING frame is associated to stream 0 127 | if stream_id != 0 { 128 | return None; 129 | } 130 | if (flags & PingFlag::Ack.bitmask()) != 0 { 131 | if len != 0 { 132 | // The PING flag MUST NOT have a payload if Ack is set 133 | return None; 134 | } else { 135 | // Ack is set and there's no payload => just an Ack frame 136 | return Some(PingFrame { 137 | data: Vec::new(), 138 | flags: flags, 139 | }); 140 | } 141 | } 142 | 143 | match PingFrame::parse_payload(&raw_frame.payload) { 144 | Some(data) => { 145 | // The data extracted 146 | Some(PingFrame { 147 | flags: flags, 148 | data: data, 149 | }) 150 | } 151 | None => None, 152 | } 153 | } 154 | 155 | /// Tests if the given flag is set for the frame. 156 | fn is_set(&self, flag: PingFlag) -> bool { 157 | (self.flags & flag.bitmask()) != 0 158 | } 159 | 160 | /// Returns the `StreamId` of the stream to which the frame is associated. 161 | /// 162 | /// A `PingFrame` always has to be associated to stream `0`. 163 | fn get_stream_id(&self) -> StreamId { 164 | 0 165 | } 166 | 167 | /// Returns a `FrameHeader` based on the current state of the `Frame`. 168 | fn get_header(&self) -> FrameHeader { 169 | (self.payload_len(), 0x6, self.flags, 0) 170 | } 171 | 172 | /// Sets the given flag for the frame. 173 | fn set_flag(&mut self, flag: PingFlag) { 174 | self.flags |= flag.bitmask(); 175 | } 176 | 177 | /// Returns a `Vec` with the serialized representation of the frame. 178 | fn serialize(&self) -> Vec { 179 | let mut buf = Vec::with_capacity(self.payload_len() as usize); 180 | // First the header 181 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter()); 182 | // now the body 183 | buf.extend(self.data.clone().into_iter()); 184 | 185 | buf 186 | } 187 | 188 | } 189 | 190 | #[cfg(test)] 191 | mod tests { 192 | use super::super::frames::{Frame, RawFrame, pack_header}; 193 | use super::super::test::{build_test_frame}; 194 | use super::{PingFlag, PingFrame}; 195 | 196 | /// Tests that the `PingFrame` correctly handles a PING frame with 197 | /// no ACK flag and a payload 198 | #[test] 199 | fn test_ping_frame_parse_no_ack() { 200 | let payload = [0, 0, 0, 0, 1, 0, 0, 0]; 201 | // A header with no ACK flag 202 | let header = (payload.len() as u32, 6, 0, 0); 203 | 204 | let frame = build_test_frame::(&header, &payload); 205 | 206 | assert_eq!(frame.get_header(), header); 207 | } 208 | 209 | /// Tests that the `PingFrame` correctly handles a PING frame which 210 | /// was not associated to stream 0 by returning an error. 211 | #[test] 212 | fn test_ping_frame_parse_not_stream_zero() { 213 | let payload = vec![1, 2, 3, 4, 5, 6, 7, 8]; 214 | // Header indicates that it is associated to stream 1 215 | let header = (payload.len() as u32, 6, 0, 1); 216 | 217 | let frame: Option = Frame::from_raw( 218 | RawFrame::with_payload(header, payload)); 219 | 220 | assert!(frame.is_none()); 221 | } 222 | 223 | /// Tests that the `PingFrame` correctly handles a PING frame which 224 | /// does not have a payload of 8 bytes 225 | #[test] 226 | fn test_ping_frame_parse_not_eight_bytes() { 227 | let payload = vec![2, 4, 6, 8]; 228 | 229 | let header = (payload.len() as u32, 6, 0, 0); 230 | 231 | let frame: Option = Frame::from_raw( 232 | RawFrame::with_payload(header, payload) 233 | ); 234 | 235 | assert!(frame.is_none()); 236 | } 237 | 238 | /// Tests that a `PingFrame` gets correctly serialized when it contains 239 | /// a payload and ACK. 240 | #[test] 241 | fn test_ping_frame_serialize_payload_ack() { 242 | let mut frame = PingFrame::new_ack(); 243 | let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; 244 | frame.data = data.clone(); 245 | let expected = { 246 | let headers = pack_header(&(8, 6, 1, 0)); 247 | let mut res: Vec = Vec::new(); 248 | res.extend(headers.to_vec().into_iter()); 249 | res.extend(data.into_iter()); 250 | 251 | res 252 | }; 253 | 254 | let serialized = frame.serialize(); 255 | 256 | assert_eq!(serialized, expected); 257 | } 258 | } 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solicit 2 | [![Build Status](https://travis-ci.org/mlalic/solicit.svg?branch=master)](https://travis-ci.org/mlalic/solicit) 3 | 4 | An HTTP/2 implementation in Rust. 5 | 6 | [API Documentation](https://mlalic.github.io/solicit/) 7 | 8 | # Goals 9 | 10 | The main goal of the project is to provide a low-level implementation of the 11 | client side of the HTTP/2 protocol and expose it in a way that higher-level 12 | libraries can make use of it. For example, it should be possible for a higher 13 | level libary to write a very simple adapter that exposes the responses 14 | obtained over an HTTP/2 connection in the same manner as those obtained over 15 | HTTP/1.1. 16 | 17 | The exposed API should make it possible to customize any stage of handling 18 | an HTTP/2 response, such as adding custom handlers for partial response data 19 | (to make it viable to either save the response in memory, stream it to a 20 | file, or just notify a different process on every new chunk or even something 21 | else entirely). 22 | 23 | The library itself should never spawn any threads, but the primitives exposed 24 | by it should be flexible enough to allow a multi-threaded client implementation 25 | (one where requests can be made from different threads, using the same underlying 26 | HTTP/2 connection). 27 | 28 | Extensive test coverage is also a major goal. No code is committed without 29 | accompanying tests. 30 | 31 | At this stage, performance was not considered as one of the primary goals. 32 | 33 | # Status 34 | 35 | Only a small subset of the full HTTP/2 spec is implemented so far, however it 36 | is already possible to issue requests and read their corresponding responses. 37 | 38 | 39 | Some features that are implemented: 40 | 41 | - Connection establishment: both cleartext TCP (with prior knowledge), as well 42 | as TLS-protected (and negotiated) connections are supported. 43 | - HPACK compression and decompression: based on the 44 | [`hpack-rs`](https://github.com/mlalic/hpack-rs) crate (which was extracted from 45 | `solicit`). 46 | - The framing layer correctly handles incoming frames, discarding frame types for which 47 | the handling (parsing, processing) is not yet implemented. 48 | Handling is implemented for `DATA`, `HEADERS`, and `SETTINGS` frames. 49 | - Frame serialization is also implemented for the aforementioned 3 frame types. 50 | 51 | 52 | # Examples 53 | 54 | As mentioned in the goals section, this library does not aim to provide a 55 | full high-level client implementation (no caching, no automatic redirect 56 | following, no strongly-typed headers, etc.). Rather, it implements the lower 57 | level details of an HTTP/2 connection -- what is essentially an additional 58 | transport layer on top of the socket connection -- and exposes an API that 59 | allows a full-featured client to be built on top of that. 60 | 61 | However, in order to showcase how such clients might be built and in order to 62 | make sure that the main goals for such clients are achievable, there are two 63 | example implementations (included in the 64 | [`solicit::client`](https://github.com/mlalic/solicit/blob/master/src/client/mod.rs) 65 | module) built on top of the underlying abstractions of the 66 | [`solicit::http`](https://github.com/mlalic/solicit/blob/master/src/http/mod.rs) 67 | module. 68 | 69 | ## Simple Client 70 | 71 | The [simple client](https://github.com/mlalic/solicit/blob/master/src/client/simple.rs) 72 | implementation allows users to issue a number of requests before blocking to 73 | read one of the responses. After a response is received, more requests can 74 | be sent over the same connection; however, requests cannot be queued while a 75 | response is being read. 76 | 77 | In a way, this is similar to how HTTP/1.1 connections with keep-alive (and 78 | pipelining) work. 79 | 80 | ### Example 81 | 82 | A clear-text (`http://`) connection. 83 | 84 | ```rust 85 | use solicit::http::connection::CleartextConnector; 86 | use solicit::client::SimpleClient; 87 | use std::str; 88 | // Connect to an HTTP/2 aware server 89 | let connector = CleartextConnector { host: "http2bin.org" }; 90 | let mut client = SimpleClient::with_connector(connector).unwrap(); 91 | // This blocks until the response is received... 92 | let response = client.get(b"/get", &[]).unwrap(); 93 | assert_eq!(response.stream_id, 1); 94 | assert_eq!(response.status_code().unwrap(), 200); 95 | // Dump the headers and the response body to stdout. 96 | // They are returned as raw bytes for the user to do as they please. 97 | // (Note: in general directly decoding assuming a utf8 encoding might not 98 | // always work -- this is meant as a simple example that shows that the 99 | // response is well formed.) 100 | for header in response.headers.iter() { 101 | println!("{}: {}", 102 | str::from_utf8(&header.0).unwrap(), 103 | str::from_utf8(&header.1).unwrap()); 104 | } 105 | println!("{}", str::from_utf8(&response.body).unwrap()); 106 | // We can issue more requests after reading this one... 107 | // These calls block until the request itself is sent, but do not wait 108 | // for a response. 109 | let req_id1 = client.request(b"GET", b"/", &[]).unwrap(); 110 | let req_id2 = client.request(b"GET", b"/asdf", &[]).unwrap(); 111 | // Now we get a response for both requests... This does block. 112 | let (resp1, resp2) = ( 113 | client.get_response(req_id1).unwrap(), 114 | client.get_response(req_id2).unwrap(), 115 | ); 116 | assert_eq!(resp1.status_code().unwrap(), 200); 117 | assert_eq!(resp2.status_code().unwrap(), 404); 118 | ``` 119 | 120 | A TLS-protected (and negotiated) `https://` connection. 121 | 122 | ```rust 123 | use solicit::http::connection::TlsConnector; 124 | use solicit::client::SimpleClient; 125 | use std::str; 126 | // Connect to an HTTP/2 aware server 127 | let certs_path = "/path/to/certs.pem"; 128 | let connector = TlsConnector::new("http2bin.org", &certs_path); 129 | let mut client = SimpleClient::with_connector(connector).unwrap(); 130 | // This blocks until the response is received... 131 | let response = client.get(b"/get", &[]).unwrap(); 132 | assert_eq!(response.stream_id, 1); 133 | assert_eq!(response.status_code().unwrap(), 200); 134 | // Dump the headers and the response body to stdout. 135 | // They are returned as raw bytes for the user to do as they please. 136 | // (Note: in general directly decoding assuming a utf8 encoding might not 137 | // always work -- this is meant as a simple example that shows that the 138 | // response is well formed.) 139 | for header in response.headers.iter() { 140 | println!("{}: {}", 141 | str::from_utf8(&header.0).unwrap(), 142 | str::from_utf8(&header.1).unwrap()); 143 | } 144 | println!("{}", str::from_utf8(&response.body).unwrap()); 145 | // We can issue more requests after reading this one... 146 | // These calls block until the request itself is sent, but do not wait 147 | // for a response. 148 | let req_id1 = client.request(b"GET", b"/", &[]).unwrap(); 149 | let req_id2 = client.request(b"GET", b"/asdf", &[]).unwrap(); 150 | // Now we get a response for both requests... This does block. 151 | let (resp1, resp2) = ( 152 | client.get_response(req_id1).unwrap(), 153 | client.get_response(req_id2).unwrap(), 154 | ); 155 | assert_eq!(resp1.status_code().unwrap(), 200); 156 | assert_eq!(resp2.status_code().unwrap(), 404); 157 | ``` 158 | 159 | For how it leverages the `solicit::http` API for its implementation, check out the 160 | [`solicit::client::simple`](https://github.com/mlalic/solicit/blob/master/src/client/simple.rs) 161 | module. 162 | 163 | ## Async Client 164 | 165 | The [async client](https://github.com/mlalic/solicit/blob/master/src/client/async.rs) 166 | leverages more features specific to HTTP/2, as compared to HTTP/1.1. 167 | 168 | It allows multiple clients to issue requests to the same underlying 169 | connection concurrently. The responses are returned to the clients in the form 170 | of a `Future`, allowing them to block on waiting for the response only once 171 | they don't have anything else to do (which could be immediately after issuing 172 | it). 173 | 174 | This client spawns one background thread per HTTP/2 connection, which exits 175 | gracefully once there are no more clients connected to it (and thus no more 176 | potential requests can be issued) or the HTTP/2 connection returns an error. 177 | 178 | This client implementation is also just an example of what can be achieved 179 | using the `solicit::htp` API -- see: 180 | [`solicit::client::async`](https://github.com/mlalic/solicit/blob/master/src/client/async.rs) 181 | 182 | ### Example 183 | 184 | ```rust 185 | #![feature(std_misc)] 186 | 187 | use solicit::client::Client; 188 | use std::thread; 189 | use std::str; 190 | 191 | // Connect to a server that supports HTTP/2 192 | let client = Client::new("nghttp2.org", 80).unwrap(); 193 | 194 | // Issue 5 requests from 5 different threads concurrently and wait for all 195 | // threads to receive their response. 196 | let _: Vec<_> = (0..5).map(|i| { 197 | let this = client.clone(); 198 | thread::scoped(move || { 199 | // This call returns immediately... 200 | let resp = this.get(b"/", &[]).unwrap(); 201 | // ...this one blocks until the full response is ready! 202 | let response = resp.into_inner(); 203 | println!("Thread {} got response ... {}", i, response.status_code().unwrap()); 204 | println!("The response contains the following headers:"); 205 | for header in response.headers.iter() { 206 | println!(" {}: {}", 207 | str::from_utf8(&header.0).unwrap(), 208 | str::from_utf8(&header.1).unwrap()); 209 | } 210 | }) 211 | }).collect(); 212 | ``` 213 | 214 | # License 215 | 216 | The project is published under the terms of the [MIT License](https://github.com/mlalic/solicit/blob/master/LICENSE). 217 | -------------------------------------------------------------------------------- /src/http/session.rs: -------------------------------------------------------------------------------- 1 | //! Defines the interface for the session-level management of HTTP/2 2 | //! communication. This is effectively an API that allows hooking into an 3 | //! HTTP/2 connection in order to handle events arising on the connection. 4 | //! 5 | //! It also provides a default implementation of this interface, the 6 | //! `DefaultSession`. This implementation is based on keeping a mapping of 7 | //! valid stream IDs to instances of `Stream` objects. When the session 8 | //! receives a callback for a particular stream ID, it first validates that 9 | //! it represents a valid stream ID and then delegates to the appropriate 10 | //! action of a `Stream`. This allows clients to easily vary the stream-level 11 | //! logic, without worrying about handling the book-keeping tasks of which 12 | //! streams are active. 13 | use std::collections::HashMap; 14 | use std::iter::FromIterator; 15 | use super::{StreamId, Header}; 16 | 17 | /// A trait that defines methods that need to be defined in order to track the 18 | /// status of a `ClientConnection`. 19 | /// 20 | /// These methods are effectively callbacks that the `ClientConnection` invokes 21 | /// on particular events in the HTTP/2 frame stream. 22 | /// 23 | /// TODO Allow the session to influence the `ClientConnection` state and raise 24 | /// errors (i.e. make the return type -> HttpResult<()>. 25 | pub trait Session { 26 | /// Notifies the `Session` that a new data chunk has arrived on the 27 | /// connection for a particular stream. Only the raw data is passed 28 | /// to the callback (all padding is already discarded by the connection). 29 | fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]); 30 | /// Notifies the `Session` that headers have arrived for a particular 31 | /// stream. The given list of headers is already decoded by the connection. 32 | fn new_headers(&mut self, stream_id: StreamId, headers: Vec
); 33 | /// Notifies the `Session` that a particular stream got closed by the peer. 34 | fn end_of_stream(&mut self, stream_id: StreamId); 35 | } 36 | 37 | /// A trait representing a single HTTP/2 client stream. An HTTP/2 connection 38 | /// multiplexes a number of streams. 39 | /// 40 | /// The trait defines which operations need to be defined by a type that should 41 | /// be useable as an HTTP/2 stream. By implementing this trait, clients can only 42 | /// implement stream-level logic, such as how the received data should be handled, 43 | /// instead of tracking which streams exist and what their states are. 44 | pub trait Stream { 45 | /// Create a new stream with the given ID 46 | fn new(stream_id: StreamId) -> Self; 47 | /// Handle a new data chunk that has arrived for the stream. 48 | fn new_data_chunk(&mut self, data: &[u8]); 49 | /// Set headers for a stream. A stream is only allowed to have one set of 50 | /// headers. 51 | fn set_headers(&mut self, headers: Vec
); 52 | /// Close the stream. 53 | fn close(&mut self); 54 | 55 | /// Returns the ID of the stream. 56 | fn id(&self) -> StreamId; 57 | /// Returns whether the stream is closed. 58 | fn is_closed(&self) -> bool; 59 | } 60 | 61 | /// An implementation of the `Stream` trait that saves all headers and data 62 | /// in memory. 63 | #[derive(Clone)] 64 | pub struct DefaultStream { 65 | /// The ID of the stream 66 | pub stream_id: StreamId, 67 | /// The headers associated with the stream (i.e. the response headers) 68 | pub headers: Option>, 69 | /// The body of the stream (i.e. the response body) 70 | pub body: Vec, 71 | /// Whether the stream is already closed 72 | pub closed: bool, 73 | } 74 | 75 | impl DefaultStream { 76 | /// Create a new `DefaultStream` with the given ID. 77 | pub fn new(stream_id: StreamId) -> DefaultStream { 78 | DefaultStream { 79 | stream_id: stream_id, 80 | headers: None, 81 | body: Vec::new(), 82 | closed: false, 83 | } 84 | } 85 | } 86 | 87 | impl Stream for DefaultStream { 88 | fn new(stream_id: StreamId) -> DefaultStream { 89 | DefaultStream::new(stream_id) 90 | } 91 | 92 | fn new_data_chunk(&mut self, data: &[u8]) { 93 | self.body.extend(data.to_vec().into_iter()); 94 | } 95 | 96 | fn set_headers(&mut self, headers: Vec
) { 97 | self.headers = Some(headers); 98 | } 99 | 100 | fn close(&mut self) { 101 | self.closed = true; 102 | } 103 | 104 | fn id(&self) -> StreamId { 105 | self.stream_id 106 | } 107 | 108 | fn is_closed(&self) -> bool { 109 | self.closed 110 | } 111 | } 112 | 113 | /// A simple implementation of the `Session` trait. 114 | /// 115 | /// Keeps track of which streams are currently active by holding a `HashMap` 116 | /// of stream IDs to `Stream` instances. Callbacks delegate to the corresponding 117 | /// stream instance, after validating the received stream ID. 118 | /// 119 | /// The purpose of the type is to make it easier for client implementations to 120 | /// only handle stream-level events by providing a `Stream` implementation, 121 | /// instead of having to implement the entire session management (tracking active 122 | /// streams, etc.). 123 | /// 124 | /// For example, by varying the `Stream` implementation it is easy to implement 125 | /// a client that streams responses directly into a file on the local file system, 126 | /// instead of keeping it in memory (like the `DefaultStream` does), without 127 | /// having to change any HTTP/2-specific logic. 128 | pub struct DefaultSession where S: Stream { 129 | streams: HashMap, 130 | } 131 | 132 | impl DefaultSession where S: Stream { 133 | /// Returns a new `DefaultSession` with no active streams. 134 | pub fn new() -> DefaultSession { 135 | DefaultSession { 136 | streams: HashMap::new(), 137 | } 138 | } 139 | 140 | /// Returns a reference to a stream with the given ID, if such a stream is 141 | /// found in the `DefaultSession`. 142 | pub fn get_stream(&self, stream_id: StreamId) -> Option<&S> { 143 | self.streams.get(&stream_id) 144 | } 145 | 146 | /// Creates a new stream with the given ID in the session. 147 | pub fn new_stream(&mut self, stream_id: StreamId) { 148 | self.streams.insert(stream_id, Stream::new(stream_id)); 149 | } 150 | 151 | /// Returns all streams that are closed and tracked by the session. 152 | /// 153 | /// The streams are moved out of the session. 154 | pub fn get_closed(&mut self) -> Vec { 155 | let ids: Vec<_> = self.streams.iter() 156 | .filter_map(|(i, s)| { 157 | if s.is_closed() { Some(*i) } else { None } 158 | }) 159 | .collect(); 160 | FromIterator::from_iter(ids.into_iter().map(|i| self.streams.remove(&i).unwrap())) 161 | } 162 | } 163 | 164 | impl Session for DefaultSession where S: Stream { 165 | fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]) { 166 | debug!("Data chunk for stream {}", stream_id); 167 | let mut stream = match self.streams.get_mut(&stream_id) { 168 | None => { 169 | debug!("Received a frame for an unknown stream!"); 170 | return; 171 | }, 172 | Some(stream) => stream, 173 | }; 174 | // Now let the stream handle the data chunk 175 | stream.new_data_chunk(data); 176 | } 177 | 178 | fn new_headers(&mut self, stream_id: StreamId, headers: Vec
) { 179 | debug!("Headers for stream {}", stream_id); 180 | let mut stream = match self.streams.get_mut(&stream_id) { 181 | None => { 182 | debug!("Received a frame for an unknown stream!"); 183 | return; 184 | }, 185 | Some(stream) => stream, 186 | }; 187 | // Now let the stream handle the headers 188 | stream.set_headers(headers); 189 | } 190 | 191 | fn end_of_stream(&mut self, stream_id: StreamId) { 192 | debug!("End of stream {}", stream_id); 193 | let mut stream = match self.streams.get_mut(&stream_id) { 194 | None => { 195 | debug!("Received a frame for an unknown stream!"); 196 | return; 197 | }, 198 | Some(stream) => stream, 199 | }; 200 | stream.close() 201 | } 202 | } 203 | 204 | #[cfg(test)] 205 | mod tests { 206 | use super::{ 207 | Session, DefaultSession, 208 | Stream, 209 | }; 210 | 211 | /// Tests that a `DefaultSession` notifies the correct stream when the 212 | /// appropriate callback is invoked. 213 | /// 214 | /// A better unit test would give a mock Stream to the `DefaultSession`, 215 | /// instead of testing both the `DefaultSession` and the `DefaultStream` 216 | /// in the same time... 217 | #[test] 218 | fn test_default_session_notifies_stream() { 219 | let mut session: DefaultSession = DefaultSession::new(); 220 | session.new_stream(1); 221 | 222 | // Registering some data to stream 1... 223 | session.new_data_chunk(1, &[1, 2, 3]); 224 | // ...works. 225 | assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3]); 226 | // Some more... 227 | session.new_data_chunk(1, &[4]); 228 | // ...works. 229 | assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3, 4]); 230 | // Now headers? 231 | let headers = vec![(b":method".to_vec(), b"GET".to_vec())]; 232 | session.new_headers(1, headers.clone()); 233 | assert_eq!(session.get_stream(1).unwrap().headers.clone().unwrap(), 234 | headers); 235 | // Add another stream in the mix 236 | session.new_stream(3); 237 | // and send it some data 238 | session.new_data_chunk(3, &[100]); 239 | assert_eq!(session.get_stream(3).unwrap().body, vec![100]); 240 | // Finally, the stream 1 ends... 241 | session.end_of_stream(1); 242 | // ...and gets closed. 243 | assert!(session.get_stream(1).unwrap().closed); 244 | // but not the other one. 245 | assert!(!session.get_stream(3).unwrap().closed); 246 | // Sanity check: both streams still found in the session 247 | assert_eq!(session.streams.len(), 2); 248 | // The closed stream is returned... 249 | let closed = session.get_closed(); 250 | assert_eq!(closed.len(), 1); 251 | assert_eq!(closed[0].id(), 1); 252 | // ...and is also removed from the session! 253 | assert_eq!(session.streams.len(), 1); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/client/simple.rs: -------------------------------------------------------------------------------- 1 | //! The module contains an implementation of a simple HTTP/2 client. 2 | 3 | use super::super::http::connection::ClientConnection; 4 | use super::super::http::connection::HttpConnection; 5 | use super::super::http::connection::HttpConnect; 6 | use super::super::http::transport::TransportStream; 7 | use super::super::http::session::{DefaultSession, Stream}; 8 | use super::super::http::{StreamId, HttpResult, HttpError, Response, Header, Request}; 9 | 10 | 11 | /// A struct implementing a simple HTTP/2 client. 12 | /// 13 | /// This client works as an HTTP/1.1 client with a Keep-Alive connection and 14 | /// pipelining might work. 15 | /// 16 | /// Multiple requests can be queued up (and sent to the server) by calling 17 | /// `request` multiple times, before any `get_response`. 18 | /// 19 | /// Once a `get_response` is issued, the client blocks until it receives the 20 | /// response for the particular request that was referenced in the `get_response` 21 | /// call. 22 | /// 23 | /// Therefore, by doing `request` -> `get_response` we can use the HTTP/2 24 | /// connection as a `Keep-Alive` HTTP/1.1 connection and a pipelined flow by 25 | /// queuing up a sequence of requests and then "joining" over them by calling 26 | /// `get_response` for each of them. 27 | /// 28 | /// The responses that are returned by the client are very raw representations 29 | /// of the response. 30 | /// 31 | /// # Notes 32 | /// 33 | /// - For now (temporarily) no requests with bodies are supported. 34 | /// - For now, only HTTP/2 over a cleartext TCP connection is supported. 35 | /// - A direct HTTP/2 connection is used (not an upgrade of an HTTP/1.1 36 | /// connection) 37 | /// 38 | /// # Examples 39 | /// 40 | /// Issue a simple GET request using the helper `get` method. Premade connection 41 | /// passed to the client. 42 | /// 43 | /// ```no_run 44 | /// use std::net::TcpStream; 45 | /// use solicit::http::HttpScheme; 46 | /// use solicit::http::connection::HttpConnection; 47 | /// use solicit::client::SimpleClient; 48 | /// use std::str; 49 | /// 50 | /// // Connect to an HTTP/2 aware server 51 | /// let conn = HttpConnection::new(TcpStream::connect(&("http2bin.org", 80)).unwrap(), 52 | /// HttpScheme::Http, 53 | /// "http2bin.org".into()); 54 | /// let mut client = SimpleClient::with_connection(conn).unwrap(); 55 | /// let response = client.get(b"/", &[]).unwrap(); 56 | /// assert_eq!(response.stream_id, 1); 57 | /// assert_eq!(response.status_code().unwrap(), 200); 58 | /// // Dump the headers and the response body to stdout. 59 | /// // They are returned as raw bytes for the user to do as they please. 60 | /// // (Note: in general directly decoding assuming a utf8 encoding might not 61 | /// // always work -- this is meant as a simple example that shows that the 62 | /// // response is well formed.) 63 | /// for header in response.headers.iter() { 64 | /// println!("{}: {}", 65 | /// str::from_utf8(&header.0).unwrap(), 66 | /// str::from_utf8(&header.1).unwrap()); 67 | /// } 68 | /// println!("{}", str::from_utf8(&response.body).unwrap()); 69 | /// ``` 70 | /// 71 | /// Issue a simple GET request using the helper `get` method. Pass a connector 72 | /// to establish a new connection. 73 | /// 74 | /// ```no_run 75 | /// use solicit::http::connection::CleartextConnector; 76 | /// use solicit::client::SimpleClient; 77 | /// use std::str; 78 | /// 79 | /// // Connect to an HTTP/2 aware server 80 | /// let connector = CleartextConnector { host: "http2bin.org" }; 81 | /// let mut client = SimpleClient::with_connector(connector).unwrap(); 82 | /// let response = client.get(b"/", &[]).unwrap(); 83 | /// assert_eq!(response.stream_id, 1); 84 | /// assert_eq!(response.status_code().unwrap(), 200); 85 | /// // Dump the headers and the response body to stdout. 86 | /// // They are returned as raw bytes for the user to do as they please. 87 | /// // (Note: in general directly decoding assuming a utf8 encoding might not 88 | /// // always work -- this is meant as a simple example that shows that the 89 | /// // response is well formed.) 90 | /// for header in response.headers.iter() { 91 | /// println!("{}: {}", 92 | /// str::from_utf8(&header.0).unwrap(), 93 | /// str::from_utf8(&header.1).unwrap()); 94 | /// } 95 | /// println!("{}", str::from_utf8(&response.body).unwrap()); 96 | /// ``` 97 | /// 98 | /// Issue a GET request over `https` using the `TlsConnector` 99 | /// 100 | /// ```no_run 101 | /// use solicit::http::connection::TlsConnector; 102 | /// use solicit::client::SimpleClient; 103 | /// use std::str; 104 | /// 105 | /// // Connect to an HTTP/2 aware server 106 | /// let path = "/path/to/certs.pem"; 107 | /// let connector = TlsConnector::new("http2bin.org", &path); 108 | /// let mut client = SimpleClient::with_connector(connector).unwrap(); 109 | /// let response = client.get(b"/get", &[]).unwrap(); 110 | /// assert_eq!(response.stream_id, 1); 111 | /// assert_eq!(response.status_code().unwrap(), 200); 112 | /// // Dump the headers and the response body to stdout. 113 | /// // They are returned as raw bytes for the user to do as they please. 114 | /// // (Note: in general directly decoding assuming a utf8 encoding might not 115 | /// // always work -- this is meant as a simple example that shows that the 116 | /// // response is well formed.) 117 | /// for header in response.headers.iter() { 118 | /// println!("{}: {}", 119 | /// str::from_utf8(&header.0).unwrap(), 120 | /// str::from_utf8(&header.1).unwrap()); 121 | /// } 122 | /// println!("{}", str::from_utf8(&response.body).unwrap()); 123 | /// ``` 124 | #[unstable = "This is unstable"] 125 | pub struct SimpleClient where S: TransportStream { 126 | /// The underlying `ClientConnection` that the client uses 127 | conn: ClientConnection, 128 | /// Holds the ID that can be assigned to the next stream to be opened by the 129 | /// client. 130 | next_stream_id: u32, 131 | } 132 | 133 | impl SimpleClient where S: TransportStream { 134 | /// Create a new `SimpleClient` instance that will use the given `HttpConnection` 135 | /// to communicate to the server. 136 | /// 137 | /// It assumes that the connection is in an uninitialized state and will try 138 | /// to send the client preface and make sure it receives the server preface 139 | /// before returning. 140 | pub fn with_connection(conn: HttpConnection) -> HttpResult> { 141 | let mut client = SimpleClient { 142 | conn: ClientConnection::with_connection(conn, DefaultSession::new()), 143 | next_stream_id: 1, 144 | }; 145 | 146 | try!(client.init()); 147 | 148 | Ok(client) 149 | } 150 | 151 | /// A convenience constructor that first tries to establish an HTTP/2 152 | /// connection by using the given connector instance (an implementation of 153 | /// the `HttpConnect` trait). 154 | /// 155 | /// # Panics 156 | /// 157 | /// Currently, it panics if the connector returns an error. 158 | pub fn with_connector(connector: C) -> HttpResult> 159 | where C: HttpConnect { 160 | let conn = connector.connect().ok().unwrap(); 161 | SimpleClient::with_connection(conn) 162 | } 163 | 164 | /// Internal helper method that performs the initialization of the client's 165 | /// connection. 166 | #[inline] 167 | fn init(&mut self) -> HttpResult<()> { 168 | self.conn.init() 169 | } 170 | 171 | /// Send a request to the server. Blocks until the entire request has been 172 | /// sent. 173 | /// 174 | /// The request is described by the method, the path on which it should be 175 | /// invoked and the "real" headers that should be included. Clients should 176 | /// never put pseudo-headers in the `headers` parameter, as those are 177 | /// automatically included based on metadata. 178 | /// 179 | /// # Returns 180 | /// 181 | /// If the full request is successfully sent, returns the ID of the stream 182 | /// on which the request was sent. Clients can use this ID to refer to the 183 | /// response. 184 | /// 185 | /// Any IO errors are propagated. 186 | pub fn request(&mut self, method: &[u8], path: &[u8], extras: &[Header]) 187 | -> HttpResult { 188 | let stream_id = self.new_stream(); 189 | let mut headers: Vec
= vec![ 190 | (b":method".to_vec(), method.to_vec()), 191 | (b":path".to_vec(), path.to_vec()), 192 | (b":authority".to_vec(), self.conn.host().as_bytes().to_vec()), 193 | (b":scheme".to_vec(), self.conn.scheme().as_bytes().to_vec()), 194 | ]; 195 | headers.extend(extras.to_vec().into_iter()); 196 | 197 | try!(self.conn.send_request(Request { 198 | stream_id: stream_id, 199 | headers: headers, 200 | body: Vec::new(), 201 | })); 202 | 203 | Ok(stream_id) 204 | } 205 | 206 | /// Gets the response for the stream with the given ID. If a valid stream ID 207 | /// is given, it blocks until a response is received. 208 | /// 209 | /// # Returns 210 | /// 211 | /// A `Response` if the response can be successfully read. 212 | /// 213 | /// Any underlying IO errors are propagated. Errors in the HTTP/2 protocol 214 | /// also stop processing and are returned to the client. 215 | pub fn get_response(&mut self, stream_id: StreamId) -> HttpResult { 216 | match self.conn.session.get_stream(stream_id) { 217 | None => return Err(HttpError::UnknownStreamId), 218 | Some(_) => {}, 219 | }; 220 | loop { 221 | if let Some(stream) = self.conn.session.get_stream(stream_id) { 222 | if stream.is_closed() { 223 | return Ok(Response { 224 | stream_id: stream.id(), 225 | headers: stream.headers.clone().unwrap(), 226 | body: stream.body.clone(), 227 | }); 228 | } 229 | } 230 | try!(self.handle_next_frame()); 231 | } 232 | } 233 | 234 | /// Performs a GET request on the given path. This is a shortcut method for 235 | /// calling `request` followed by `get_response` for the returned stream ID. 236 | pub fn get(&mut self, path: &[u8], extra_headers: &[Header]) 237 | -> HttpResult { 238 | let stream_id = try!(self.request(b"GET", path, extra_headers)); 239 | self.get_response(stream_id) 240 | } 241 | 242 | /// Internal helper method that initializes a new stream and returns its 243 | /// ID once done. 244 | fn new_stream(&mut self) -> StreamId { 245 | let stream_id = self.get_next_stream_id(); 246 | self.conn.session.new_stream(stream_id); 247 | 248 | stream_id 249 | } 250 | 251 | /// Internal helper method that gets the next valid stream ID number. 252 | fn get_next_stream_id(&mut self) -> StreamId { 253 | let ret = self.next_stream_id; 254 | self.next_stream_id += 2; 255 | 256 | ret 257 | } 258 | 259 | /// Internal helper method that triggers the client to handle the next 260 | /// frame off the HTTP/2 connection. 261 | #[inline] 262 | fn handle_next_frame(&mut self) -> HttpResult<()> { 263 | self.conn.handle_next_frame() 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/http/frame/frames.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use super::super::StreamId; 3 | 4 | /// An alias for the 9-byte buffer that each HTTP/2 frame header must be stored 5 | /// in. 6 | pub type FrameHeaderBuffer = [u8; 9]; 7 | /// An alias for the 4-tuple representing the components of each HTTP/2 frame 8 | /// header. 9 | pub type FrameHeader = (u32, u8, u8, u32); 10 | 11 | /// Deconstructs a `FrameHeader` into its corresponding 4 components, 12 | /// represented as a 4-tuple: `(length, frame_type, flags, stream_id)`. 13 | /// 14 | /// The frame `type` and `flags` components are returned as their original 15 | /// octet representation, rather than reinterpreted. 16 | pub fn unpack_header(header: &FrameHeaderBuffer) -> FrameHeader { 17 | let length: u32 = 18 | ((header[0] as u32) << 16) | 19 | ((header[1] as u32) << 8) | 20 | ((header[2] as u32) << 0); 21 | let frame_type = header[3]; 22 | let flags = header[4]; 23 | let stream_id: u32 = unpack_octets_4!(header, 5, u32); 24 | 25 | (length, frame_type, flags, stream_id) 26 | } 27 | 28 | /// Constructs a buffer of 9 bytes that represents the given `FrameHeader`. 29 | pub fn pack_header(header: &FrameHeader) -> FrameHeaderBuffer { 30 | let &(length, frame_type, flags, stream_id) = header; 31 | 32 | [ 33 | (((length >> 16) & 0x000000FF) as u8), 34 | (((length >> 8) & 0x000000FF) as u8), 35 | (((length >> 0) & 0x000000FF) as u8), 36 | frame_type, 37 | flags, 38 | (((stream_id >> 24) & 0x000000FF) as u8), 39 | (((stream_id >> 16) & 0x000000FF) as u8), 40 | (((stream_id >> 8) & 0x000000FF) as u8), 41 | (((stream_id >> 0) & 0x000000FF) as u8), 42 | ] 43 | } 44 | 45 | /// A helper function that parses the given payload, considering it padded. 46 | /// 47 | /// This means that the first byte is the length of the padding with that many 48 | /// 0 bytes expected to follow the actual payload. 49 | /// 50 | /// # Returns 51 | /// 52 | /// A slice of the given payload where the actual one is found and the length 53 | /// of the padding. 54 | /// 55 | /// If the padded payload is invalid (e.g. the length of the padding is equal 56 | /// to the total length), returns `None`. 57 | pub fn parse_padded_payload<'a>(payload: &'a [u8]) -> Option<(&'a [u8], u8)> { 58 | if payload.len() == 0 { 59 | // We make sure not to index the payload before we're sure how 60 | // large the buffer is. 61 | // If this is the case, the frame is invalid as no padding 62 | // length can be extracted, even though the frame should be 63 | // padded. 64 | return None; 65 | } 66 | let pad_len = payload[0] as usize; 67 | if pad_len >= payload.len() { 68 | // This is invalid: the padding length MUST be less than the 69 | // total frame size. 70 | return None; 71 | } 72 | 73 | Some((&payload[1..payload.len() - pad_len], pad_len as u8)) 74 | } 75 | 76 | /// A trait that all HTTP/2 frame header flags need to implement. 77 | pub trait Flag { 78 | /// Returns a bit mask that represents the flag. 79 | fn bitmask(&self) -> u8; 80 | } 81 | 82 | /// A trait that all HTTP/2 frame structs need to implement. 83 | pub trait Frame { 84 | /// The type that represents the flags that the particular `Frame` can take. 85 | /// This makes sure that only valid `Flag`s are used with each `Frame`. 86 | type FlagType: Flag; 87 | 88 | /// Creates a new `Frame` from the given `RawFrame` (i.e. header and 89 | /// payload), if possible. 90 | /// 91 | /// # Returns 92 | /// 93 | /// `None` if a valid `Frame` cannot be constructed from the given 94 | /// `RawFrame`. Some reasons why this may happen is a wrong frame type in 95 | /// the header, a body that cannot be decoded according to the particular 96 | /// frame's rules, etc. 97 | /// 98 | /// Otherwise, returns a newly constructed `Frame`. 99 | fn from_raw(raw_frame: RawFrame) -> Option; 100 | 101 | /// Tests if the given flag is set for the frame. 102 | fn is_set(&self, flag: Self::FlagType) -> bool; 103 | /// Returns the `StreamId` of the stream to which the frame is associated 104 | fn get_stream_id(&self) -> StreamId; 105 | /// Returns a `FrameHeader` based on the current state of the `Frame`. 106 | fn get_header(&self) -> FrameHeader; 107 | 108 | /// Sets the given flag for the frame. 109 | fn set_flag(&mut self, flag: Self::FlagType); 110 | 111 | /// Returns a `Vec` with the serialized representation of the frame. 112 | fn serialize(&self) -> Vec; 113 | } 114 | 115 | /// A struct that defines the format of the raw HTTP/2 frame, i.e. the frame 116 | /// as it is read from the wire. 117 | /// 118 | /// This format is defined in section 4.1. of the HTTP/2 spec. 119 | /// 120 | /// The `RawFrame` struct simply stores the raw components of an HTTP/2 frame: 121 | /// its header and the payload as a sequence of bytes. 122 | /// 123 | /// It does not try to interpret the payload bytes, nor do any validation in 124 | /// terms of its validity based on the frame type given in the header. 125 | /// It is simply a wrapper around the two parts of an HTTP/2 frame. 126 | pub struct RawFrame { 127 | /// The parsed header of the frame. 128 | pub header: FrameHeader, 129 | /// The payload of the frame, as the raw byte sequence, as received on 130 | /// the wire. 131 | pub payload: Vec, 132 | } 133 | 134 | impl RawFrame { 135 | /// Creates a new `RawFrame` with the given `FrameHeader`. The payload is 136 | /// left empty. 137 | pub fn new(header: FrameHeader) -> RawFrame { 138 | RawFrame::with_payload(header, Vec::new()) 139 | } 140 | 141 | /// Creates a new `RawFrame` with the given header and payload. 142 | pub fn with_payload(header: FrameHeader, payload: Vec) -> RawFrame { 143 | RawFrame { 144 | header: header, 145 | payload: payload, 146 | } 147 | } 148 | 149 | /// Creates a new `RawFrame` by parsing the given buffer. 150 | /// 151 | /// # Returns 152 | /// 153 | /// If the first bytes of the buffer represent a valid frame, a `RawFrame` 154 | /// that represents it is returned. Obviously, the buffer should contain 155 | /// both the header and the payload of the frame. 156 | /// 157 | /// If the first bytes of the buffer cannot be interpreted as a raw frame, 158 | /// `None` is returned. This includes the case where the buffer does not 159 | /// contain enough data to contain the entire payload (whose length was 160 | /// advertised in the header). 161 | pub fn from_buf(buf: &[u8]) -> Option { 162 | if buf.len() < 9 { 163 | return None; 164 | } 165 | let header = unpack_header(unsafe { 166 | assert!(buf.len() >= 9); 167 | // We just asserted that this transmute is safe. 168 | mem::transmute(buf.as_ptr()) 169 | }); 170 | let payload_len = header.0 as usize; 171 | 172 | if buf[9..].len() < payload_len { 173 | return None; 174 | } 175 | 176 | Some(RawFrame { 177 | header: header, 178 | payload: buf[9..9 + header.0 as usize].to_vec(), 179 | }) 180 | } 181 | } 182 | 183 | #[cfg(test)] 184 | mod tests { 185 | use super::{ 186 | unpack_header, 187 | pack_header, 188 | RawFrame, 189 | }; 190 | 191 | 192 | /// Tests that the `unpack_header` function correctly returns the 193 | /// components of HTTP/2 frame headers. 194 | #[test] 195 | fn test_unpack_header() { 196 | { 197 | let header = [0; 9]; 198 | assert_eq!((0, 0, 0, 0), unpack_header(&header)); 199 | } 200 | { 201 | let header = [0, 0, 1, 2, 3, 0, 0, 0, 4]; 202 | assert_eq!((1, 2, 3, 4), unpack_header(&header)); 203 | } 204 | { 205 | let header = [0, 0, 1, 200, 100, 0, 0, 0, 4]; 206 | assert_eq!((1, 200, 100, 4), unpack_header(&header)); 207 | } 208 | { 209 | let header = [0, 0, 1, 0, 0, 0, 0, 0, 0]; 210 | assert_eq!((1, 0, 0, 0), unpack_header(&header)); 211 | } 212 | { 213 | let header = [0, 1, 0, 0, 0, 0, 0, 0, 0]; 214 | assert_eq!((256, 0, 0, 0), unpack_header(&header)); 215 | } 216 | { 217 | let header = [1, 0, 0, 0, 0, 0, 0, 0, 0]; 218 | assert_eq!((256 * 256, 0, 0, 0), unpack_header(&header)); 219 | } 220 | { 221 | let header = [0, 0, 0, 0, 0, 0, 0, 0, 1]; 222 | assert_eq!((0, 0, 0, 1), unpack_header(&header)); 223 | } 224 | { 225 | let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1]; 226 | assert_eq!(((1 << 24) - 1, 0, 0, 1), unpack_header(&header)); 227 | } 228 | { 229 | let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1]; 230 | assert_eq!( 231 | ((1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)), 232 | unpack_header(&header)); 233 | } 234 | } 235 | 236 | /// Tests that the `pack_header` function correctly returns the buffer 237 | /// corresponding to components of HTTP/2 frame headers. 238 | #[test] 239 | fn test_pack_header() { 240 | { 241 | let header = [0; 9]; 242 | assert_eq!(pack_header(&(0, 0, 0, 0)), header); 243 | } 244 | { 245 | let header = [0, 0, 1, 2, 3, 0, 0, 0, 4]; 246 | assert_eq!(pack_header(&(1, 2, 3, 4)), header); 247 | } 248 | { 249 | let header = [0, 0, 1, 200, 100, 0, 0, 0, 4]; 250 | assert_eq!(pack_header(&(1, 200, 100, 4)), header); 251 | } 252 | { 253 | let header = [0, 0, 1, 0, 0, 0, 0, 0, 0]; 254 | assert_eq!(pack_header(&(1, 0, 0, 0)), header); 255 | } 256 | { 257 | let header = [0, 1, 0, 0, 0, 0, 0, 0, 0]; 258 | assert_eq!(pack_header(&(256, 0, 0, 0)), header); 259 | } 260 | { 261 | let header = [1, 0, 0, 0, 0, 0, 0, 0, 0]; 262 | assert_eq!(pack_header(&(256 * 256, 0, 0, 0)), header); 263 | } 264 | { 265 | let header = [0, 0, 0, 0, 0, 0, 0, 0, 1]; 266 | assert_eq!(pack_header(&(0, 0, 0, 1)), header); 267 | } 268 | { 269 | let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1]; 270 | assert_eq!(pack_header(&((1 << 24) - 1, 0, 0, 1)), header); 271 | } 272 | { 273 | let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1]; 274 | let header_components = ( 275 | (1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24) 276 | ); 277 | assert_eq!(pack_header(&header_components), header); 278 | } 279 | } 280 | 281 | /// Tests that the `RawFrame::from_buf` method correctly constructs a 282 | /// `RawFrame` from a given buffer. 283 | #[test] 284 | fn test_raw_frame_from_buffer() { 285 | // Correct frame 286 | { 287 | let data = b"123"; 288 | let header = (data.len() as u32, 0x1, 0, 1); 289 | let buf = { 290 | let mut buf = Vec::new(); 291 | buf.extend(pack_header(&header).to_vec().into_iter()); 292 | buf.extend(data.to_vec().into_iter()); 293 | buf 294 | }; 295 | 296 | let raw = RawFrame::from_buf(&buf).unwrap(); 297 | 298 | assert_eq!(raw.header, header); 299 | assert_eq!(raw.payload, data) 300 | } 301 | // Correct frame with trailing data 302 | { 303 | let data = b"123"; 304 | let header = (data.len() as u32, 0x1, 0, 1); 305 | let buf = { 306 | let mut buf = Vec::new(); 307 | buf.extend(pack_header(&header).to_vec().into_iter()); 308 | buf.extend(data.to_vec().into_iter()); 309 | buf.extend(vec![1, 2, 3, 4, 5].into_iter()); 310 | buf 311 | }; 312 | 313 | let raw = RawFrame::from_buf(&buf).unwrap(); 314 | 315 | assert_eq!(raw.header, header); 316 | assert_eq!(raw.payload, data) 317 | } 318 | // Missing payload chunk 319 | { 320 | let data = b"123"; 321 | let header = (data.len() as u32, 0x1, 0, 1); 322 | let buf = { 323 | let mut buf = Vec::new(); 324 | buf.extend(pack_header(&header).to_vec().into_iter()); 325 | buf.extend(data[..2].to_vec().into_iter()); 326 | buf 327 | }; 328 | 329 | assert!(RawFrame::from_buf(&buf).is_none()); 330 | } 331 | // Missing header chunk 332 | { 333 | let header = (0, 0x1, 0, 1); 334 | let buf = { 335 | let mut buf = Vec::new(); 336 | buf.extend(pack_header(&header)[..5].to_vec().into_iter()); 337 | buf 338 | }; 339 | 340 | assert!(RawFrame::from_buf(&buf).is_none()); 341 | } 342 | // Completely empty buffer 343 | { 344 | assert!(RawFrame::from_buf(&[]).is_none()); 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/client/async.rs: -------------------------------------------------------------------------------- 1 | //! Contains an implementation of an asynchronous client. 2 | //! 3 | //! It allows users to make requests to the same underlying connection from 4 | //! different threads concurrently, as well as to receive the response 5 | //! asynchronously. 6 | use std::net::TcpStream; 7 | use std::collections::HashMap; 8 | 9 | use std::sync::mpsc::{Sender, Receiver}; 10 | use std::sync::mpsc; 11 | use std::thread; 12 | 13 | use super::super::http::{StreamId, HttpError, HttpScheme, Response, Request, Header}; 14 | use super::super::http::connection::{HttpConnection, ClientConnection}; 15 | use super::super::http::session::{DefaultSession, DefaultStream}; 16 | 17 | /// A struct representing an asynchronously dispatched request. It is used 18 | /// internally be the `ClientService` and `Client` structs. 19 | struct AsyncRequest { 20 | /// The method of the request 21 | pub method: Vec, 22 | /// The path being requested 23 | pub path: Vec, 24 | /// Extra headers that should be included in the request. Does *not* 25 | /// include meta-headers. 26 | pub headers: Vec
, 27 | /// The sender side of a channel where the response to this request should 28 | /// be delivered. 29 | tx: Sender, 30 | } 31 | 32 | /// An enum that represents errors that can be raised by the operation of a 33 | /// `ClientService`. 34 | enum ClientServiceErr { 35 | /// Corresponds to the case where the service has finished its operation. 36 | Done, 37 | /// Corresponds to the case where the service is unable to continue due to 38 | /// an error that occurred on the underlying HTTP/2 connection. 39 | Http(HttpError), 40 | } 41 | 42 | /// An internal struct encapsulating a service that lets multiple clients 43 | /// issue concurrent requests to the same HTTP/2 connection. 44 | /// 45 | /// The service handles issuing requests that it receives on a channel (the 46 | /// receiving end of it is `rx`) to the server and relaying the requests to 47 | /// the corresponding channel (the one given in the `AsyncRequest` instance 48 | /// that was read off the requests channel). 49 | /// 50 | /// The service does not automatically start running in a background thread. 51 | /// The user needs to start that explicitly and decide how they want to handle 52 | /// running the `run_once` method. 53 | struct ClientService { 54 | /// The ID that will be assigned to the next client-initiated stream. 55 | next_stream_id: StreamId, 56 | /// The number of requests that have been sent, but are yet unanswered. 57 | outstanding_reqs: u32, 58 | /// The limit to the number of requests that can be pending (unanswered, 59 | /// but sent). 60 | limit: u32, 61 | /// The connection that is used for underlying HTTP/2 communication. 62 | conn: ClientConnection, 63 | /// A mapping of stream IDs to the sender side of a channel that is 64 | /// expecting a response to the request that is to arrive on that stream. 65 | chans: HashMap>, 66 | /// The receiver end of a channel to which requests that clients wish to 67 | /// issue are queued. 68 | rx: Receiver, 69 | } 70 | 71 | impl ClientService { 72 | /// Creates a new `ClientService` that will communicate over a new HTTP/2 73 | /// connection to the server found at the given host and port combination. 74 | /// 75 | /// # Returns 76 | /// 77 | /// Returns the newly created `ClientService` and the sender side of the 78 | /// channel on which the new service instance expects requests to arrive. 79 | /// 80 | /// If no HTTP/2 connection can be established to the given host on the 81 | /// given port, returns `None`. 82 | pub fn new(host: &str, port: u16) -> Option<(ClientService, Sender)> { 83 | let (tx, rx): (Sender, Receiver) = 84 | mpsc::channel(); 85 | let mut conn = ClientConnection::with_connection( 86 | HttpConnection::new( 87 | TcpStream::connect(&(host, port)).unwrap(), 88 | HttpScheme::Http, 89 | host.into()), 90 | DefaultSession::::new()); 91 | match conn.init() { 92 | Ok(_) => {}, 93 | Err(_) => return None, 94 | }; 95 | 96 | let service = ClientService { 97 | next_stream_id: 1, 98 | outstanding_reqs: 0, 99 | limit: 3, 100 | conn: conn, 101 | chans: HashMap::new(), 102 | rx: rx, 103 | }; 104 | 105 | Some((service, tx)) 106 | } 107 | 108 | /// Performs one iteration of the service. 109 | /// 110 | /// If there are no currently oustanding requests (sent, but yet no full 111 | /// response received), the function blocks until a new request is received. 112 | /// Once there is a new request, it will be sent to the server in its 113 | /// entirety. 114 | /// 115 | /// If there is an outstanding request, the function tries to handle the 116 | /// response-related payload incoming on the HTTP/2 connection. This could 117 | /// be more than one HTTP/2 frame since `SETTINGS` or other frames might be 118 | /// interleaved with `DATA` and `HEADERS` frames representing the responses. 119 | /// 120 | /// Any received responses after handling the first payload are sent to the 121 | /// corresponding channel that is expecting the particular response. 122 | /// 123 | /// Finally, an additional request is sent (in its entirety) if the limit 124 | /// to the number of concurrent requests was not reached and there are 125 | /// queued requests from clients. 126 | /// 127 | /// # Returns 128 | /// 129 | /// On a successful pass, the function returns an `Ok(())`. 130 | /// 131 | /// The `Err` response is returned when there are no more responses to be 132 | /// received and there are no more clients connected to the service (and 133 | /// thus no more requests could ever be issued by the instance). This 134 | /// corresponds to the `ClientServiceErr::Done` variant. 135 | /// 136 | /// Any HTTP/2 error is propagated (wrapped into a ClientServiceErr::Http 137 | /// variant). 138 | pub fn run_once(&mut self) -> Result<(), ClientServiceErr> { 139 | // If there are no responses that we should read, block until there's 140 | // a new request. This precludes handling server pushes, pings, or 141 | // settings changes that may happen in the mean time until there's a 142 | // new request, since nothing is reading from the connection until then. 143 | if self.outstanding_reqs == 0 { 144 | debug!("Service thread blocking until there's a new request..."); 145 | let async_req = match self.rx.recv() { 146 | Ok(req) => req, 147 | // The receive operation can only fail if the sender has 148 | // disconnected implying no further receives are possible. 149 | // At that point, we make sure to gracefully stop the service. 150 | Err(_) => return Err(ClientServiceErr::Done), 151 | }; 152 | self.send_request(async_req); 153 | } 154 | 155 | // Handles the next frame... 156 | debug!("Handling next frame"); 157 | match self.conn.handle_next_frame() { 158 | Ok(_) => {}, 159 | Err(e) => return Err(ClientServiceErr::Http(e)), 160 | }; 161 | // ...and then any connections that may have been closed in the meantime 162 | // are converted to responses and notifications sent to appropriate 163 | // channels. 164 | self.handle_closed(); 165 | // At this point we try to queue another outstanding request (if the 166 | // limit has not been reached). 167 | self.queue_next_request(); 168 | 169 | Ok(()) 170 | } 171 | 172 | /// Internal helper method. Sends a request to the server based on the 173 | /// parameters given in the `AsyncRequest`. It blocks until the request is 174 | /// fully transmitted to the server. 175 | fn send_request(&mut self, async_req: AsyncRequest) { 176 | let req = self.create_request( 177 | async_req.method, 178 | async_req.path, 179 | async_req.headers); 180 | 181 | debug!("Sending new request... id = {}", req.stream_id); 182 | 183 | self.conn.session.new_stream(req.stream_id); 184 | self.chans.insert(req.stream_id, async_req.tx); 185 | self.conn.send_request(req).ok().unwrap(); 186 | self.outstanding_reqs += 1; 187 | } 188 | 189 | /// Internal helper method. Creates a new `Request` instance based on the 190 | /// given parameters. Such a `Request` instance is ready to be passed to 191 | /// the connection for transmission to the server. 192 | fn create_request(&mut self, 193 | method: Vec, 194 | path: Vec, 195 | extra_headers: Vec
) -> Request { 196 | let mut headers: Vec
= Vec::new(); 197 | headers.extend(vec![ 198 | (b":method".to_vec(), method), 199 | (b":path".to_vec(), path), 200 | (b":authority".to_vec(), self.conn.host().as_bytes().to_vec()), 201 | (b":scheme".to_vec(), self.conn.scheme().as_bytes().to_vec()), 202 | ].into_iter()); 203 | headers.extend(extra_headers.into_iter()); 204 | 205 | let req = Request { 206 | stream_id: self.next_stream_id, 207 | headers: headers, 208 | body: Vec::new(), 209 | }; 210 | self.next_stream_id += 2; 211 | 212 | req 213 | } 214 | 215 | /// Internal helper method. Sends a response assembled from the given 216 | /// stream to the corresponding channel that is waiting for the response. 217 | /// 218 | /// The given `stream` instance is consumed by this method. 219 | fn send_response(&mut self, stream: DefaultStream) { 220 | match self.chans.remove(&stream.stream_id) { 221 | None => { 222 | // This should never happen, it means the session gave us 223 | // a response that we didn't request. 224 | panic!("Received a response for an unknown request!"); 225 | }, 226 | Some(tx) => { 227 | let _ = tx.send(Response { 228 | stream_id: stream.stream_id, 229 | headers: stream.headers.unwrap(), 230 | body: stream.body, 231 | }); 232 | } 233 | }; 234 | } 235 | 236 | /// Internal helper method. Handles all closed streams by sending appropriate 237 | /// notifications to waiting channels. 238 | /// 239 | /// For now, the channels are all given a `Response`, even though the 240 | /// stream might end up being closed by the server with an error. 241 | fn handle_closed(&mut self) { 242 | let done = self.conn.session.get_closed(); 243 | for stream in done { 244 | self.send_response(stream); 245 | self.outstanding_reqs -= 1; 246 | } 247 | } 248 | 249 | /// Internal helper method. If there are yet unsent requests queued by a 250 | /// client to the service and the service has not exceeded the limit of 251 | /// concurrent requests that it is allowed to issue, it sends a single 252 | /// new request to the server. Blocks until this request is sent. 253 | fn queue_next_request(&mut self) { 254 | if self.outstanding_reqs < self.limit { 255 | // Try to queue another request since we haven't gone over 256 | // the (arbitrary) limit. 257 | debug!("Not over the limit yet. Checking for more requests..."); 258 | if let Ok(async_req) = self.rx.try_recv() { 259 | self.send_request(async_req); 260 | } 261 | } 262 | } 263 | } 264 | 265 | /// A struct representing an HTTP/2 client that receives responses to its 266 | /// requests asynchronously. Additionally, this client can be cloned and all 267 | /// clones can issue (concurrently) requests to the server, using the same 268 | /// underlying HTTP/2 connection. 269 | /// 270 | /// # Example 271 | /// 272 | /// ```no_run 273 | /// use solicit::client::Client; 274 | /// use std::thread; 275 | /// use std::str; 276 | /// 277 | /// // Connect to a server that supports HTTP/2 278 | /// let client = Client::new("nghttp2.org", 80).unwrap(); 279 | /// 280 | /// // Issue 5 requests from 5 different threads concurrently and wait for all 281 | /// // threads to receive their response. 282 | /// let _: Vec<_> = (0..5).map(|i| { 283 | /// let this = client.clone(); 284 | /// thread::scoped(move || { 285 | /// let resp = this.get(b"/", &[]).unwrap(); 286 | /// let response = resp.recv().unwrap(); 287 | /// println!("Thread {} got response ... {}", i, response.status_code().ok().unwrap()); 288 | /// println!("The response contains the following headers:"); 289 | /// for header in response.headers.iter() { 290 | /// println!(" {}: {}", 291 | /// str::from_utf8(&header.0).unwrap(), 292 | /// str::from_utf8(&header.1).unwrap()); 293 | /// } 294 | /// }) 295 | /// }).collect(); 296 | /// ``` 297 | #[derive(Clone)] 298 | pub struct Client { 299 | /// The sender side of a channel on which a running `ClientService` expects 300 | /// to receive new requests, which are to be sent to the server. 301 | sender: Sender, 302 | } 303 | 304 | impl Client { 305 | /// Creates a brand new HTTP/2 client. This means that a new HTTP/2 306 | /// connection will be established behind the scenes. A thread is spawned 307 | /// to handle the connection in the background, so that the thread that 308 | /// creates the client can use it asynchronously. 309 | /// 310 | /// # Returns 311 | /// 312 | /// A `Client` instance that allows access to the underlying HTTP/2 313 | /// connection on the application level. Only full requests and responses 314 | /// are exposed to users. 315 | /// 316 | /// The returned `Client` can be cloned and all clones will use the same 317 | /// underlying HTTP/2 connection. Once all cloned instances (as well as the 318 | /// original one) are dropped, the thread that was spawned will also exit 319 | /// gracefully. Any error on the underlying HTTP/2 connection also causes 320 | /// the thread to exit. 321 | /// 322 | /// If the HTTP/2 connection cannot be initialized returns `None`. 323 | pub fn new(host: &str, port: u16) -> Option { 324 | let (mut service, rx) = match ClientService::new(host, port) { 325 | Some((service, rx)) => (service, rx), 326 | None => return None, 327 | }; 328 | 329 | thread::spawn(move || { 330 | while let Ok(_) = service.run_once() {} 331 | debug!("Service thread halting"); 332 | }); 333 | 334 | Some(Client { 335 | sender: rx, 336 | }) 337 | } 338 | 339 | /// Issues a new request to the server. 340 | /// 341 | /// The request's method, path, and extra headers are provided as parameters. 342 | /// The headers should *never* include any meta-headers (such as `:method`). 343 | /// 344 | /// # Returns 345 | /// 346 | /// The method itself returns immediately upon queuing the request. It does 347 | /// not wait for the request to be transmitted nor for the response to 348 | /// arrive. Once the caller is interested in the final response, they can 349 | /// block on the returned `Receiver` end of a channel which will receive 350 | /// the response once generated. 351 | /// 352 | /// The `Response` instance that the channel receives will contain the full 353 | /// response body and is available only once the full response body has 354 | /// been received. 355 | /// 356 | /// If the method is unable to queue the request, it must mean that the 357 | /// underlying HTTP/2 connection to which this client is associated has 358 | /// failed and it returns `None`. 359 | pub fn request(&self, method: &[u8], path: &[u8], headers: &[Header]) 360 | -> Option> { 361 | let (resp_tx, resp_rx): (Sender, Receiver) = 362 | mpsc::channel(); 363 | // A send can only fail if the receiver is disconnected. If the send 364 | // fails here, it means that the service hit an error on the underlying 365 | // HTTP/2 connection and will never come alive again. 366 | let res = self.sender.send(AsyncRequest { 367 | method: method.to_vec(), 368 | path: path.to_vec(), 369 | headers: headers.to_vec(), 370 | tx: resp_tx, 371 | }); 372 | 373 | match res { 374 | Ok(_) => Some(resp_rx), 375 | Err(_) => None, 376 | } 377 | } 378 | 379 | /// Issues a GET request to the server. 380 | /// 381 | /// A convenience wrapper around the `request` method that sets the correct 382 | /// method. 383 | pub fn get(&self, path: &[u8], headers: &[Header]) -> Option> { 384 | self.request(b"GET", path, headers) 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /src/http/frame/dataframe.rs: -------------------------------------------------------------------------------- 1 | use super::super::StreamId; 2 | use super::frames::{ 3 | Frame, 4 | Flag, 5 | parse_padded_payload, 6 | pack_header, 7 | RawFrame, 8 | FrameHeader 9 | }; 10 | 11 | /// An enum representing the flags that a `DataFrame` can have. 12 | /// The integer representation associated to each variant is that flag's 13 | /// bitmask. 14 | /// 15 | /// HTTP/2 spec, section 6.1. 16 | #[derive(Clone)] 17 | #[derive(PartialEq)] 18 | #[derive(Debug)] 19 | #[derive(Copy)] 20 | pub enum DataFlag { 21 | EndStream = 0x1, 22 | Padded = 0x8, 23 | } 24 | 25 | impl Flag for DataFlag { 26 | #[inline] 27 | fn bitmask(&self) -> u8 { 28 | *self as u8 29 | } 30 | } 31 | 32 | /// A struct representing the DATA frames of HTTP/2, as defined in the HTTP/2 33 | /// spec, section 6.1. 34 | #[derive(PartialEq)] 35 | #[derive(Debug)] 36 | pub struct DataFrame { 37 | /// The data found in the frame as an opaque byte sequence. It never 38 | /// includes padding bytes. 39 | pub data: Vec, 40 | /// Represents the flags currently set on the `DataFrame`, packed into a 41 | /// single byte. 42 | flags: u8, 43 | /// The ID of the stream with which the frame is associated. 44 | stream_id: StreamId, 45 | /// The length of the padding applied to the data. Since the spec defines 46 | /// that the padding length is at most an unsigned integer value, we also 47 | /// keep a `u8`, instead of a `usize`. 48 | padding_len: Option, 49 | } 50 | 51 | impl DataFrame { 52 | /// Creates a new empty `DataFrame`, associated to the stream with the 53 | /// given ID. 54 | pub fn new(stream_id: StreamId) -> DataFrame { 55 | DataFrame { 56 | stream_id: stream_id, 57 | // All flags unset by default 58 | flags: 0, 59 | // No data stored in the frame yet 60 | data: Vec::new(), 61 | // No padding 62 | padding_len: None, 63 | } 64 | } 65 | 66 | /// Returns `true` if the DATA frame is padded, otherwise false. 67 | pub fn is_padded(&self) -> bool { 68 | self.is_set(DataFlag::Padded) 69 | } 70 | 71 | /// Sets the number of bytes that should be used as padding for this 72 | /// frame. 73 | pub fn set_padding(&mut self, pad_len: u8) { 74 | self.set_flag(DataFlag::Padded); 75 | self.padding_len = Some(pad_len); 76 | } 77 | 78 | /// Returns the total length of the payload, taking into account possible 79 | /// padding. 80 | fn payload_len(&self) -> u32 { 81 | if self.is_padded() { 82 | 1 + (self.data.len() as u32) + (self.padding_len.unwrap_or(0) as u32) 83 | } else { 84 | // Downcasting here is all right, because the HTTP/2 frames cannot 85 | // have a length larger than a 32 bit unsigned integer. 86 | self.data.len() as u32 87 | } 88 | } 89 | 90 | /// Parses the given slice as a DATA frame's payload. Depending on the 91 | /// `padded` flag, it will treat the given bytes as a data frame with 92 | /// padding or without. 93 | /// 94 | /// # Returns 95 | /// 96 | /// A tuple wrapped in the `Some` variant, representing the true data and 97 | /// the original padding length. 98 | /// If there was no padding, returns `None` for the second tuple member. 99 | /// 100 | /// If the payload was invalid for a DATA frame, returns `None` 101 | fn parse_payload(payload: &[u8], padded: bool) 102 | -> Option<(Vec, Option)> { 103 | let (data, pad_len) = if padded { 104 | match parse_padded_payload(payload) { 105 | Some((data, pad_len)) => (data, Some(pad_len)), 106 | None => return None, 107 | } 108 | } else { 109 | (payload, None) 110 | }; 111 | 112 | Some((data.to_vec(), pad_len)) 113 | } 114 | } 115 | 116 | impl Frame for DataFrame { 117 | type FlagType = DataFlag; 118 | 119 | /// Creates a new `DataFrame` from the given `RawFrame` (i.e. header and 120 | /// payload), if possible. Returns `None` if a valid `DataFrame` cannot be 121 | /// constructed from the given `RawFrame`. 122 | fn from_raw(raw_frame: RawFrame) -> Option { 123 | // Unpack the header 124 | let (len, frame_type, flags, stream_id) = raw_frame.header; 125 | // Check that the frame type is correct for this frame implementation 126 | if frame_type != 0x0 { 127 | return None; 128 | } 129 | // Check that the length given in the header matches the payload 130 | // length; if not, something went wrong and we do not consider this a 131 | // valid frame. 132 | if (len as usize) != raw_frame.payload.len() { 133 | return None; 134 | } 135 | // A DATA frame cannot be associated to the connection itself. 136 | if stream_id == 0x0 { 137 | return None; 138 | } 139 | // No validation is required for the flags, since according to the spec, 140 | // unknown flags MUST be ignored. 141 | // Everything has been validated so far: try to extract the data from 142 | // the payload. 143 | let padded = (flags & DataFlag::Padded.bitmask()) != 0; 144 | match DataFrame::parse_payload(&raw_frame.payload, padded) { 145 | Some((data, Some(padding_len))) => { 146 | // The data got extracted (from a padded frame) 147 | Some(DataFrame { 148 | stream_id: stream_id, 149 | flags: flags, 150 | data: data, 151 | padding_len: Some(padding_len), 152 | }) 153 | }, 154 | Some((data, None)) => { 155 | // The data got extracted (from a no-padding frame) 156 | Some(DataFrame { 157 | stream_id: stream_id, 158 | flags: flags, 159 | data: data, 160 | padding_len: None, 161 | }) 162 | }, 163 | None => None, 164 | } 165 | } 166 | 167 | /// Tests if the given flag is set for the frame. 168 | fn is_set(&self, flag: DataFlag) -> bool { 169 | (self.flags & flag.bitmask()) != 0 170 | } 171 | 172 | /// Sets the given flag for the frame. 173 | fn set_flag(&mut self, flag: DataFlag) { 174 | self.flags |= flag.bitmask(); 175 | } 176 | 177 | /// Returns the `StreamId` of the stream to which the frame is associated. 178 | fn get_stream_id(&self) -> StreamId { 179 | self.stream_id 180 | } 181 | 182 | /// Returns a `FrameHeader` based on the current state of the frame. 183 | fn get_header(&self) -> FrameHeader { 184 | (self.payload_len(), 0x0, self.flags, self.stream_id) 185 | } 186 | 187 | /// Returns a `Vec` with the serialized representation of the frame. 188 | fn serialize(&self) -> Vec { 189 | let mut buf = Vec::with_capacity(9 + self.payload_len() as usize); 190 | // First the header... 191 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter()); 192 | // ...now the data, depending on whether it's wrapped or not 193 | if self.is_padded() { 194 | let pad_len = self.padding_len.unwrap_or(0); 195 | buf.push(pad_len); 196 | buf.extend(self.data.clone().into_iter()); 197 | // The padding bytes MUST be 0 198 | for _ in 0..pad_len { buf.push(0); } 199 | } else { 200 | buf.extend(self.data.clone().into_iter()); 201 | } 202 | 203 | buf 204 | } 205 | } 206 | 207 | #[cfg(test)] 208 | mod tests { 209 | use super::super::frames::{Frame, RawFrame, pack_header}; 210 | use super::super::test::{build_test_frame, build_padded_frame_payload}; 211 | use super::{DataFrame, DataFlag}; 212 | /// Tests that the `DataFrame` struct correctly interprets a DATA frame 213 | /// with no padding set. 214 | #[test] 215 | fn test_data_frame_parse_no_padding() { 216 | let data = b"asdf"; 217 | let payload = data.to_vec(); 218 | // A header with the flag indicating no padding 219 | let header = (payload.len() as u32, 0u8, 0u8, 1u32); 220 | 221 | let frame = build_test_frame::(&header, &payload); 222 | 223 | // The frame correctly returns the data? 224 | assert_eq!(&frame.data, &data); 225 | // ...and the headers? 226 | assert_eq!(frame.get_header(), header); 227 | } 228 | 229 | /// Tests that the `DataFrame` struct correctly interprets a DATA frame 230 | /// with a number of padding bytes set. 231 | #[test] 232 | fn test_data_frame_padded() { 233 | let data = b"asdf"; 234 | let payload = build_padded_frame_payload(data, 5); 235 | // A header with the flag indicating padding 236 | let header = (payload.len() as u32, 0u8, 8u8, 1u32); 237 | 238 | let frame = build_test_frame::(&header, &payload); 239 | 240 | // The frame correctly returns the data? 241 | assert_eq!(&frame.data, &data); 242 | // ...and the headers? 243 | assert_eq!(frame.get_header(), header); 244 | } 245 | 246 | /// Tests that a DATA frame with a zero-length payload is still considered 247 | /// valid. 248 | /// 249 | /// There doesn't seem to be anything in the spec that would make it invalid. 250 | /// The spec says that frames are considered invalid if their size is too 251 | /// small to contain all the mandatory parts of the frame of a particular 252 | /// type. Since the DATA frame does not have any mandatory fields (of size 253 | /// greater than 1), a zero-len payload should be all right. 254 | #[test] 255 | fn test_data_frame_zero_len_payload() { 256 | let data = b""; 257 | let payload = data.to_vec(); 258 | // A header with the flag indicating no padding 259 | let header = (payload.len() as u32, 0u8, 0u8, 1u32); 260 | 261 | let frame = build_test_frame::(&header, &payload); 262 | 263 | // The frame correctly returns the data? 264 | assert_eq!(&frame.data, &data); 265 | // ...and the headers? 266 | assert_eq!(frame.get_header(), header); 267 | } 268 | 269 | /// Tests that the `DataFrame` struct correctly handles the case where the 270 | /// padding is invalid: the size of the padding given is greater than or 271 | /// equal to the total size of the frame. 272 | #[test] 273 | fn test_data_frame_padding_invalid() { 274 | let payload = [5, b'a', b's', b'd', b'f']; 275 | // A header with the flag indicating padding 276 | let header = (payload.len() as u32, 0u8, 8u8, 1u32); 277 | 278 | let frame: Option = Frame::from_raw( 279 | RawFrame::with_payload(header.clone(), payload.to_vec())); 280 | 281 | // The frame was not even created since the raw bytes are invalid 282 | assert!(frame.is_none()) 283 | } 284 | 285 | /// Tests that if a frame that should be parsed has a stream ID of 0, it is 286 | /// not considered a valid DATA frame. 287 | #[test] 288 | fn test_data_frame_stream_zero() { 289 | let data = b"asdf"; 290 | let payload = data.to_vec(); 291 | // Stream 0 292 | let header = (payload.len() as u32, 0u8, 0u8, 0u32); 293 | 294 | let frame: Option = Frame::from_raw( 295 | RawFrame::with_payload(header.clone(), payload.to_vec())); 296 | 297 | // The frame is not valid. 298 | assert!(frame.is_none()); 299 | } 300 | 301 | /// Tests that the `DataFrame` struct correctly interprets a DATA frame 302 | /// with no padding and no data. 303 | #[test] 304 | fn test_data_frame_no_padding_empty() { 305 | let payload = []; 306 | let header = (payload.len() as u32, 0u8, 0u8, 1u32); 307 | 308 | let frame = build_test_frame::(&header, &payload); 309 | 310 | // The frame correctly returns the data -- i.e. an empty array? 311 | assert_eq!(&frame.data, &[]); 312 | // ...and the headers? 313 | assert_eq!(frame.get_header(), header); 314 | } 315 | 316 | /// Tests that the `DataFrame` struct correctly interprets a DATA frame 317 | /// with padding, but an empty payload. 318 | #[test] 319 | fn test_data_frame_padding_empty_payload() { 320 | let payload = []; 321 | let header = (payload.len() as u32, 0u8, 8u8, 1u32); 322 | 323 | let frame: Option = Frame::from_raw( 324 | RawFrame::with_payload(header.clone(), payload.to_vec())); 325 | 326 | // In this case, we cannot receive a frame, since the payload did not 327 | // contain even the first byte, necessary to find the padding length. 328 | assert!(frame.is_none()); 329 | } 330 | 331 | /// Tests that the `DataFrame` struct correctly interprets a DATA frame 332 | /// with padding of size 0. 333 | #[test] 334 | fn test_data_frame_null_padding() { 335 | let data = b"test string"; 336 | let payload = build_padded_frame_payload(data, 0); 337 | // A header with the flag indicating padding 338 | let header = (payload.len() as u32, 0u8, 8u8, 1u32); 339 | 340 | let frame = build_test_frame::(&header, &payload); 341 | 342 | // The frame correctly returns the data? 343 | assert_eq!(&frame.data, &data); 344 | // ...and the headers? 345 | assert_eq!(frame.get_header(), header); 346 | } 347 | 348 | /// Tests that the `DataFrame` struct correctly handles the situation 349 | /// where the header does not contain a frame type corresponding to the 350 | /// DATA frame type. 351 | #[test] 352 | fn test_data_frame_invalid_type() { 353 | let data = b"dummy"; 354 | let payload = build_padded_frame_payload(data, 0); 355 | // The header has an invalid type (0x1 instead of 0x0). 356 | let header = (payload.len() as u32, 1u8, 8u8, 1u32); 357 | 358 | let frame: Option = Frame::from_raw( 359 | RawFrame::with_payload(header.clone(), payload.to_vec())); 360 | 361 | assert!(frame.is_none()); 362 | } 363 | 364 | /// Tests that `DataFrame`s get correctly serialized when created with no 365 | /// padding and with no data. 366 | #[test] 367 | fn test_data_frame_serialize_no_padding_empty() { 368 | let frame = DataFrame::new(1); 369 | let expected = { 370 | let headers = pack_header(&(0, 0, 0, 1)); 371 | let mut res: Vec = Vec::new(); 372 | res.extend(headers.to_vec().into_iter()); 373 | 374 | res 375 | }; 376 | 377 | let serialized = frame.serialize(); 378 | 379 | assert_eq!(serialized, expected); 380 | } 381 | 382 | /// Tests that `DataFrame`s get correctly serialized when created with no 383 | /// padding and with some amount of data. 384 | #[test] 385 | fn test_data_frame_serialize_no_padding() { 386 | let mut frame = DataFrame::new(1); 387 | let data = vec![1, 2, 3, 4, 5, 100]; 388 | frame.data = data.clone(); 389 | let expected = { 390 | let headers = pack_header(&(6, 0, 0, 1)); 391 | let mut res: Vec = Vec::new(); 392 | res.extend(headers.to_vec().into_iter()); 393 | res.extend(data.into_iter()); 394 | 395 | res 396 | }; 397 | 398 | let serialized = frame.serialize(); 399 | 400 | assert_eq!(serialized, expected); 401 | } 402 | 403 | /// Tests that `DataFrame`s get correctly serialized when created with 404 | /// some amount of padding and some data. 405 | #[test] 406 | fn test_data_frame_serialize_padding() { 407 | let mut frame = DataFrame::new(1); 408 | let data = vec![1, 2, 3, 4, 5, 100]; 409 | frame.data = data.clone(); 410 | frame.set_padding(5); 411 | let expected = { 412 | let headers = pack_header(&(6 + 1 + 5, 0, 8, 1)); 413 | let mut res: Vec = Vec::new(); 414 | // Headers 415 | res.extend(headers.to_vec().into_iter()); 416 | // Padding len 417 | res.push(5); 418 | // Data 419 | res.extend(data.into_iter()); 420 | // Actual padding 421 | for _ in 0..5 { res.push(0); } 422 | 423 | res 424 | }; 425 | 426 | let serialized = frame.serialize(); 427 | 428 | assert_eq!(serialized, expected); 429 | } 430 | 431 | /// Tests that `DataFrame`s get correctly serialized when created with 432 | /// 0 padding. This is a distinct case from having *no padding*. 433 | #[test] 434 | fn test_data_frame_serialize_null_padding() { 435 | let mut frame = DataFrame::new(1); 436 | let data = vec![1, 2, 3, 4, 5, 100]; 437 | frame.data = data.clone(); 438 | frame.set_flag(DataFlag::Padded); 439 | let expected = { 440 | let headers = pack_header(&(6 + 1, 0, 8, 1)); 441 | let mut res: Vec = Vec::new(); 442 | // Headers 443 | res.extend(headers.to_vec().into_iter()); 444 | // Padding len 445 | res.push(0); 446 | // Data 447 | res.extend(data.into_iter()); 448 | 449 | res 450 | }; 451 | 452 | let serialized = frame.serialize(); 453 | 454 | assert_eq!(serialized, expected); 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /src/http/frame/settingsframe.rs: -------------------------------------------------------------------------------- 1 | use super::super::StreamId; 2 | use super::frames::{ 3 | Frame, 4 | Flag, 5 | RawFrame, 6 | FrameHeader, 7 | pack_header 8 | }; 9 | 10 | /// An enum that lists all valid settings that can be sent in a SETTINGS 11 | /// frame. 12 | /// 13 | /// Each setting has a value that is a 32 bit unsigned integer (6.5.1.). 14 | #[derive(Clone)] 15 | #[derive(PartialEq)] 16 | #[derive(Debug)] 17 | #[derive(Copy)] 18 | pub enum HttpSetting { 19 | HeaderTableSize(u32), 20 | EnablePush(u32), 21 | MaxConcurrentStreams(u32), 22 | InitialWindowSize(u32), 23 | MaxFrameSize(u32), 24 | MaxHeaderListSize(u32), 25 | } 26 | 27 | impl HttpSetting { 28 | /// Creates a new `HttpSetting` with the correct variant corresponding to 29 | /// the given setting id, based on the settings IDs defined in section 30 | /// 6.5.2. 31 | pub fn from_id(id: u16, val: u32) -> Option { 32 | match id { 33 | 1 => Some(HttpSetting::HeaderTableSize(val)), 34 | 2 => Some(HttpSetting::EnablePush(val)), 35 | 3 => Some(HttpSetting::MaxConcurrentStreams(val)), 36 | 4 => Some(HttpSetting::InitialWindowSize(val)), 37 | 5 => Some(HttpSetting::MaxFrameSize(val)), 38 | 6 => Some(HttpSetting::MaxHeaderListSize(val)), 39 | _ => None, 40 | } 41 | } 42 | 43 | /// Creates a new `HttpSetting` by parsing the given buffer of 6 bytes, 44 | /// which contains the raw byte representation of the setting, according 45 | /// to the "SETTINGS format" defined in section 6.5.1. 46 | /// 47 | /// The `raw_setting` parameter should have length at least 6 bytes, since 48 | /// the length of the raw setting is exactly 6 bytes. 49 | /// 50 | /// # Panics 51 | /// 52 | /// If given a buffer shorter than 6 bytes, the function will panic. 53 | fn parse_setting(raw_setting: &[u8]) -> Option { 54 | let id: u16 = ((raw_setting[0] as u16) << 8) | (raw_setting[1] as u16); 55 | let val: u32 = unpack_octets_4!(raw_setting, 2, u32); 56 | 57 | HttpSetting::from_id(id, val) 58 | } 59 | 60 | /// Returns the setting ID as an unsigned 16 bit integer, as defined in 61 | /// section 6.5.2. 62 | pub fn get_id(&self) -> u16 { 63 | match self { 64 | &HttpSetting::HeaderTableSize(_) => 1, 65 | &HttpSetting::EnablePush(_) => 2, 66 | &HttpSetting::MaxConcurrentStreams(_) => 3, 67 | &HttpSetting::InitialWindowSize(_) => 4, 68 | &HttpSetting::MaxFrameSize(_) => 5, 69 | &HttpSetting::MaxHeaderListSize(_) => 6, 70 | } 71 | } 72 | 73 | /// Gets the setting value by unpacking it from the wrapped `u32`. 74 | pub fn get_val(&self) -> u32 { 75 | match self { 76 | &HttpSetting::HeaderTableSize(ref val) => val.clone(), 77 | &HttpSetting::EnablePush(ref val) => val.clone(), 78 | &HttpSetting::MaxConcurrentStreams(ref val) => val.clone(), 79 | &HttpSetting::InitialWindowSize(ref val) => val.clone(), 80 | &HttpSetting::MaxFrameSize(ref val) => val.clone(), 81 | &HttpSetting::MaxHeaderListSize(ref val) => val.clone(), 82 | } 83 | } 84 | 85 | /// Serializes a setting into its "on-the-wire" representation of 6 octets, 86 | /// according to section 6.5.1. 87 | fn serialize(&self) -> [u8; 6] { 88 | let (id, val) = (self.get_id(), self.get_val()); 89 | [ 90 | ((id >> 8) & 0x00FF) as u8, 91 | ((id >> 0) & 0x00FF) as u8, 92 | (((val >> 24) & 0x000000FF) as u8), 93 | (((val >> 16) & 0x000000FF) as u8), 94 | (((val >> 8) & 0x000000FF) as u8), 95 | (((val >> 0) & 0x000000FF) as u8), 96 | ] 97 | } 98 | } 99 | 100 | /// An enum representing the flags that a `SettingsFrame` can have. 101 | /// The integer representation associated to each variant is that flag's 102 | /// bitmask. 103 | /// 104 | /// HTTP/2 spec, section 6.5. 105 | #[derive(Clone)] 106 | #[derive(PartialEq)] 107 | #[derive(Debug)] 108 | #[derive(Copy)] 109 | pub enum SettingsFlag { 110 | Ack = 0x1, 111 | } 112 | 113 | impl Flag for SettingsFlag { 114 | #[inline] 115 | fn bitmask(&self) -> u8 { 116 | *self as u8 117 | } 118 | } 119 | 120 | /// A struct representing the SETTINGS frames of HTTP/2, as defined in the 121 | /// HTTP/2 spec, section 6.5. 122 | /// 123 | /// The struct does not try to prevent the client from creating malformed 124 | /// SETTINGS frames, such as ones that have the ACK flag set along with some 125 | /// settings values. The users are responsible to follow the prescribed rules 126 | /// before sending the frame to the peer. 127 | /// 128 | /// On parsing received frames, it treats the following as errors: 129 | /// 130 | /// - ACK flag and a number of settings both set 131 | /// - Payload length not a multiple of 6 132 | /// - Stream ID not zero (SETTINGS frames MUST be associated to stream 0) 133 | /// 134 | /// What is *not* treated as an error (for now) are settings values out of 135 | /// allowed bounds such as a EnablePush being set to something other than 0 or 136 | /// 1. 137 | #[derive(PartialEq)] 138 | #[derive(Debug)] 139 | pub struct SettingsFrame { 140 | /// Contains all the settings that are currently set in the frame. It is 141 | /// safe to access this field (to read, add, or remove settings), even 142 | /// though a helper method `add_setting` exists. 143 | pub settings: Vec, 144 | /// Represents the flags currently set on the `SettingsFrame`, packed into 145 | /// a single byte. 146 | flags: u8, 147 | } 148 | 149 | impl SettingsFrame { 150 | /// Creates a new `SettingsFrame` 151 | pub fn new() -> SettingsFrame { 152 | SettingsFrame { 153 | settings: Vec::new(), 154 | // By default, no flags are set 155 | flags: 0, 156 | } 157 | } 158 | 159 | /// A convenience constructor that returns a `SettingsFrame` with the ACK 160 | /// flag already set and no settings. 161 | pub fn new_ack() -> SettingsFrame { 162 | SettingsFrame { 163 | settings: Vec::new(), 164 | flags: SettingsFlag::Ack.bitmask(), 165 | } 166 | } 167 | 168 | /// Adds the given setting to the frame. 169 | pub fn add_setting(&mut self, setting: HttpSetting) { 170 | self.settings.push(setting); 171 | } 172 | 173 | /// Sets the ACK flag for the frame. This method is just a convenience 174 | /// method for calling `frame.set_flag(SettingsFlag::Ack)`. 175 | pub fn set_ack(&mut self) { 176 | self.set_flag(SettingsFlag::Ack) 177 | } 178 | 179 | /// Checks whether the `SettingsFrame` has an ACK attached to it. 180 | pub fn is_ack(&self) -> bool { 181 | self.is_set(SettingsFlag::Ack) 182 | } 183 | 184 | /// Returns the total length of the payload in bytes. 185 | fn payload_len(&self) -> u32 { 186 | // Each setting is represented with 6 bytes => 187 | 6 * self.settings.len() as u32 188 | } 189 | 190 | /// Parses the given buffer, considering it a representation of a settings 191 | /// frame payload. 192 | /// 193 | /// # Returns 194 | /// 195 | /// A `Vec` of settings that are set by the given payload. 196 | /// 197 | /// Any unknown setting is ignored, as per the HTTP/2 spec requirement. 198 | /// 199 | /// If the frame is invalid (i.e. the length of the payload is not a 200 | /// multiple of 6) it returns `None`. 201 | fn parse_payload(payload: &[u8]) -> Option> { 202 | if payload.len() % 6 != 0 { 203 | return None; 204 | } 205 | 206 | // Iterates through chunks of the raw payload of size 6 bytes and 207 | // parses each of them into an `HttpSetting` 208 | Some(payload.chunks(6).filter_map(|chunk| { 209 | HttpSetting::parse_setting(chunk) 210 | }).collect()) 211 | } 212 | } 213 | 214 | impl Frame for SettingsFrame { 215 | /// The type that represents the flags that the particular `Frame` can take. 216 | /// This makes sure that only valid `Flag`s are used with each `Frame`. 217 | type FlagType = SettingsFlag; 218 | 219 | /// Creates a new `SettingsFrame` with the given `RawFrame` (i.e. header and 220 | /// payload), if possible. 221 | /// 222 | /// # Returns 223 | /// 224 | /// `None` if a valid `SettingsFrame` cannot be constructed from the given 225 | /// `RawFrame`. The stream ID *must* be 0 in order for the frame to be 226 | /// valid. If the `ACK` flag is set, there MUST not be a payload. The 227 | /// total payload length must be multiple of 6. 228 | /// 229 | /// Otherwise, returns a newly constructed `SettingsFrame`. 230 | fn from_raw(raw_frame: RawFrame) -> Option { 231 | // Unpack the header 232 | let (len, frame_type, flags, stream_id) = raw_frame.header; 233 | // Check that the frame type is correct for this frame implementation 234 | if frame_type != 0x4 { 235 | return None; 236 | } 237 | // Check that the length given in the header matches the payload 238 | // length; if not, something went wrong and we do not consider this a 239 | // valid frame. 240 | if (len as usize) != raw_frame.payload.len() { 241 | return None; 242 | } 243 | // Check that the SETTINGS frame is associated to stream 0 244 | if stream_id != 0 { 245 | return None; 246 | } 247 | if (flags & SettingsFlag::Ack.bitmask()) != 0 { 248 | if len != 0 { 249 | // The SETTINGS flag MUST not have a payload if Ack is set 250 | return None; 251 | } else { 252 | // Ack is set and there's no payload => just an Ack frame 253 | return Some(SettingsFrame { 254 | settings: Vec::new(), 255 | flags: flags, 256 | }); 257 | } 258 | } 259 | 260 | match SettingsFrame::parse_payload(&raw_frame.payload) { 261 | Some(settings) => { 262 | Some(SettingsFrame { 263 | settings: settings, 264 | flags: flags, 265 | }) 266 | }, 267 | None => None, 268 | } 269 | } 270 | 271 | /// Tests if the given flag is set for the frame. 272 | fn is_set(&self, flag: SettingsFlag) -> bool { 273 | (self.flags & flag.bitmask()) != 0 274 | } 275 | 276 | /// Returns the `StreamId` of the stream to which the frame is associated. 277 | /// 278 | /// A `SettingsFrame` always has to be associated to stream `0`. 279 | fn get_stream_id(&self) -> StreamId { 280 | 0 281 | } 282 | 283 | /// Returns a `FrameHeader` based on the current state of the `Frame`. 284 | fn get_header(&self) -> FrameHeader { 285 | (self.payload_len(), 0x4, self.flags, 0) 286 | } 287 | 288 | /// Sets the given flag for the frame. 289 | fn set_flag(&mut self, flag: SettingsFlag) { 290 | self.flags |= flag.bitmask(); 291 | } 292 | 293 | /// Returns a `Vec` with the serialized representation of the frame. 294 | fn serialize(&self) -> Vec { 295 | let mut buf = Vec::with_capacity(self.payload_len() as usize); 296 | // First the header... 297 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter()); 298 | // ...now the settings 299 | for setting in self.settings.iter() { 300 | buf.extend(setting.serialize().to_vec().into_iter()); 301 | } 302 | 303 | buf 304 | } 305 | } 306 | 307 | 308 | #[cfg(test)] 309 | mod tests { 310 | use super::super::frames::{Frame, RawFrame, pack_header}; 311 | use super::super::test::{build_test_frame}; 312 | use super::{HttpSetting, SettingsFrame}; 313 | 314 | /// Tests that the `HttpSetting::parse_setting` method correctly creates 315 | /// settings from raw bytes. 316 | #[test] 317 | fn test_setting_deserialize() { 318 | { 319 | let buf = [0, 1, 0, 0, 1, 0]; 320 | 321 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 322 | 323 | assert_eq!(setting, HttpSetting::HeaderTableSize(1 << 8)); 324 | } 325 | { 326 | let buf = [0, 2, 0, 0, 0, 1]; 327 | 328 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 329 | 330 | assert_eq!(setting, HttpSetting::EnablePush(1)); 331 | } 332 | { 333 | let buf = [0, 3, 0, 0, 0, 0]; 334 | 335 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 336 | 337 | assert_eq!(setting, HttpSetting::MaxConcurrentStreams(0)); 338 | } 339 | { 340 | let buf = [0, 4, 0, 0, 0, 1]; 341 | 342 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 343 | 344 | assert_eq!(setting, HttpSetting::InitialWindowSize(1)); 345 | } 346 | { 347 | let buf = [0, 5, 0, 0, 0, 255]; 348 | 349 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 350 | 351 | assert_eq!(setting, HttpSetting::MaxFrameSize((1 << 8) - 1)); 352 | } 353 | { 354 | let buf = [0, 6, 0, 0, 0, 255]; 355 | 356 | let setting = HttpSetting::parse_setting(&buf).unwrap(); 357 | 358 | assert_eq!(setting, HttpSetting::MaxHeaderListSize((1 << 8) - 1)); 359 | } 360 | { 361 | let buf = [0, 7, 0, 0, 0, 255]; 362 | 363 | let setting = HttpSetting::parse_setting(&buf); 364 | 365 | assert!(setting.is_none()); 366 | } 367 | { 368 | let buf = [0, 0, 0, 0, 0, 255]; 369 | 370 | let setting = HttpSetting::parse_setting(&buf); 371 | 372 | assert!(setting.is_none()); 373 | } 374 | } 375 | 376 | /// Tests that the `HttpSetting::serialize` method correctly creates 377 | /// a 6 byte buffer based on the given setting. 378 | #[test] 379 | fn test_setting_serialize() { 380 | { 381 | let buf = [0, 1, 0, 0, 1, 0]; 382 | 383 | let setting = HttpSetting::HeaderTableSize(1 << 8); 384 | 385 | assert_eq!(buf, setting.serialize()); 386 | } 387 | { 388 | let buf = [0, 2, 0, 0, 0, 1]; 389 | 390 | let setting = HttpSetting::EnablePush(1); 391 | 392 | assert_eq!(buf, setting.serialize()); 393 | } 394 | { 395 | let buf = [0, 3, 0, 0, 0, 0]; 396 | 397 | let setting = HttpSetting::MaxConcurrentStreams(0); 398 | 399 | assert_eq!(buf, setting.serialize()); 400 | } 401 | { 402 | let buf = [0, 4, 0, 0, 0, 1]; 403 | 404 | let setting = HttpSetting::InitialWindowSize(1); 405 | 406 | assert_eq!(buf, setting.serialize()); 407 | } 408 | { 409 | let buf = [0, 5, 0, 0, 0, 255]; 410 | 411 | let setting = HttpSetting::MaxFrameSize((1 << 8) - 1); 412 | 413 | assert_eq!(buf, setting.serialize()); 414 | } 415 | { 416 | let buf = [0, 6, 0, 0, 0, 255]; 417 | 418 | let setting = HttpSetting::MaxHeaderListSize((1 << 8) - 1); 419 | 420 | assert_eq!(buf, setting.serialize()); 421 | } 422 | } 423 | 424 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame with 425 | /// no ACK flag and only a single setting. 426 | #[test] 427 | fn test_settings_frame_parse_no_ack_one_setting() { 428 | let payload = [0, 1, 0, 0, 0, 1]; 429 | // A header with the flag indicating no padding 430 | let header = (payload.len() as u32, 4, 0, 0); 431 | 432 | let frame = build_test_frame::(&header, &payload); 433 | 434 | // The frame correctly interprets the settings? 435 | assert_eq!(frame.settings, vec![HttpSetting::HeaderTableSize(1)]); 436 | // ...and the headers? 437 | assert_eq!(frame.get_header(), header); 438 | } 439 | 440 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame with 441 | /// no ACK flag and multiple settings within the frame. 442 | #[test] 443 | fn test_settings_frame_parse_no_ack_multiple_settings() { 444 | let settings = vec![ 445 | HttpSetting::HeaderTableSize(1), 446 | HttpSetting::MaxHeaderListSize(5), 447 | HttpSetting::EnablePush(0), 448 | ]; 449 | let payload = { 450 | let mut res: Vec = Vec::new(); 451 | for s in settings.iter().map(|s| s.serialize()) { res.extend(s.to_vec().into_iter()); } 452 | 453 | res 454 | }; 455 | let header = (payload.len() as u32, 4, 0, 0); 456 | 457 | let frame = build_test_frame::(&header, &payload); 458 | 459 | // The frame correctly interprets the settings? 460 | assert_eq!(frame.settings, settings); 461 | // ...and the headers? 462 | assert_eq!(frame.get_header(), header); 463 | assert!(!frame.is_ack()); 464 | } 465 | 466 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame with 467 | /// no ACK and multiple *duplicate* settings within the frame. 468 | #[test] 469 | fn test_settings_frame_parse_no_ack_duplicate_settings() { 470 | let settings = vec![ 471 | HttpSetting::HeaderTableSize(1), 472 | HttpSetting::MaxHeaderListSize(5), 473 | HttpSetting::EnablePush(0), 474 | HttpSetting::HeaderTableSize(2), 475 | ]; 476 | let payload = { 477 | let mut res: Vec = Vec::new(); 478 | for s in settings.iter().map(|s| s.serialize()) { res.extend(s.to_vec().into_iter()); } 479 | 480 | res 481 | }; 482 | let header = (payload.len() as u32, 4, 0, 0); 483 | 484 | let frame = build_test_frame::(&header, &payload); 485 | 486 | // All the settings are returned, even the duplicates 487 | assert_eq!(frame.settings, settings); 488 | // ...and the headers? 489 | assert_eq!(frame.get_header(), header); 490 | assert!(!frame.is_ack()); 491 | } 492 | 493 | /// Tests that a `SettingsFrame` correctly handles a SETTING frame with no 494 | /// ACK and an unknown setting within the frame. The unknown setting is 495 | /// simply ignored. 496 | #[test] 497 | fn test_settings_frame_parse_no_ack_unknown_setting() { 498 | let settings = vec![ 499 | HttpSetting::HeaderTableSize(1), 500 | HttpSetting::MaxHeaderListSize(5), 501 | ]; 502 | let payload = { 503 | let mut res: Vec = Vec::new(); 504 | for s in settings.iter().map(|s| s.serialize()) { res.extend(s.to_vec().into_iter()); } 505 | res.extend(vec![0, 10, 0, 0, 0, 0].into_iter()); 506 | for s in settings.iter().map(|s| s.serialize()) { res.extend(s.to_vec().into_iter()); } 507 | 508 | res 509 | }; 510 | let header = (payload.len() as u32, 4, 0, 0); 511 | 512 | let frame = build_test_frame::(&header, &payload); 513 | 514 | // All the settings are returned twice, but the unkown isn't found in 515 | // the returned Vec. For now, we ignore the unknown setting fully, not 516 | // exposing it in any way to any other higher-level clients. 517 | assert_eq!(frame.settings.len(), 4); 518 | assert_eq!(&frame.settings[0..2], &settings[..]); 519 | assert_eq!(&frame.settings[2..], &settings[..]); 520 | assert!(!frame.is_ack()); 521 | } 522 | 523 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame with an 524 | /// ACK flag and no settings. 525 | #[test] 526 | fn test_settings_frame_parse_ack_no_settings() { 527 | let payload = []; 528 | let header = (payload.len() as u32, 4, 1, 0); 529 | 530 | let frame = build_test_frame::(&header, &payload); 531 | 532 | // No settings there? 533 | assert_eq!(frame.settings, vec![]); 534 | // ...and the headers? 535 | assert_eq!(frame.get_header(), header); 536 | // ...and the frame indicates it's an ACK 537 | assert!(frame.is_ack()); 538 | } 539 | 540 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame with an 541 | /// ACK flag, along with settings. In this case, the frame needs to be 542 | /// considered invalid. 543 | #[test] 544 | fn test_settings_frame_parse_ack_with_settings() { 545 | let settings = [ 546 | HttpSetting::EnablePush(0), 547 | ]; 548 | let payload = { 549 | let mut res: Vec = Vec::new(); 550 | for s in settings.iter().map(|s| s.serialize()) { res.extend(s.to_vec().into_iter()); } 551 | 552 | res 553 | }; 554 | let header = (payload.len() as u32, 4, 1, 0); 555 | 556 | let frame: Option = Frame::from_raw( 557 | RawFrame::with_payload(header, payload)); 558 | 559 | assert!(frame.is_none()); 560 | } 561 | 562 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame which 563 | /// was not associated to stream 0 by returning an error. 564 | #[test] 565 | fn test_settings_frame_parse_not_stream_zero() { 566 | let payload = vec![]; 567 | // Header indicates that it is associated to stream 1 568 | let header = (payload.len() as u32, 4, 1, 1); 569 | 570 | let frame: Option = Frame::from_raw( 571 | RawFrame::with_payload(header, payload)); 572 | 573 | assert!(frame.is_none()); 574 | } 575 | 576 | /// Tests that a `SettingsFrame` correctly handles a SETTINGS frame which 577 | /// does not have a payload with a number of bytes that's a multiple of 6. 578 | #[test] 579 | fn test_settings_frame_parse_not_multiple_of_six() { 580 | let payload = vec![1, 2, 3]; 581 | 582 | let header = (payload.len() as u32, 4, 0, 0); 583 | 584 | let frame: Option = Frame::from_raw( 585 | RawFrame::with_payload(header, payload)); 586 | 587 | assert!(frame.is_none()); 588 | } 589 | 590 | /// Tests that a `SettingsFrame` gets correctly serialized when it contains 591 | /// only settings and no ACK. 592 | #[test] 593 | fn test_settings_frame_serialize_no_ack_settings() { 594 | let mut frame = SettingsFrame::new(); 595 | frame.add_setting(HttpSetting::EnablePush(0)); 596 | let expected = { 597 | let mut res: Vec = Vec::new(); 598 | res.extend(pack_header(&(6, 4, 0, 0)).to_vec().into_iter()); 599 | res.extend(HttpSetting::EnablePush(0).serialize().to_vec().into_iter()); 600 | 601 | res 602 | }; 603 | 604 | let serialized = frame.serialize(); 605 | 606 | assert_eq!(serialized, expected); 607 | } 608 | 609 | /// Tests that a `SettingsFrame` gets correctly serialized when it contains 610 | /// multiple settings and no ACK. 611 | #[test] 612 | fn test_settings_frame_serialize_no_ack_multiple_settings() { 613 | let mut frame = SettingsFrame::new(); 614 | frame.add_setting(HttpSetting::EnablePush(0)); 615 | frame.add_setting(HttpSetting::MaxHeaderListSize(0)); 616 | let expected = { 617 | let mut res: Vec = Vec::new(); 618 | res.extend(pack_header(&(6 * 2, 4, 0, 0)).to_vec().into_iter()); 619 | res.extend(HttpSetting::EnablePush(0).serialize().to_vec().into_iter()); 620 | res.extend(HttpSetting::MaxHeaderListSize(0).serialize().to_vec().into_iter()); 621 | 622 | res 623 | }; 624 | 625 | let serialized = frame.serialize(); 626 | 627 | assert_eq!(serialized, expected); 628 | } 629 | 630 | /// Tests that a `SettingsFrame` gets correctly serialized when it contains 631 | /// multiple settings and no ACK. 632 | #[test] 633 | fn test_settings_frame_serialize_ack() { 634 | let frame = SettingsFrame::new_ack(); 635 | let expected = pack_header(&(0, 4, 1, 0)).to_vec(); 636 | 637 | let serialized = frame.serialize(); 638 | 639 | assert_eq!(serialized, expected); 640 | } 641 | } 642 | -------------------------------------------------------------------------------- /src/http/frame/headersframe.rs: -------------------------------------------------------------------------------- 1 | use super::super::StreamId; 2 | use super::frames::{ 3 | Frame, 4 | Flag, 5 | parse_padded_payload, 6 | pack_header, 7 | RawFrame, 8 | FrameHeader 9 | }; 10 | use super::{ 11 | ContinuationFrame, 12 | ContinuationFlag, 13 | }; 14 | use std::cmp::min; 15 | 16 | /// An enum representing the flags that a `HeadersFrame` can have. 17 | /// The integer representation associated to each variant is that flag's 18 | /// bitmask. 19 | /// 20 | /// HTTP/2 spec, section 6.2. 21 | #[derive(Clone)] 22 | #[derive(PartialEq)] 23 | #[derive(Debug)] 24 | #[derive(Copy)] 25 | pub enum HeadersFlag { 26 | EndStream = 0x1, 27 | EndHeaders = 0x4, 28 | Padded = 0x8, 29 | Priority = 0x20, 30 | } 31 | 32 | impl Flag for HeadersFlag { 33 | #[inline] 34 | fn bitmask(&self) -> u8 { 35 | *self as u8 36 | } 37 | } 38 | 39 | /// The struct represents the dependency information that can be attached to 40 | /// a stream and sent within a HEADERS frame (one with the Priority flag set). 41 | #[derive(PartialEq)] 42 | #[derive(Debug)] 43 | #[derive(Clone)] 44 | pub struct StreamDependency { 45 | /// The ID of the stream that a particular stream depends on 46 | pub stream_id: StreamId, 47 | /// The weight for the stream. The value exposed (and set) here is always 48 | /// in the range [0, 255], instead of [1, 256] \(as defined in section 5.3.2.) 49 | /// so that the value fits into a `u8`. 50 | pub weight: u8, 51 | /// A flag indicating whether the stream dependency is exclusive. 52 | pub is_exclusive: bool, 53 | } 54 | 55 | impl StreamDependency { 56 | /// Creates a new `StreamDependency` with the given stream ID, weight, and 57 | /// exclusivity. 58 | pub fn new(stream_id: StreamId, weight: u8, is_exclusive: bool) 59 | -> StreamDependency { 60 | StreamDependency { 61 | stream_id: stream_id, 62 | weight: weight, 63 | is_exclusive: is_exclusive, 64 | } 65 | } 66 | 67 | /// Parses the first 5 bytes in the buffer as a `StreamDependency`. 68 | /// (Each 5-byte sequence is always decodable into a stream dependency 69 | /// structure). 70 | /// 71 | /// # Panics 72 | /// 73 | /// If the given buffer has less than 5 elements, the method will panic. 74 | pub fn parse(buf: &[u8]) -> StreamDependency { 75 | // The most significant bit of the first byte is the "E" bit indicating 76 | // whether the dependency is exclusive. 77 | let is_exclusive = buf[0] & 0x80 != 0; 78 | let stream_id = { 79 | // Parse the first 4 bytes into a u32... 80 | let mut id = unpack_octets_4!(buf, 0, u32); 81 | // ...clear the first bit since the stream id is only 31 bits. 82 | id &= !(1 << 31); 83 | id 84 | }; 85 | 86 | StreamDependency { 87 | stream_id: stream_id, 88 | weight: buf[4], 89 | is_exclusive: is_exclusive, 90 | } 91 | } 92 | 93 | /// Serializes the `StreamDependency` into a 5-byte buffer representing the 94 | /// dependency description, as described in section 6.2. of the HTTP/2 95 | /// spec: 96 | /// 97 | /// ```notest 98 | /// 0 1 2 3 99 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 100 | /// +-+-------------+-----------------------------------------------+ 101 | /// |E| Stream Dependency (31) | 102 | /// +-+-------------+-----------------------------------------------+ 103 | /// | Weight (8) | 104 | /// +-+-------------+-----------------------------------------------+ 105 | /// ``` 106 | /// 107 | /// Where "E" is set if the dependency is exclusive. 108 | pub fn serialize(&self) -> [u8; 5] { 109 | let e_bit = if self.is_exclusive { 110 | 1 << 7 111 | } else { 112 | 0 113 | }; 114 | [ 115 | (((self.stream_id >> 24) & 0x000000FF) as u8) | e_bit, 116 | (((self.stream_id >> 16) & 0x000000FF) as u8), 117 | (((self.stream_id >> 8) & 0x000000FF) as u8), 118 | (((self.stream_id >> 0) & 0x000000FF) as u8), 119 | self.weight, 120 | ] 121 | } 122 | } 123 | 124 | /// A struct representing the HEADERS frames of HTTP/2, as defined in the 125 | /// HTTP/2 spec, section 6.2. 126 | #[derive(PartialEq)] 127 | #[derive(Debug)] 128 | pub struct HeadersFrame { 129 | /// The header fragment bytes stored within the frame. 130 | pub header_fragment: Vec, 131 | /// The ID of the stream with which this frame is associated 132 | pub stream_id: StreamId, 133 | /// The stream dependency information, if any. 134 | pub stream_dep: Option, 135 | /// The length of the padding, if any. 136 | pub padding_len: Option, 137 | /// The set of flags for the frame, packed into a single byte. 138 | flags: u8, 139 | } 140 | 141 | impl HeadersFrame { 142 | /// Creates a new `HeadersFrame` with the given header fragment and stream 143 | /// ID. No padding, no stream dependency, and no flags are set. 144 | pub fn new(fragment: Vec, stream_id: StreamId) -> HeadersFrame { 145 | HeadersFrame { 146 | header_fragment: fragment, 147 | stream_id: stream_id, 148 | stream_dep: None, 149 | padding_len: None, 150 | flags: 0, 151 | } 152 | } 153 | 154 | /// Creates a new `HeadersFrame` with the given header fragment, stream ID 155 | /// and stream dependency information. No padding and no flags are set. 156 | pub fn with_dependency( 157 | fragment: Vec, 158 | stream_id: StreamId, 159 | stream_dep: StreamDependency) -> HeadersFrame { 160 | HeadersFrame { 161 | header_fragment: fragment, 162 | stream_id: stream_id, 163 | stream_dep: Some(stream_dep), 164 | padding_len: None, 165 | flags: HeadersFlag::Priority.bitmask(), 166 | } 167 | } 168 | 169 | /// Returns whether this frame ends the headers. If not, there MUST be a 170 | /// number of follow up CONTINUATION frames that send the rest of the 171 | /// header data. 172 | pub fn is_headers_end(&self) -> bool { 173 | self.is_set(HeadersFlag::EndHeaders) 174 | } 175 | 176 | /// Returns whther this frame ends the stream it is associated with. 177 | pub fn is_end_of_stream(&self) -> bool { 178 | self.is_set(HeadersFlag::EndStream) 179 | } 180 | 181 | /// Sets the padding length for the frame, as well as the corresponding 182 | /// Padded flag. 183 | pub fn set_padding(&mut self, padding_len: u8) { 184 | self.padding_len = Some(padding_len); 185 | self.set_flag(HeadersFlag::Padded); 186 | } 187 | 188 | /// Returns the length of the payload of the current frame, including any 189 | /// possible padding in the number of bytes. 190 | fn payload_len(&self) -> u32 { 191 | let padding = if self.is_set(HeadersFlag::Padded) { 192 | 1 + self.padding_len.unwrap_or(0) as u32 193 | } else { 194 | 0 195 | }; 196 | let priority = if self.is_set(HeadersFlag::Priority) { 197 | 5 198 | } else { 199 | 0 200 | }; 201 | 202 | self.header_fragment.len() as u32 + priority + padding 203 | } 204 | } 205 | 206 | impl Frame for HeadersFrame { 207 | /// The type that represents the flags that the particular `Frame` can take. 208 | /// This makes sure that only valid `Flag`s are used with each `Frame`. 209 | type FlagType = HeadersFlag; 210 | 211 | /// Creates a new `HeadersFrame` with the given `RawFrame` (i.e. header and 212 | /// payload), if possible. 213 | /// 214 | /// # Returns 215 | /// 216 | /// `None` if a valid `HeadersFrame` cannot be constructed from the given 217 | /// `RawFrame`. The stream ID *must not* be 0. 218 | /// 219 | /// Otherwise, returns a newly constructed `HeadersFrame`. 220 | fn from_raw(raw_frame: RawFrame) -> Option { 221 | // Unpack the header 222 | let (len, frame_type, flags, stream_id) = raw_frame.header; 223 | // Check that the frame type is correct for this frame implementation 224 | if frame_type != 0x1 { 225 | return None; 226 | } 227 | // Check that the length given in the header matches the payload 228 | // length; if not, something went wrong and we do not consider this a 229 | // valid frame. 230 | if (len as usize) != raw_frame.payload.len() { 231 | return None; 232 | } 233 | // Check that the HEADERS frame is not associated to stream 0 234 | if stream_id == 0 { 235 | return None; 236 | } 237 | 238 | // First, we get a slice containing the actual payload, depending on if 239 | // the frame is padded. 240 | let padded = (flags & HeadersFlag::Padded.bitmask()) != 0; 241 | let (actual, pad_len) = if padded { 242 | match parse_padded_payload(&raw_frame.payload) { 243 | Some((data, pad_len)) => (data, Some(pad_len)), 244 | None => return None, 245 | } 246 | } else { 247 | (&raw_frame.payload[..], None) 248 | }; 249 | 250 | // From the actual payload we extract the stream dependency info, if 251 | // the appropriate flag is set. 252 | let priority = (flags & HeadersFlag::Priority.bitmask()) != 0; 253 | let (data, stream_dep) = if priority { 254 | (&actual[5..], Some(StreamDependency::parse(&actual[..5]))) 255 | } else { 256 | (actual, None) 257 | }; 258 | 259 | Some(HeadersFrame { 260 | header_fragment: data.to_vec(), 261 | stream_id: stream_id, 262 | stream_dep: stream_dep, 263 | padding_len: pad_len, 264 | flags: flags, 265 | }) 266 | } 267 | 268 | /// Tests if the given flag is set for the frame. 269 | fn is_set(&self, flag: HeadersFlag) -> bool { 270 | (self.flags & flag.bitmask()) != 0 271 | } 272 | 273 | /// Returns the `StreamId` of the stream to which the frame is associated. 274 | /// 275 | /// A `SettingsFrame` always has to be associated to stream `0`. 276 | fn get_stream_id(&self) -> StreamId { 277 | self.stream_id 278 | } 279 | 280 | /// Returns a `FrameHeader` based on the current state of the `Frame`. 281 | fn get_header(&self) -> FrameHeader { 282 | (self.payload_len(), 0x1, self.flags, self.stream_id) 283 | } 284 | 285 | /// Sets the given flag for the frame. 286 | fn set_flag(&mut self, flag: HeadersFlag) { 287 | self.flags |= flag.bitmask(); 288 | } 289 | 290 | /// Returns a `Vec` with the serialized representation of the frame. 291 | /// 292 | /// # Panics 293 | /// 294 | /// If the `HeadersFlag::Priority` flag was set, but no stream dependency 295 | /// information is given (i.e. `stream_dep` is `None`). 296 | fn serialize(&self) -> Vec { 297 | let mut buf = Vec::with_capacity(self.payload_len() as usize); 298 | // First the header... 299 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter()); 300 | // Now the length of the padding, if any. 301 | let padded = self.is_set(HeadersFlag::Padded); 302 | if padded { 303 | buf.push(self.padding_len.unwrap_or(0)); 304 | } 305 | // The stream dependency fields follow, if the priority flag is set 306 | if self.is_set(HeadersFlag::Priority) { 307 | let dep_buf = match self.stream_dep { 308 | Some(ref dep) => dep.serialize(), 309 | None => panic!("Priority flag set, but no dependency information given"), 310 | }; 311 | buf.extend(dep_buf.to_vec().into_iter()); 312 | } 313 | // If the length of payload + header + padding > max frame size, seperate this 314 | // into multiple frames 315 | // TODO: replace 16384 with MAX_FRAME_SIZE from settings frame 316 | if self.payload_len() + 9 + (self.padding_len.unwrap_or(0) as u32) > 16384 { 317 | //Break the payload first 318 | //can't just access the end of the vec, check for where to slice the fragment 319 | //the pad length, stream dep, and weight have already been written, so we 320 | //only have room for max frame size - 9(header size) - payload field size 321 | let mut chunk_size = min(self.header_fragment.len(), (16384 - 9) as usize - (self.payload_len() as usize - &self.header_fragment.len())); 322 | let (mut data, mut remainder) = (self.header_fragment[..chunk_size].to_vec(), &self.header_fragment[chunk_size..]); 323 | while remainder.len() > 0{ 324 | buf.extend(ContinuationFrame::new(data.to_vec(), self.stream_id).serialize().into_iter()); 325 | chunk_size = min(remainder.len(), (16384 - 9) as usize); 326 | data = remainder[..chunk_size].to_vec(); 327 | remainder = &remainder[chunk_size..]; 328 | } 329 | // then the padding 330 | if padded { 331 | chunk_size = min(self.padding_len.unwrap_or(0) as usize, 16384 - 9 - data.len()); 332 | for _ in 0..chunk_size { data.push(0) }; 333 | let mut padding_len = self.padding_len.unwrap_or(0) as usize - chunk_size; 334 | while padding_len > 0 { 335 | buf.extend(ContinuationFrame::new(data, self.stream_id).serialize().into_iter()); 336 | chunk_size = min(padding_len, 16384 - 9); 337 | padding_len = padding_len - chunk_size; 338 | data = Vec::with_capacity(chunk_size as usize); 339 | for _ in 0..chunk_size { data.push(0); } 340 | } 341 | 342 | } 343 | buf.extend(ContinuationFrame::with_end(data, self.stream_id).serialize().into_iter()); 344 | 345 | } else { 346 | // Now the actual headers fragment 347 | buf.extend(self.header_fragment.clone().into_iter()); 348 | // Finally, add the trailing padding, if required 349 | if padded { 350 | for _ in 0..self.padding_len.unwrap_or(0) { buf.push(0); } 351 | } 352 | } 353 | 354 | buf 355 | } 356 | } 357 | 358 | #[cfg(test)] 359 | mod tests { 360 | use super::super::frames::{Frame, RawFrame, pack_header}; 361 | use super::super::test::{build_test_frame, build_padded_frame_payload}; 362 | use super::{HeadersFrame, HeadersFlag, StreamDependency}; 363 | 364 | /// Tests that a simple HEADERS frame is correctly parsed. The frame does 365 | /// not contain any padding nor priority information. 366 | #[test] 367 | fn test_headers_frame_parse_simple() { 368 | let data = b"123"; 369 | let payload = data.to_vec(); 370 | let header = (payload.len() as u32, 0x1, 0, 1); 371 | 372 | let frame = build_test_frame::(&header, &payload); 373 | 374 | assert_eq!(frame.header_fragment, data); 375 | assert_eq!(frame.flags, 0); 376 | assert_eq!(frame.get_stream_id(), 1); 377 | assert!(frame.stream_dep.is_none()); 378 | assert!(frame.padding_len.is_none()); 379 | } 380 | 381 | /// Tests that a HEADERS frame with padding is correctly parsed. 382 | #[test] 383 | fn test_headers_frame_parse_with_padding() { 384 | let data = b"123"; 385 | let payload = build_padded_frame_payload(data, 6); 386 | let header = (payload.len() as u32, 0x1, 0x08, 1); 387 | 388 | let frame = build_test_frame::(&header, &payload); 389 | 390 | assert_eq!(frame.header_fragment, data); 391 | assert_eq!(frame.flags, 8); 392 | assert_eq!(frame.get_stream_id(), 1); 393 | assert!(frame.stream_dep.is_none()); 394 | assert_eq!(frame.padding_len.unwrap(), 6); 395 | } 396 | 397 | /// Tests that a HEADERS frame with the priority flag (and necessary fields) 398 | /// is correctly parsed. 399 | #[test] 400 | fn test_headers_frame_parse_with_priority() { 401 | let data = b"123"; 402 | let dep = StreamDependency::new(0, 5, true); 403 | let payload = { 404 | let mut buf: Vec = Vec::new(); 405 | buf.extend(dep.serialize().to_vec().into_iter()); 406 | buf.extend(data.to_vec().into_iter()); 407 | 408 | buf 409 | }; 410 | let header = (payload.len() as u32, 0x1, 0x20, 1); 411 | 412 | let frame = build_test_frame::(&header, &payload); 413 | 414 | assert_eq!(frame.header_fragment, data); 415 | assert_eq!(frame.flags, 0x20); 416 | assert_eq!(frame.get_stream_id(), 1); 417 | assert_eq!(frame.stream_dep.unwrap(), dep); 418 | assert!(frame.padding_len.is_none()); 419 | } 420 | 421 | /// Tests that a HEADERS frame with both padding and priority gets 422 | /// correctly parsed. 423 | #[test] 424 | fn test_headers_frame_parse_padding_and_priority() { 425 | let data = b"123"; 426 | let dep = StreamDependency::new(0, 5, true); 427 | let full = { 428 | let mut buf: Vec = Vec::new(); 429 | buf.extend(dep.serialize().to_vec().into_iter()); 430 | buf.extend(data.to_vec().into_iter()); 431 | 432 | buf 433 | }; 434 | let payload = build_padded_frame_payload(&full, 4); 435 | let header = (payload.len() as u32, 0x1, 0x20 | 0x8, 1); 436 | 437 | let frame = build_test_frame::(&header, &payload); 438 | 439 | assert_eq!(frame.header_fragment, data); 440 | assert_eq!(frame.flags, 0x20 | 0x8); 441 | assert_eq!(frame.get_stream_id(), 1); 442 | assert_eq!(frame.stream_dep.unwrap(), dep); 443 | assert_eq!(frame.padding_len.unwrap(), 4); 444 | } 445 | 446 | /// Tests that a HEADERS with stream ID 0 is considered invalid. 447 | #[test] 448 | fn test_headers_frame_parse_invalid_stream_id() { 449 | let data = b"123"; 450 | let payload = data.to_vec(); 451 | let header = (payload.len() as u32, 0x1, 0, 0); 452 | 453 | let frame: Option = Frame::from_raw( 454 | RawFrame::with_payload(header, payload)); 455 | 456 | assert!(frame.is_none()); 457 | } 458 | 459 | /// Tests that the `HeadersFrame::parse` method considers any frame with 460 | /// a frame ID other than 1 in the frame header invalid. 461 | #[test] 462 | fn test_headers_frame_parse_invalid_type() { 463 | let data = b"123"; 464 | let payload = data.to_vec(); 465 | let header = (payload.len() as u32, 0x2, 0, 1); 466 | 467 | let frame: Option = Frame::from_raw( 468 | RawFrame::with_payload(header, payload)); 469 | 470 | assert!(frame.is_none()); 471 | } 472 | 473 | /// Tests that a simple HEADERS frame (no padding, no priority) gets 474 | /// correctly serialized. 475 | #[test] 476 | fn test_headers_frame_serialize_simple() { 477 | let data = b"123"; 478 | let payload = data.to_vec(); 479 | let header = (payload.len() as u32, 0x1, 0, 1); 480 | let expected = { 481 | let headers = pack_header(&header); 482 | let mut res: Vec = Vec::new(); 483 | res.extend(headers.to_vec().into_iter()); 484 | res.extend(payload.into_iter()); 485 | 486 | res 487 | }; 488 | let frame = HeadersFrame::new(data.to_vec(), 1); 489 | 490 | let actual = frame.serialize(); 491 | 492 | assert_eq!(expected, actual); 493 | } 494 | 495 | /// Tests that a HEADERS frame with padding is correctly serialized. 496 | #[test] 497 | fn test_headers_frame_serialize_with_padding() { 498 | let data = b"123"; 499 | let payload = build_padded_frame_payload(data, 6); 500 | let header = (payload.len() as u32, 0x1, 0x08, 1); 501 | let expected = { 502 | let headers = pack_header(&header); 503 | let mut res: Vec = Vec::new(); 504 | res.extend(headers.to_vec().into_iter()); 505 | res.extend(payload.into_iter()); 506 | 507 | res 508 | }; 509 | let mut frame = HeadersFrame::new(data.to_vec(), 1); 510 | frame.set_padding(6); 511 | 512 | let actual = frame.serialize(); 513 | 514 | assert_eq!(expected, actual); 515 | } 516 | 517 | /// Tests that a HEADERS frame with priority gets correctly serialized. 518 | #[test] 519 | fn test_headers_frame_serialize_with_priority() { 520 | let data = b"123"; 521 | let dep = StreamDependency::new(0, 5, true); 522 | let payload = { 523 | let mut buf: Vec = Vec::new(); 524 | buf.extend(dep.serialize().to_vec().into_iter()); 525 | buf.extend(data.to_vec().into_iter()); 526 | 527 | buf 528 | }; 529 | let header = (payload.len() as u32, 0x1, 0x20, 1); 530 | let expected = { 531 | let headers = pack_header(&header); 532 | let mut res: Vec = Vec::new(); 533 | res.extend(headers.to_vec().into_iter()); 534 | res.extend(payload.into_iter()); 535 | 536 | res 537 | }; 538 | let frame = HeadersFrame::with_dependency(data.to_vec(), 1, dep.clone()); 539 | 540 | let actual = frame.serialize(); 541 | 542 | assert_eq!(expected, actual); 543 | } 544 | 545 | /// Tests that a HEADERS frame with both padding and a priority gets correctly 546 | /// serialized. 547 | #[test] 548 | fn test_headers_frame_serialize_padding_and_priority() { 549 | let data = b"123"; 550 | let dep = StreamDependency::new(0, 5, true); 551 | let full = { 552 | let mut buf: Vec = Vec::new(); 553 | buf.extend(dep.serialize().to_vec().into_iter()); 554 | buf.extend(data.to_vec().into_iter()); 555 | 556 | buf 557 | }; 558 | let payload = build_padded_frame_payload(&full, 4); 559 | let header = (payload.len() as u32, 0x1, 0x20 | 0x8, 1); 560 | let expected = { 561 | let headers = pack_header(&header); 562 | let mut res: Vec = Vec::new(); 563 | res.extend(headers.to_vec().into_iter()); 564 | res.extend(payload.into_iter()); 565 | 566 | res 567 | }; 568 | let mut frame = HeadersFrame::with_dependency(data.to_vec(), 1, dep.clone()); 569 | frame.set_padding(4); 570 | 571 | let actual = frame.serialize(); 572 | 573 | assert_eq!(expected, actual); 574 | } 575 | 576 | /// Tests that the `HeadersFrame::is_headers_end` method returns the correct 577 | /// value depending on the `EndHeaders` flag being set or not. 578 | #[test] 579 | fn test_headers_frame_is_headers_end() { 580 | let mut frame = HeadersFrame::new(vec![], 1); 581 | assert!(!frame.is_headers_end()); 582 | 583 | frame.set_flag(HeadersFlag::EndHeaders); 584 | assert!(frame.is_headers_end()); 585 | } 586 | /// Tests that a stream dependency structure can be correctly parsed by the 587 | /// `StreamDependency::parse` method. 588 | #[test] 589 | fn test_parse_stream_dependency() { 590 | { 591 | let buf = [0, 0, 0, 1, 5]; 592 | 593 | let dep = StreamDependency::parse(&buf); 594 | 595 | assert_eq!(dep.stream_id, 1); 596 | assert_eq!(dep.weight, 5); 597 | // This one was not exclusive! 598 | assert!(!dep.is_exclusive) 599 | } 600 | { 601 | // Most significant bit set => is exclusive! 602 | let buf = [128, 0, 0, 1, 5]; 603 | 604 | let dep = StreamDependency::parse(&buf); 605 | 606 | assert_eq!(dep.stream_id, 1); 607 | assert_eq!(dep.weight, 5); 608 | // This one was indeed exclusive! 609 | assert!(dep.is_exclusive) 610 | } 611 | { 612 | // Most significant bit set => is exclusive! 613 | let buf = [255, 255, 255, 255, 5]; 614 | 615 | let dep = StreamDependency::parse(&buf); 616 | 617 | assert_eq!(dep.stream_id, (1 << 31) - 1); 618 | assert_eq!(dep.weight, 5); 619 | // This one was indeed exclusive! 620 | assert!(dep.is_exclusive); 621 | } 622 | { 623 | let buf = [127, 255, 255, 255, 5]; 624 | 625 | let dep = StreamDependency::parse(&buf); 626 | 627 | assert_eq!(dep.stream_id, (1 << 31) - 1); 628 | assert_eq!(dep.weight, 5); 629 | // This one was not exclusive! 630 | assert!(!dep.is_exclusive); 631 | } 632 | } 633 | 634 | /// Tests that a stream dependency structure can be correctly serialized by 635 | /// the `StreamDependency::serialize` method. 636 | #[test] 637 | fn test_serialize_stream_dependency() { 638 | { 639 | let buf = [0, 0, 0, 1, 5]; 640 | let dep = StreamDependency::new(1, 5, false); 641 | 642 | assert_eq!(buf, dep.serialize()); 643 | } 644 | { 645 | // Most significant bit set => is exclusive! 646 | let buf = [128, 0, 0, 1, 5]; 647 | let dep = StreamDependency::new(1, 5, true); 648 | 649 | assert_eq!(buf, dep.serialize()); 650 | } 651 | { 652 | // Most significant bit set => is exclusive! 653 | let buf = [255, 255, 255, 255, 5]; 654 | let dep = StreamDependency::new((1 << 31) - 1, 5, true); 655 | 656 | assert_eq!(buf, dep.serialize()); 657 | } 658 | { 659 | let buf = [127, 255, 255, 255, 5]; 660 | let dep = StreamDependency::new((1 << 31) - 1, 5, false); 661 | 662 | assert_eq!(buf, dep.serialize()); 663 | } 664 | } 665 | } 666 | -------------------------------------------------------------------------------- /src/http/connection.rs: -------------------------------------------------------------------------------- 1 | //! The module contains the implementation of an HTTP/2 connection. 2 | //! 3 | //! This provides an API to read and write raw HTTP/2 frames. 4 | //! 5 | //! The basic `HttpConnection` provides an API to read and write raw HTTP/2 6 | //! frames. 7 | //! 8 | //! The `ClientConnection` provides a slightly higher level API (based on the 9 | //! `HttpConnection`) that exposes client-specific functions of an HTTP/2 10 | //! connection, such as sending requests. 11 | 12 | use std::net::TcpStream; 13 | use std::convert::AsRef; 14 | use std::marker::MarkerTrait; 15 | use std::borrow::Cow; 16 | use std::path::Path; 17 | use std::io; 18 | use std::str; 19 | 20 | use openssl::ssl::{Ssl, SslStream, SslContext}; 21 | use openssl::ssl::{SSL_VERIFY_PEER, SSL_VERIFY_FAIL_IF_NO_PEER_CERT}; 22 | use openssl::ssl::SSL_OP_NO_COMPRESSION; 23 | use openssl::ssl::error::SslError; 24 | use openssl::ssl::SslMethod; 25 | 26 | use super::session::Session; 27 | use super::ALPN_PROTOCOLS; 28 | use super::{HttpError, HttpResult, Request, HttpScheme}; 29 | use super::transport::TransportStream; 30 | use super::frame::{ 31 | Frame, 32 | RawFrame, 33 | DataFrame, 34 | DataFlag, 35 | HeadersFrame, 36 | HeadersFlag, 37 | SettingsFrame, 38 | HttpSetting, 39 | unpack_header, 40 | }; 41 | use hpack; 42 | 43 | /// An enum representing all frame variants that can be returned by an 44 | /// `HttpConnection`. 45 | /// 46 | /// The variants wrap the appropriate `Frame` implementation. 47 | #[derive(PartialEq)] 48 | #[derive(Debug)] 49 | pub enum HttpFrame { 50 | DataFrame(DataFrame), 51 | HeadersFrame(HeadersFrame), 52 | SettingsFrame(SettingsFrame), 53 | } 54 | 55 | /// The struct implements the HTTP/2 connection level logic. 56 | /// 57 | /// It provides an API for writing and reading HTTP/2 frames. It also takes 58 | /// care to validate the received frames. 59 | pub struct HttpConnection where S: TransportStream { 60 | /// The stream to which the raw bytes will be written 61 | stream: S, 62 | /// The scheme of the connection 63 | pub scheme: HttpScheme, 64 | /// The host to which the connection is established 65 | pub host: String, 66 | } 67 | 68 | impl HttpConnection where S: TransportStream { 69 | /// Creates a new `HttpConnection` that will use the given stream as its 70 | /// underlying transport layer. 71 | /// 72 | /// The host to which the connection is established, as well as the connection 73 | /// scheme are provided. 74 | pub fn new<'a>(stream: S, scheme: HttpScheme, host: Cow<'a, str>) -> HttpConnection { 75 | HttpConnection { 76 | stream: stream, 77 | scheme: scheme, 78 | host: host.into_owned(), 79 | } 80 | } 81 | 82 | /// Sends the given frame to the peer. 83 | /// 84 | /// # Returns 85 | /// 86 | /// Any IO errors raised by the underlying transport layer are wrapped in a 87 | /// `HttpError::IoError` variant and propagated upwards. 88 | /// 89 | /// If the frame is successfully written, returns a unit Ok (`Ok(())`). 90 | pub fn send_frame(&mut self, frame: F) -> HttpResult<()> { 91 | debug!("Sending frame ... {:?}", frame.get_header()); 92 | try!(self.stream.write_all(&frame.serialize())); 93 | Ok(()) 94 | } 95 | 96 | /// Reads a new frame from the transport layer. 97 | /// 98 | /// # Returns 99 | /// 100 | /// Any IO errors raised by the underlying transport layer are wrapped in a 101 | /// `HttpError::IoError` variant and propagated upwards. 102 | /// 103 | /// If the frame type is unknown the `HttpError::UnknownFrameType` variant 104 | /// is returned. 105 | /// 106 | /// If the frame type is recognized, but the frame cannot be successfully 107 | /// decoded, the `HttpError::InvalidFrame` variant is returned. For now, 108 | /// invalid frames are not further handled by informing the peer (e.g. 109 | /// sending PROTOCOL_ERROR) nor can the exact reason behind failing to 110 | /// decode the frame be extracted. 111 | /// 112 | /// If a frame is successfully read and parsed, returns the frame wrapped 113 | /// in the appropriate variant of the `HttpFrame` enum. 114 | pub fn recv_frame(&mut self) -> HttpResult { 115 | let header = unpack_header(&try!(self.read_header_bytes())); 116 | debug!("Received frame header {:?}", header); 117 | 118 | let payload = try!(self.read_payload(header.0)); 119 | let raw_frame = RawFrame::with_payload(header, payload); 120 | 121 | // TODO: The reason behind being unable to decode the frame should be 122 | // extracted and an appropriate connection-level action taken 123 | // (e.g. responding with a PROTOCOL_ERROR). 124 | let frame = match header.1 { 125 | 0x0 => HttpFrame::DataFrame(try!(self.parse_frame(raw_frame))), 126 | 0x1 => HttpFrame::HeadersFrame(try!(self.parse_frame(raw_frame))), 127 | 0x4 => HttpFrame::SettingsFrame(try!(self.parse_frame(raw_frame))), 128 | _ => return Err(HttpError::UnknownFrameType), 129 | }; 130 | 131 | Ok(frame) 132 | } 133 | 134 | /// Reads the header bytes of the next frame from the underlying stream. 135 | /// 136 | /// # Returns 137 | /// 138 | /// Since each frame header is exactly 9 octets long, returns an array of 139 | /// 9 bytes if the frame header is successfully read. 140 | /// 141 | /// Any IO errors raised by the underlying transport layer are wrapped in a 142 | /// `HttpError::IoError` variant and propagated upwards. 143 | fn read_header_bytes(&mut self) -> HttpResult<[u8; 9]> { 144 | let mut buf = [0; 9]; 145 | try!(self.stream.read_exact(&mut buf)); 146 | 147 | Ok(buf) 148 | } 149 | 150 | /// Reads the payload of an HTTP/2 frame with the given length. 151 | /// 152 | /// # Returns 153 | /// 154 | /// A newly allocated buffer containing the entire payload of the frame. 155 | /// 156 | /// Any IO errors raised by the underlying transport layer are wrapped in a 157 | /// `HttpError::IoError` variant and propagated upwards. 158 | fn read_payload(&mut self, len: u32) -> HttpResult> { 159 | debug!("Trying to read {} bytes of frame payload", len); 160 | let length = len as usize; 161 | let mut buf: Vec = Vec::with_capacity(length); 162 | // This is completely safe since we *just* allocated the vector with 163 | // the same capacity. 164 | unsafe { buf.set_len(length); } 165 | try!(self.stream.read_exact(&mut buf)); 166 | 167 | Ok(buf) 168 | } 169 | 170 | /// A helper method that parses the given `RawFrame` into the given `Frame` 171 | /// implementation. 172 | /// 173 | /// # Returns 174 | /// 175 | /// Failing to decode the given `Frame` from the `raw_frame`, an 176 | /// `HttpError::InvalidFrame` error is returned. 177 | #[inline] 178 | fn parse_frame(&self, raw_frame: RawFrame) -> HttpResult { 179 | Frame::from_raw(raw_frame).ok_or(HttpError::InvalidFrame) 180 | } 181 | } 182 | 183 | /// A marker trait for errors raised by attempting to establish an HTTP/2 184 | /// connection. 185 | pub trait HttpConnectError: MarkerTrait {} 186 | 187 | /// A trait that can be implemented by structs that want to provide the 188 | /// functionality of establishing HTTP/2 connections. 189 | pub trait HttpConnect { 190 | /// The type of the underlying transport stream that the `HttpConnection`s 191 | /// produced by this `HttpConnect` implementation will be based on. 192 | type Stream: TransportStream; 193 | /// The type of the error that can be produced by trying to establish the 194 | /// connection (i.e. calling the `connect` method). 195 | type Err; 196 | 197 | /// Establishes an HTTP/2 connection... 198 | fn connect(self) -> Result, Self::Err>; 199 | } 200 | 201 | /// A struct implementing the functionality of establishing an HTTP/2 202 | /// connection over a TLS-backed stream. The protocol negotiation also takes 203 | /// place within the TLS negotiation. 204 | pub struct TlsConnector<'a, 'ctx> { 205 | pub host: &'a str, 206 | // pub context: Option<&'ctx SslContext>, 207 | context: Http2TlsContext<'ctx>, 208 | } 209 | 210 | /// A private enum that represents the two options for configuring the 211 | /// `TlsConnector` 212 | enum Http2TlsContext<'a> { 213 | /// This means that the `TlsConnector` will use the referenced `SslContext` 214 | /// instance when creating a new `SslStream` 215 | Wrapped(&'a SslContext), 216 | /// This means that the `TlsConnector` will create a new context with the 217 | /// certificates file being found at the given path. 218 | CertPath(&'a Path), 219 | } 220 | 221 | /// An enum representing possible errors that can arise when trying to 222 | /// establish an HTTP/2 connection over TLS. 223 | pub enum TlsConnectError { 224 | /// The variant corresponds to the underlying raw TCP connection returning 225 | /// an error. 226 | IoError(io::Error), 227 | /// The variant corresponds to the TLS negotiation returning an error. 228 | SslError(SslError), 229 | /// The variant corresponds to the case when the TLS connection is 230 | /// established, but the application protocol that was negotiated didn't 231 | /// end up being HTTP/2. 232 | /// It wraps the established SSL stream in order to allow the client to 233 | /// decide what to do with it (and the application protocol that was 234 | /// chosen). 235 | Http2NotSupported(SslStream), 236 | } 237 | 238 | impl From for TlsConnectError { 239 | fn from(err: io::Error) -> TlsConnectError { 240 | TlsConnectError::IoError(err) 241 | } 242 | } 243 | 244 | impl From for TlsConnectError { 245 | fn from(err: SslError) -> TlsConnectError { 246 | TlsConnectError::SslError(err) 247 | } 248 | } 249 | 250 | impl HttpConnectError for TlsConnectError {} 251 | 252 | impl<'a, 'ctx> TlsConnector<'a, 'ctx> { 253 | /// Creates a new `TlsConnector` that will create a new `SslContext` before 254 | /// trying to establish the TLS connection. The path to the CA file that the 255 | /// context will use needs to be provided. 256 | pub fn new>(host: &'a str, ca_file_path: &'ctx P) -> TlsConnector<'a, 'ctx> { 257 | TlsConnector { 258 | host: host, 259 | context: Http2TlsContext::CertPath(ca_file_path.as_ref()), 260 | } 261 | } 262 | 263 | /// Creates a new `TlsConnector` that will use the provided context to 264 | /// create the `SslStream` that will back the HTTP/2 connection. 265 | pub fn with_context(host: &'a str, context: &'ctx SslContext) -> TlsConnector<'a, 'ctx> { 266 | TlsConnector { 267 | host: host, 268 | context: Http2TlsContext::Wrapped(context), 269 | } 270 | } 271 | 272 | /// Builds up a default `SslContext` instance wth TLS settings that the 273 | /// HTTP/2 spec mandates. The path to the CA file needs to be provided. 274 | pub fn build_default_context(ca_file_path: &Path) -> Result { 275 | // HTTP/2 connections need to be on top of TLSv1.2 or newer. 276 | let mut context = try!(SslContext::new(SslMethod::Tlsv1_2)); 277 | 278 | // This makes the certificate required (only VERIFY_PEER would mean optional) 279 | context.set_verify(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, None); 280 | try!(context.set_CA_file(ca_file_path)); 281 | // Compression is not allowed by the spec 282 | context.set_options(SSL_OP_NO_COMPRESSION); 283 | // The HTTP/2 protocol identifiers are constant at the library level... 284 | context.set_npn_protocols(ALPN_PROTOCOLS); 285 | 286 | Ok(context) 287 | } 288 | } 289 | 290 | impl<'a, 'ctx> HttpConnect for TlsConnector<'a, 'ctx> { 291 | type Stream = SslStream; 292 | type Err = TlsConnectError; 293 | 294 | fn connect(self) -> Result>, TlsConnectError> { 295 | // First, create a TCP connection to port 443 296 | let raw_tcp = try!(TcpStream::connect(&(self.host, 443))); 297 | // Now build the SSL instance, depending on which SSL context should be 298 | // used... 299 | let ssl = match self.context { 300 | Http2TlsContext::CertPath(path) => { 301 | let ctx = try!(TlsConnector::build_default_context(&path)); 302 | try!(Ssl::new(&ctx)) 303 | }, 304 | Http2TlsContext::Wrapped(ctx) => try!(Ssl::new(ctx)), 305 | }; 306 | // SNI must be used 307 | try!(ssl.set_hostname(self.host)); 308 | 309 | // Wrap the Ssl instance into an `SslStream` 310 | let ssl_stream = try!(SslStream::new_from(ssl, raw_tcp)); 311 | // This connector only understands HTTP/2, so if that wasn't chosen in 312 | // NPN, we raise an error. 313 | let fail = match ssl_stream.get_selected_npn_protocol() { 314 | None => true, 315 | Some(proto) => { 316 | // Make sure that the protocol is one of the HTTP/2 protocols. 317 | debug!("Selected protocol -> {:?}", str::from_utf8(proto)); 318 | let found = ALPN_PROTOCOLS.iter().any(|&http2_proto| http2_proto == proto); 319 | 320 | // We fail if we don't find an HTTP/2 protcol match... 321 | !found 322 | } 323 | }; 324 | if fail { 325 | // We need the fail flag (instead of returning from one of the match 326 | // arms above because we need to move the `ssl_stream` and that is 327 | // not possible above (since it's borrowed at that point). 328 | return Err(TlsConnectError::Http2NotSupported(ssl_stream)); 329 | } 330 | 331 | // Finally, we can wrap it all up into an `HttpConnection` 332 | Ok(HttpConnection::new(ssl_stream, HttpScheme::Https, self.host.into())) 333 | } 334 | } 335 | 336 | /// A struct that establishes an HTTP/2 connection based on a prior-knowledge 337 | /// cleartext TCP connection. It defaults to using port 80 on the given host. 338 | /// 339 | /// More information in the [spec](http://http2.github.io/http2-spec/#known-http) 340 | pub struct CleartextConnector<'a> { 341 | /// The host to which the connection should be established 342 | pub host: &'a str, 343 | } 344 | 345 | /// A newtype wrapping the `io::Error`, as it occurs when attempting to 346 | /// establish an HTTP/2 connection over cleartext TCP (with prior knowledge). 347 | pub struct CleartextConnectError(io::Error); 348 | 349 | /// For convenience we make sure that `io::Error`s are easily convertable to 350 | /// the `CleartextConnectError`, if needed. 351 | impl From for CleartextConnectError { 352 | fn from(e: io::Error) -> CleartextConnectError { CleartextConnectError(e) } 353 | } 354 | 355 | /// The error is marked as an `HttpConnectError` 356 | impl HttpConnectError for CleartextConnectError {} 357 | 358 | impl<'a> HttpConnect for CleartextConnector<'a> { 359 | type Stream = TcpStream; 360 | type Err = CleartextConnectError; 361 | 362 | /// Establishes a cleartext TCP-backed HTTP/2 connection to the host on 363 | /// port 80. 364 | /// If it is not possible, returns an `HttpError`. 365 | fn connect(self) -> Result, CleartextConnectError> { 366 | let stream = try!(TcpStream::connect((self.host, 80))); 367 | let conn = HttpConnection::new(stream, HttpScheme::Http, self.host.into()); 368 | 369 | Ok(conn) 370 | } 371 | } 372 | 373 | /// A struct implementing the client side of an HTTP/2 connection. 374 | /// 375 | /// It builds on top of an `HttpConnection` and provides additional methods 376 | /// that are only used by clients. 377 | pub struct ClientConnection 378 | where TS: TransportStream, S: Session { 379 | /// The underlying `HttpConnection` that will be used for any HTTP/2 380 | /// communication. 381 | conn: HttpConnection, 382 | /// HPACK encoder 383 | encoder: hpack::Encoder<'static>, 384 | /// HPACK decoder 385 | decoder: hpack::Decoder<'static>, 386 | /// The `Session` associated with this connection. It is essentially a set 387 | /// of callbacks that are triggered by the connection when different states 388 | /// in the HTTP/2 communication arise. 389 | pub session: S, 390 | } 391 | 392 | impl ClientConnection where TS: TransportStream, S: Session { 393 | /// Creates a new `ClientConnection` that will use the given `HttpConnection` 394 | /// for all its underlying HTTP/2 communication. 395 | pub fn with_connection(conn: HttpConnection, session: S) 396 | -> ClientConnection { 397 | ClientConnection { 398 | conn: conn, 399 | encoder: hpack::Encoder::new(), 400 | decoder: hpack::Decoder::new(), 401 | session: session, 402 | } 403 | } 404 | 405 | /// Returns the host to which the underlying `HttpConnection` is established. 406 | #[inline] 407 | pub fn host(&self) -> &str { 408 | &self.conn.host 409 | } 410 | 411 | /// Returns the scheme of the underlying `HttpConnection`. 412 | #[inline] 413 | pub fn scheme(&self) -> HttpScheme { 414 | self.conn.scheme 415 | } 416 | 417 | /// Performs the initialization of the `ClientConnection`. 418 | /// 419 | /// Sends the client preface, followed by validating the receipt of the 420 | /// server preface. 421 | pub fn init(&mut self) -> HttpResult<()> { 422 | try!(self.write_preface()); 423 | try!(self.read_preface()); 424 | Ok(()) 425 | } 426 | 427 | /// Writes the client preface to the underlying HTTP/2 connection. 428 | /// 429 | /// According to the HTTP/2 spec, a client preface is first a specific 430 | /// sequence of octets, followed by a settings frame. 431 | /// 432 | /// # Returns 433 | /// Any error raised by the underlying connection is propagated. 434 | fn write_preface(&mut self) -> HttpResult<()> { 435 | // The first part of the client preface is always this sequence of 24 436 | // raw octets. 437 | let preface = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; 438 | try!(self.conn.stream.write(preface)); 439 | 440 | // It is followed by the client's settings. 441 | let settings = { 442 | let mut frame = SettingsFrame::new(); 443 | frame.add_setting(HttpSetting::EnablePush(0)); 444 | frame 445 | }; 446 | try!(self.conn.send_frame(settings)); 447 | debug!("Sent client preface"); 448 | 449 | Ok(()) 450 | } 451 | 452 | /// Reads and handles the server preface from the underlying HTTP/2 453 | /// connection. 454 | /// 455 | /// According to the HTTP/2 spec, a server preface consists of a single 456 | /// settings frame. 457 | /// 458 | /// # Returns 459 | /// 460 | /// Any error raised by the underlying connection is propagated. 461 | /// 462 | /// Additionally, if it is not possible to decode the server preface, 463 | /// it returns the `HttpError::UnableToConnect` variant. 464 | fn read_preface(&mut self) -> HttpResult<()> { 465 | match self.conn.recv_frame() { 466 | Ok(HttpFrame::SettingsFrame(settings)) => { 467 | debug!("Correctly received a SETTINGS frame from the server"); 468 | try!(self.handle_settings_frame(settings)); 469 | }, 470 | // Wrong frame received... 471 | Ok(_) => return Err(HttpError::UnableToConnect), 472 | // Already an error -- propagate that. 473 | Err(e) => return Err(e), 474 | } 475 | Ok(()) 476 | } 477 | 478 | /// A method that sends the given `Request` to the server. 479 | /// 480 | /// The method blocks until the entire request has been sent. 481 | /// 482 | /// All errors are propagated. 483 | /// 484 | /// # Note 485 | /// 486 | /// Request body is ignored for now. 487 | pub fn send_request(&mut self, req: Request) -> HttpResult<()> { 488 | let headers_fragment = self.encoder.encode(&req.headers); 489 | // For now, sending header fragments larger than 16kB is not supported 490 | // (i.e. the encoded representation cannot be split into CONTINUATION 491 | // frames). 492 | let mut frame = HeadersFrame::new(headers_fragment, req.stream_id); 493 | frame.set_flag(HeadersFlag::EndHeaders); 494 | // Since we are not supporting methods which require request bodies to 495 | // be sent, we end the stream from this side already. 496 | // TODO: Support bodies! 497 | frame.set_flag(HeadersFlag::EndStream); 498 | 499 | // Sending this HEADER frame opens the new stream and is equivalent to 500 | // sending the given request to the server. 501 | try!(self.conn.send_frame(frame)); 502 | 503 | Ok(()) 504 | } 505 | 506 | /// Fully handle the next incoming frame, blocking to read it from the 507 | /// underlying transport stream if not available yet. 508 | /// 509 | /// All communication errors are propagated. 510 | pub fn handle_next_frame(&mut self) -> HttpResult<()> { 511 | debug!("Waiting for frame..."); 512 | let frame = match self.conn.recv_frame() { 513 | Ok(frame) => frame, 514 | Err(HttpError::UnknownFrameType) => { 515 | debug!("Ignoring unknown frame type"); 516 | return Ok(()) 517 | }, 518 | Err(e) => { 519 | debug!("Encountered an HTTP/2 error, stopping."); 520 | return Err(e); 521 | }, 522 | }; 523 | 524 | self.handle_frame(frame) 525 | } 526 | 527 | /// Private helper method that actually handles a received frame. 528 | fn handle_frame(&mut self, frame: HttpFrame) -> HttpResult<()> { 529 | match frame { 530 | HttpFrame::DataFrame(frame) => { 531 | debug!("Data frame received"); 532 | self.handle_data_frame(frame) 533 | }, 534 | HttpFrame::HeadersFrame(frame) => { 535 | debug!("Headers frame received"); 536 | self.handle_headers_frame(frame) 537 | }, 538 | HttpFrame::SettingsFrame(frame) => { 539 | debug!("Settings frame received"); 540 | self.handle_settings_frame(frame) 541 | } 542 | } 543 | } 544 | 545 | /// Private helper method that handles a received `DataFrame`. 546 | fn handle_data_frame(&mut self, frame: DataFrame) -> HttpResult<()> { 547 | self.session.new_data_chunk(frame.get_stream_id(), &frame.data); 548 | 549 | if frame.is_set(DataFlag::EndStream) { 550 | debug!("End of stream {}", frame.get_stream_id()); 551 | self.session.end_of_stream(frame.get_stream_id()) 552 | } 553 | 554 | Ok(()) 555 | } 556 | 557 | /// Private helper method that handles a received `HeadersFrame`. 558 | fn handle_headers_frame(&mut self, frame: HeadersFrame) -> HttpResult<()> { 559 | let headers = try!(self.decoder.decode(&frame.header_fragment) 560 | .map_err(|e| HttpError::CompressionError(e))); 561 | self.session.new_headers(frame.get_stream_id(), headers); 562 | 563 | if frame.is_end_of_stream() { 564 | debug!("End of stream {}", frame.get_stream_id()); 565 | self.session.end_of_stream(frame.get_stream_id()); 566 | } 567 | 568 | Ok(()) 569 | } 570 | 571 | /// Private helper method that handles a received `SettingsFrame`. 572 | fn handle_settings_frame(&mut self, frame: SettingsFrame) -> HttpResult<()> { 573 | if !frame.is_ack() { 574 | // TODO: Actually handle the settings change before 575 | // sending out the ACK. 576 | debug!("Sending a SETTINGS ack"); 577 | try!(self.conn.send_frame(SettingsFrame::new_ack())); 578 | } 579 | 580 | Ok(()) 581 | } 582 | } 583 | 584 | #[cfg(test)] 585 | mod tests { 586 | use std::io::{Cursor, Read, Write}; 587 | use std::io; 588 | 589 | use super::super::frame::{ 590 | Frame, DataFrame, HeadersFrame, 591 | SettingsFrame, 592 | pack_header, 593 | RawFrame, 594 | }; 595 | use super::{HttpConnection, HttpFrame, ClientConnection}; 596 | use super::super::transport::TransportStream; 597 | use super::super::{HttpError, HttpScheme, Request, StreamId, Header}; 598 | use super::super::session::Session; 599 | use hpack; 600 | 601 | /// A helper stub implementation of a `TransportStream`. 602 | /// 603 | /// When read from this stream, it spits out bytes from a predefined `Vec` 604 | /// in the original given order. Once those are exhausted, it returns an EOF. 605 | /// 606 | /// When writng to the stream, the bytes are aggregated to internal buffer. 607 | /// The contents of the buffer can be accessed using the `get_writted` 608 | /// method. 609 | /// 610 | /// It is possible to "close" the stream (both ends at once) so that 611 | /// aftwerwards any read or write attempt returns an `io::Error`; 612 | struct StubTransportStream { 613 | reader: Cursor>, 614 | writer: Cursor>, 615 | closed: bool, 616 | } 617 | 618 | impl StubTransportStream { 619 | /// Initializes the stream with the given byte vector representing 620 | /// the bytes that will be read from the stream. 621 | fn with_stub_content(stub: &Vec) -> StubTransportStream { 622 | StubTransportStream { 623 | reader: Cursor::new(stub.clone()), 624 | writer: Cursor::new(Vec::new()), 625 | closed: false, 626 | } 627 | } 628 | 629 | /// Returns a slice representing the bytes already written to the 630 | /// stream. 631 | fn get_written(&self) -> &[u8] { 632 | self.writer.get_ref() 633 | } 634 | 635 | /// Returns the position up to which the stream has been read. 636 | fn get_read_pos(&self) -> u64 { 637 | self.reader.position() 638 | } 639 | 640 | /// Closes the stream, making any read or write operation return an 641 | /// `io::Error` from there on out. 642 | fn close(&mut self) { 643 | self.closed = true; 644 | } 645 | } 646 | 647 | impl io::Read for StubTransportStream { 648 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 649 | if self.closed { 650 | Err(io::Error::new(io::ErrorKind::Other, "Closed")) 651 | } else { 652 | self.reader.read(buf) 653 | } 654 | } 655 | } 656 | 657 | impl io::Write for StubTransportStream { 658 | fn write(&mut self, buf: &[u8]) -> io::Result { 659 | if self.closed { 660 | Err(io::Error::new(io::ErrorKind::Other, "Closed")) 661 | } else { 662 | self.writer.write(buf) 663 | } 664 | } 665 | 666 | fn flush(&mut self) -> io::Result<()> { 667 | self.writer.flush() 668 | } 669 | } 670 | 671 | impl TransportStream for StubTransportStream {} 672 | 673 | /// A helper struct implementing the `Session` trait, intended for testing 674 | /// purposes. 675 | /// 676 | /// It is basically a poor man's mock, providing us the ability to check 677 | /// how many times the particular callbacks were called (although not the 678 | /// order in which they were called). 679 | /// 680 | /// Additionally, when created with `new_verify` it is possible to provide 681 | /// a list of headers and data chunks and make the session verify that it 682 | /// them from the connection in the exactly given order (i.e. relative 683 | /// order of chunks and headers; there is no way to check the order between 684 | /// chunks and headers for now). 685 | struct TestSession { 686 | silent: bool, 687 | /// List of expected headers -- in the order that they are expected 688 | headers: Vec>, 689 | /// List of expected data chunks -- in the order that they are expected 690 | chunks: Vec>, 691 | /// The current number of header calls. 692 | curr_header: usize, 693 | /// The current number of data chunk calls. 694 | curr_chunk: usize, 695 | } 696 | 697 | impl TestSession { 698 | /// Returns a new `TestSession` that only counts how many times the 699 | /// callback methods were invoked. 700 | fn new() -> TestSession { 701 | TestSession { 702 | silent: true, 703 | headers: Vec::new(), 704 | chunks: Vec::new(), 705 | curr_header: 0, 706 | curr_chunk: 0, 707 | } 708 | } 709 | 710 | /// Returns a new `TestSession` that veriies that the headers received 711 | /// in the callbacks are equal to those in the given headers `Vec` and 712 | /// that they come in exactly the given order. Does the same for chunks. 713 | fn new_verify(headers: Vec>, chunks: Vec>) 714 | -> TestSession { 715 | TestSession { 716 | silent: false, 717 | headers: headers, 718 | chunks: chunks, 719 | curr_header: 0, 720 | curr_chunk: 0, 721 | } 722 | } 723 | } 724 | 725 | impl Session for TestSession { 726 | fn new_data_chunk(&mut self, _: StreamId, data: &[u8]) { 727 | if !self.silent { 728 | assert_eq!(&self.chunks[self.curr_chunk], &data); 729 | } 730 | self.curr_chunk += 1; 731 | } 732 | 733 | fn new_headers(&mut self, _: StreamId, headers: Vec
) { 734 | if !self.silent { 735 | assert_eq!(self.headers[self.curr_header], headers); 736 | } 737 | self.curr_header += 1; 738 | } 739 | 740 | fn end_of_stream(&mut self, _: StreamId) {} 741 | } 742 | 743 | /// A test that makes sure that the `StubTransportStream` exhibits 744 | /// properties that a "real" `TransportStream` would too. 745 | #[test] 746 | fn sanity_check_stub_stream() { 747 | // `read` returns 0 at the "end of file"? 748 | { 749 | let mut stream = StubTransportStream::with_stub_content(&vec![]); 750 | let mut buf = [0u8; 5]; 751 | assert_eq!(stream.read(&mut buf).unwrap(), 0); 752 | assert_eq!(stream.read(&mut buf).unwrap(), 0); 753 | } 754 | // A closed stream always returns an io::Error 755 | { 756 | let mut stream = StubTransportStream::with_stub_content(&vec![]); 757 | stream.close(); 758 | assert!(stream.write(&[1]).is_err()); 759 | assert!(stream.read(&mut [0; 5]).is_err()); 760 | } 761 | } 762 | 763 | /// A helper function that creates an `HttpConnection` with a `StubTransportStream` 764 | /// where the content of the stream is defined by the given `stub_data` 765 | fn build_http_conn(stub_data: &Vec) -> HttpConnection { 766 | HttpConnection { 767 | stream: StubTransportStream::with_stub_content(stub_data), 768 | scheme: HttpScheme::Http, 769 | host: "".to_string(), 770 | } 771 | } 772 | 773 | /// A helper function that builds a buffer of bytes from the given `Vec` of 774 | /// `HttpFrame`s, by serializing them in the given order. 775 | fn build_stub_from_frames(frames: &Vec) -> Vec { 776 | let mut buf: Vec = Vec::new(); 777 | for frame in frames.iter() { 778 | let serialized = match frame { 779 | &HttpFrame::DataFrame(ref frame) => frame.serialize(), 780 | &HttpFrame::HeadersFrame(ref frame) => frame.serialize(), 781 | &HttpFrame::SettingsFrame(ref frame) => frame.serialize(), 782 | }; 783 | buf.extend(serialized.into_iter()); 784 | } 785 | 786 | buf 787 | } 788 | 789 | /// Tests that it is possible to read a single frame from the stream. 790 | #[test] 791 | fn test_read_single_frame() { 792 | let frames: Vec = vec![ 793 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 794 | ]; 795 | let mut conn = build_http_conn(&build_stub_from_frames(&frames)); 796 | 797 | let actual: Vec<_> = (0..frames.len()).map(|_| conn.recv_frame().ok().unwrap()) 798 | .collect(); 799 | 800 | assert_eq!(actual, frames); 801 | } 802 | 803 | /// Tests that multiple frames are correctly read from the stream. 804 | #[test] 805 | fn test_read_multiple_frames() { 806 | let frames: Vec = vec![ 807 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 808 | HttpFrame::DataFrame(DataFrame::new(1)), 809 | HttpFrame::DataFrame(DataFrame::new(3)), 810 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 3)), 811 | ]; 812 | let mut conn = build_http_conn(&build_stub_from_frames(&frames)); 813 | 814 | let actual: Vec<_> = (0..frames.len()).map(|_| conn.recv_frame().ok().unwrap()) 815 | .collect(); 816 | 817 | assert_eq!(actual, frames); 818 | } 819 | 820 | /// Tests that when reading from a stream that initially contains no data, 821 | /// an `IoError` is returned. 822 | #[test] 823 | fn test_read_no_data() { 824 | let mut conn = build_http_conn(&vec![]); 825 | 826 | let res = conn.recv_frame(); 827 | 828 | assert!(match res.err().unwrap() { 829 | HttpError::IoError(_) => true, 830 | _ => false, 831 | }); 832 | } 833 | 834 | /// Tests that a read past the end of file (stream) results in an `IoError`. 835 | #[test] 836 | fn test_read_past_eof() { 837 | let frames: Vec = vec![ 838 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 839 | ]; 840 | let mut conn = build_http_conn(&build_stub_from_frames(&frames)); 841 | 842 | let _: Vec<_> = (0..frames.len()).map(|_| conn.recv_frame().ok().unwrap()) 843 | .collect(); 844 | let res = conn.recv_frame(); 845 | 846 | assert!(match res.err().unwrap() { 847 | HttpError::IoError(_) => true, 848 | _ => false, 849 | }); 850 | } 851 | 852 | /// Tests that when reading off a stream that doesn't have a complete frame 853 | /// header causes a graceful failure. 854 | #[test] 855 | fn test_read_invalid_stream_incomplete_frame() { 856 | let frames: Vec = vec![ 857 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 858 | ]; 859 | let mut conn = build_http_conn(&{ 860 | let mut buf: Vec = Vec::new(); 861 | buf.extend(build_stub_from_frames(&frames).into_iter()); 862 | // We add an extra trailing byte (a start of the header of another 863 | // frame). 864 | buf.push(0); 865 | buf 866 | }); 867 | 868 | let actual: Vec<_> = (0..frames.len()).map(|_| conn.recv_frame().ok().unwrap()) 869 | .collect(); 870 | // The first frame is correctly read 871 | assert_eq!(actual, frames); 872 | // ...but now we get an error 873 | assert!(match conn.recv_frame().err().unwrap() { 874 | HttpError::IoError(_) => true, 875 | _ => false, 876 | }); 877 | } 878 | 879 | /// Tests that when reading off a stream that doesn't have a frame payload 880 | /// (when it should) causes a graceful failure. 881 | #[test] 882 | fn test_read_invalid_stream_incomplete_payload() { 883 | let frames: Vec = vec![ 884 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 885 | ]; 886 | let mut conn = build_http_conn(&{ 887 | let mut buf: Vec = Vec::new(); 888 | buf.extend(build_stub_from_frames(&frames).into_iter()); 889 | // We add a header indicating that there should be 1 byte of payload 890 | let header = (1u32, 0u8, 0u8, 1u32); 891 | buf.extend(pack_header(&header).to_vec().into_iter()); 892 | // ...but we don't add any payload! 893 | buf 894 | }); 895 | 896 | let actual: Vec<_> = (0..frames.len()).map(|_| conn.recv_frame().ok().unwrap()) 897 | .collect(); 898 | // The first frame is correctly read 899 | assert_eq!(actual, frames); 900 | // ...but now we get an error 901 | assert!(match conn.recv_frame().err().unwrap() { 902 | HttpError::IoError(_) => true, 903 | _ => false, 904 | }); 905 | } 906 | 907 | /// Tests that when reading off a stream that contains an invalid frame 908 | /// returns an appropriate indicator. 909 | #[test] 910 | fn test_read_invalid_frame() { 911 | // A DATA header which is attached to stream 0 912 | let frames: Vec = vec![ 913 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 0)), 914 | ]; 915 | let mut conn = build_http_conn(&build_stub_from_frames(&frames)); 916 | 917 | // An error indicating that the frame is invalid. 918 | assert!(match conn.recv_frame().err().unwrap() { 919 | HttpError::InvalidFrame => true, 920 | _ => false, 921 | }); 922 | } 923 | 924 | /// Tests that when reading a frame with a header that indicates an 925 | /// unknown frame type, an appropriate error is returned. 926 | #[test] 927 | fn test_read_unknown_frame() { 928 | let mut conn = build_http_conn(&{ 929 | let mut buf: Vec = Vec::new(); 930 | // Frame type 10 with a payload of length 1 on stream 1 931 | let header = (1u32, 10u8, 0u8, 1u32); 932 | buf.extend(pack_header(&header).to_vec().into_iter()); 933 | buf.push(1); 934 | buf 935 | }); 936 | 937 | // Unknown frame error. 938 | assert!(match conn.recv_frame().err().unwrap() { 939 | HttpError::UnknownFrameType => true, 940 | _ => false, 941 | }); 942 | } 943 | 944 | /// Tests that it is possible to write a single frame to the connection. 945 | #[test] 946 | fn test_write_single_frame() { 947 | let frames: Vec = vec![ 948 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 949 | ]; 950 | let expected = build_stub_from_frames(&frames); 951 | let mut conn = build_http_conn(&vec![]); 952 | 953 | for frame in frames.into_iter() { 954 | let _ = match frame { 955 | HttpFrame::DataFrame(frame) => conn.send_frame(frame), 956 | HttpFrame::SettingsFrame(frame) => conn.send_frame(frame), 957 | HttpFrame::HeadersFrame(frame) => conn.send_frame(frame), 958 | }; 959 | } 960 | 961 | assert_eq!(expected, conn.stream.get_written()); 962 | } 963 | 964 | 965 | /// Tests that multiple frames are correctly written to the stream. 966 | #[test] 967 | fn test_write_multiple_frames() { 968 | let frames: Vec = vec![ 969 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 970 | HttpFrame::DataFrame(DataFrame::new(1)), 971 | HttpFrame::DataFrame(DataFrame::new(3)), 972 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 3)), 973 | ]; 974 | let expected = build_stub_from_frames(&frames); 975 | let mut conn = build_http_conn(&vec![]); 976 | 977 | for frame in frames.into_iter() { 978 | let _ = match frame { 979 | HttpFrame::DataFrame(frame) => conn.send_frame(frame), 980 | HttpFrame::SettingsFrame(frame) => conn.send_frame(frame), 981 | HttpFrame::HeadersFrame(frame) => conn.send_frame(frame), 982 | }; 983 | } 984 | 985 | assert_eq!(expected, conn.stream.get_written()); 986 | } 987 | 988 | /// Tests that a write to a closed stream fails with an IoError. 989 | #[test] 990 | fn test_write_to_closed_stream() { 991 | let frames: Vec = vec![ 992 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 993 | ]; 994 | let mut conn = build_http_conn(&vec![]); 995 | // Close the underlying stream! 996 | conn.stream.close(); 997 | 998 | for frame in frames.into_iter() { 999 | let res = match frame { 1000 | HttpFrame::DataFrame(frame) => conn.send_frame(frame), 1001 | HttpFrame::SettingsFrame(frame) => conn.send_frame(frame), 1002 | HttpFrame::HeadersFrame(frame) => conn.send_frame(frame), 1003 | }; 1004 | 1005 | assert!(match res { 1006 | Err(HttpError::IoError(_)) => true, 1007 | _ => false, 1008 | }); 1009 | } 1010 | } 1011 | 1012 | /// A helper function that parses out the first frame contained in the 1013 | /// given buffer, expecting it to be the frame type of the generic parameter 1014 | /// `F`. Returns the size of the raw frame read and the frame itself. 1015 | /// 1016 | /// Panics if unable to obtain such a frame. 1017 | fn get_frame_from_buf(buf: &[u8]) -> (F, usize) { 1018 | let raw = RawFrame::from_buf(buf).unwrap(); 1019 | let len = raw.header.0 as usize; 1020 | let frame = Frame::from_raw(raw).unwrap(); 1021 | 1022 | (frame, len + 9) 1023 | } 1024 | 1025 | /// Tests that a client connection is correctly initialized, by writing the 1026 | /// client preface and reading the server preface. 1027 | #[test] 1028 | fn test_init_client_conn() { 1029 | let frames = vec![HttpFrame::SettingsFrame(SettingsFrame::new())]; 1030 | let server_frame_buf = build_stub_from_frames(&frames); 1031 | let mut conn = ClientConnection::with_connection( 1032 | build_http_conn(&server_frame_buf), 1033 | TestSession::new()); 1034 | 1035 | conn.init().ok().unwrap(); 1036 | let written = conn.conn.stream.get_written(); 1037 | 1038 | // The first bytes written to the underlying transport layer are the 1039 | // preface bytes. 1040 | let preface = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; 1041 | let frames_buf = &written[preface.len()..]; 1042 | // Immediately after that we sent a settings frame... 1043 | assert_eq!(preface, &written[..preface.len()]); 1044 | let len_settings = { 1045 | let (frame, sz): (SettingsFrame, _) = get_frame_from_buf(frames_buf); 1046 | // ...which was not an ack, but our own settings. 1047 | assert!(!frame.is_ack()); 1048 | sz 1049 | }; 1050 | // Then, we have read the server's response 1051 | assert_eq!( 1052 | server_frame_buf.len() as u64, 1053 | conn.conn.stream.get_read_pos()); 1054 | // Finally, we also expect that the client has already sent a response 1055 | // (an ack) to this server settings frame. 1056 | let len_ack = { 1057 | let (settings_frame, sz): (SettingsFrame, _) = 1058 | get_frame_from_buf(&frames_buf[len_settings..]); 1059 | assert!(settings_frame.is_ack()); 1060 | sz 1061 | }; 1062 | // ...and we have not written anything else! 1063 | assert_eq!(len_settings + len_ack, frames_buf.len()); 1064 | } 1065 | 1066 | /// Tests that a client connection fails to initialize when the server does 1067 | /// not send a settings frame as its first frame (i.e. server preface). 1068 | #[test] 1069 | fn test_init_client_conn_no_settings() { 1070 | let frames = vec![HttpFrame::DataFrame(DataFrame::new(1))]; 1071 | let server_frame_buf = build_stub_from_frames(&frames); 1072 | let mut conn = ClientConnection::with_connection( 1073 | build_http_conn(&server_frame_buf), 1074 | TestSession::new()); 1075 | 1076 | // We get an error since the first frame sent by the server was not 1077 | // SETTINGS. 1078 | assert!(conn.init().is_err()); 1079 | } 1080 | 1081 | /// Tests that a `ClientConnection` correctly sends a `Request` with no 1082 | /// body. 1083 | #[test] 1084 | fn test_client_conn_send_request_no_body() { 1085 | let req = Request { 1086 | stream_id: 1, 1087 | // An incomplete header list, but this does not matter for this test. 1088 | headers: vec![ 1089 | (b":method".to_vec(), b"GET".to_vec()), 1090 | (b":path".to_vec(), b"/".to_vec()), 1091 | ], 1092 | body: Vec::new(), 1093 | }; 1094 | let mut conn = ClientConnection::with_connection( 1095 | build_http_conn(&vec![]), TestSession::new()); 1096 | 1097 | conn.send_request(req).unwrap(); 1098 | let written = conn.conn.stream.get_written(); 1099 | 1100 | let (frame, sz): (HeadersFrame, _) = get_frame_from_buf(written); 1101 | // We sent a headers frame with end of headers and end of stream flags 1102 | assert!(frame.is_headers_end()); 1103 | assert!(frame.is_end_of_stream()); 1104 | // ...and nothing else! 1105 | assert_eq!(sz, written.len()); 1106 | } 1107 | 1108 | /// Tests that the `ClientConnection` correctly notifies the session on a 1109 | /// new data chunk. 1110 | #[test] 1111 | fn test_client_conn_notifies_session_header() { 1112 | let frames: Vec = vec![ 1113 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 1)), 1114 | ]; 1115 | let mut conn = ClientConnection::with_connection( 1116 | build_http_conn(&build_stub_from_frames(&frames)), 1117 | TestSession::new()); 1118 | 1119 | conn.handle_next_frame().ok().unwrap(); 1120 | 1121 | // A poor man's mock... 1122 | // The header callback was called 1123 | assert_eq!(conn.session.curr_header, 1); 1124 | // ...no chunks were seen. 1125 | assert_eq!(conn.session.curr_chunk, 0); 1126 | } 1127 | 1128 | /// Tests that the `ClientConnection` correctly notifies the session on 1129 | /// a new data chunk. 1130 | #[test] 1131 | fn test_client_conn_notifies_session_data() { 1132 | let frames: Vec = vec![ 1133 | HttpFrame::DataFrame(DataFrame::new(1)), 1134 | ]; 1135 | let mut conn = ClientConnection::with_connection( 1136 | build_http_conn(&build_stub_from_frames(&frames)), 1137 | TestSession::new()); 1138 | 1139 | conn.handle_next_frame().ok().unwrap(); 1140 | 1141 | // A poor man's mock... 1142 | // The header callback was not called 1143 | assert_eq!(conn.session.curr_header, 0); 1144 | // and exactly one chunk seen. 1145 | assert_eq!(conn.session.curr_chunk, 1); 1146 | } 1147 | 1148 | /// Tests that there is no notification for an invalid headers frame. 1149 | #[test] 1150 | fn test_client_conn_invalid_frame_no_notification() { 1151 | let frames: Vec = vec![ 1152 | // Associated to stream 0! 1153 | HttpFrame::HeadersFrame(HeadersFrame::new(vec![], 0)), 1154 | ]; 1155 | let mut conn = ClientConnection::with_connection( 1156 | build_http_conn(&build_stub_from_frames(&frames)), 1157 | TestSession::new()); 1158 | 1159 | // We get an invalid frame error back... 1160 | assert_eq!( 1161 | conn.handle_next_frame().err().unwrap(), 1162 | HttpError::InvalidFrame); 1163 | 1164 | // A poor man's mock... 1165 | // No callbacks triggered 1166 | assert_eq!(conn.session.curr_header, 0); 1167 | assert_eq!(conn.session.curr_chunk, 0); 1168 | } 1169 | 1170 | /// Tests that the session gets the correct values for the headers and data 1171 | /// from the `ClientConnection`. 1172 | #[test] 1173 | fn test_client_conn_session_gets_headers_data_values() { 1174 | let headers = vec![(b":method".to_vec(), b"GET".to_vec())]; 1175 | let frames: Vec = vec![ 1176 | HttpFrame::HeadersFrame(HeadersFrame::new( 1177 | hpack::Encoder::new().encode(&headers), 1178 | 1)), 1179 | HttpFrame::DataFrame(DataFrame::new(1)), { 1180 | let mut frame = DataFrame::new(1); 1181 | frame.data = b"1234".to_vec(); 1182 | HttpFrame::DataFrame(frame) 1183 | }, 1184 | ]; 1185 | let mut conn = ClientConnection::with_connection( 1186 | build_http_conn(&build_stub_from_frames(&frames)), 1187 | TestSession::new_verify( 1188 | vec![headers], 1189 | vec![b"".to_vec(), b"1234".to_vec()])); 1190 | 1191 | conn.handle_next_frame().ok().unwrap(); 1192 | conn.handle_next_frame().ok().unwrap(); 1193 | conn.handle_next_frame().ok().unwrap(); 1194 | 1195 | // Two chunks and one header processed? 1196 | assert_eq!(conn.session.curr_chunk, 2); 1197 | assert_eq!(conn.session.curr_header, 1); 1198 | } 1199 | } 1200 | --------------------------------------------------------------------------------