├── .idea
├── .name
├── vcs.xml
├── modules.xml
├── solicit.iml
└── misc.xml
├── .gitignore
├── src
├── lib.rs
├── client
│ ├── mod.rs
│ ├── simple.rs
│ └── async.rs
└── http
│ ├── frame
│ ├── test.rs
│ ├── mod.rs
│ ├── continuationframe.rs
│ ├── pingframe.rs
│ ├── priorityframe.rs
│ ├── frames.rs
│ ├── dataframe.rs
│ ├── settingsframe.rs
│ └── headersframe.rs
│ ├── transport.rs
│ ├── mod.rs
│ ├── session.rs
│ └── connection.rs
├── Cargo.toml
├── .travis.yml
├── LICENSE
├── CONTRIBUTING.md
└── README.md
/.idea/.name:
--------------------------------------------------------------------------------
1 | solicit
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /Cargo.lock
3 | /.idea/workspace.xml
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.idea/solicit.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.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 use self::priorityframe::{
61 | PriorityFrame,
62 | };
63 |
64 | pub mod frames;
65 | mod test;
66 | pub mod dataframe;
67 | pub mod settingsframe;
68 | pub mod headersframe;
69 | pub mod pingframe;
70 | pub mod continuationframe;
71 | pub mod priorityframe;
72 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Solicit
2 |
3 | You want to contribute? You're awesome! When submitting a Pull Request, please have your commits follow these guidelines:
4 |
5 | ## Git Commit Guidelines
6 |
7 | These guidelines have been copied from the [AngularJS](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines)
8 | project.
9 |
10 | We have very precise rules over how our git commit messages can be formatted. This leads to **more
11 | readable messages** that are easy to follow when looking through the **project history**. But also,
12 | we use the git commit messages to **generate the change log**.
13 |
14 | ### Commit Message Format
15 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
16 | format that includes a **type**, a **scope** and a **subject**:
17 |
18 | ``
19 | ():
20 |
21 |
22 |
23 |
24 | ``
25 |
26 | Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
27 | to read on github as well as in various git tools.
28 |
29 | ### Type
30 | Must be one of the following:
31 |
32 | * **feat**: A new feature
33 | * **fix**: A bug fix
34 | * **docs**: Documentation only changes
35 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
36 | semi-colons, etc)
37 | * **refactor**: A code change that neither fixes a bug or adds a feature
38 | * **perf**: A code change that improves performance
39 | * **test**: Adding missing tests
40 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
41 | generation
42 |
43 | ### Scope
44 | The scope should refer to a module in solicit that is being touched. Examples:
45 |
46 | * http
47 | * client
48 | * frame
49 | * flow
50 |
51 | ### Subject
52 | The subject contains succinct description of the change:
53 |
54 | * use the imperative, present tense: "change" not "changed" nor "changes"
55 | * don't capitalize first letter
56 | * no dot (.) at the end
57 |
58 | ###Body
59 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
60 | The body should include the motivation for the change and contrast this with previous behavior.
61 |
62 | ###Footer
63 | The footer should contain any information about **Breaking Changes** and is also the place to
64 | reference GitHub issues that this commit **Closes**.
65 |
66 | The last line of commits introducing breaking changes should be in the form `BREAKING CHANGE: `
67 |
68 |
69 | A detailed explanation can be found in this [document][commit-message-format].
70 |
71 | [commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
72 |
--------------------------------------------------------------------------------
/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 PING 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 `PingFrame`, 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 | [](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/priorityframe.rs:
--------------------------------------------------------------------------------
1 | use super::super::StreamId;
2 | use super::frames::{
3 | Frame,
4 | Flag,
5 | pack_header,
6 | RawFrame,
7 | FrameHeader
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 NoFlag {
20 | None = 0x0,
21 | }
22 |
23 | impl Flag for NoFlag {
24 | #[inline]
25 | fn bitmask(&self) -> u8 {
26 | *self as u8
27 | }
28 | }
29 |
30 | /// The struct represents the dependency information that can be attached to a stream
31 | /// and sent within HEADERS frame
32 | #[derive(PartialEq)]
33 | #[derive(Debug)]
34 | #[derive(Clone)]
35 | pub struct StreamDependency {
36 | /// The ID of the stream that a particular stream depends on
37 | pub stream_id: StreamId,
38 | /// The weight for the stream. The value exposed (and set) here is always
39 | /// in the range [0, 255], instead of [1, 256] so that the value fits
40 | /// into a `u8`.
41 | pub weight: u8,
42 | /// A flag indicating whether the stream dependency is exclusive.
43 | pub is_exclusive: bool,
44 | }
45 |
46 | impl StreamDependency {
47 | /// Creates a new `StreamDependency` with the given stream Id, weight, and
48 | /// exclusivity.
49 | pub fn new(stream_id: StreamId, weight: u8, is_exclusive: bool)
50 | -> StreamDependency {
51 | StreamDependency {
52 | stream_id: stream_id,
53 | weight: weight,
54 | is_exclusive: is_exclusive,
55 | }
56 | }
57 |
58 | /// Parses the 5-byte length frame
59 | ///
60 | /// # Panics
61 | ///
62 | /// If the frame is less than 5 bytes, the method will panic
63 | pub fn parse(buf: &[u8]) -> StreamDependency {
64 | // The most significant bit of the first byte is the "E" bit indicating
65 | // whether the dependency is exclusive.
66 | let is_exclusive = buf[0] & 0x80 != 0;
67 | let stream_id = {
68 | // Parse the first 4 bytes into a u32
69 | let mut id = unpack_octets_4!(buf, 0, u32);
70 | // and clear the first bit since the stream id is only 31 bits.
71 | id &= !(1 << 31);
72 | id
73 | };
74 |
75 | StreamDependency {
76 | stream_id: stream_id,
77 | weight: buf[4],
78 | is_exclusive: is_exclusive,
79 | }
80 | }
81 |
82 | /// Serializes the `StreamDependency` into a 5-byte buffer representing the
83 | /// dependency description.
84 | pub fn serialize(&self) -> [u8; 5] {
85 | let e_bit = if self.is_exclusive {
86 | 1 << 7
87 | } else {
88 | 0
89 | };
90 | [
91 | (((self.stream_id >> 24) & 0x000000FF) as u8) | e_bit,
92 | (((self.stream_id >> 16) & 0x000000FF) as u8),
93 | (((self.stream_id >> 8) & 0x000000FF) as u8),
94 | (((self.stream_id >> 0) & 0x000000FF) as u8),
95 | self.weight,
96 | ]
97 | }
98 | }
99 |
100 | /// A struct representing the PRIORITY frmas of HTTP/2
101 | #[derive(PartialEq)]
102 | #[derive(Debug)]
103 | pub struct PriorityFrame {
104 | /// The id of the stream with which this frame is associated
105 | pub stream_id: StreamId,
106 | /// The stream dependency information
107 | pub stream_dep: StreamDependency,
108 | /// `Priority` frame does not define any flags
109 | flags: u8,
110 | /// The data in frame
111 | pub data: Vec,
112 | }
113 |
114 | impl PriorityFrame {
115 | pub fn new(stream_id: StreamId, stream_dep: StreamDependency)
116 | -> PriorityFrame {
117 | PriorityFrame {
118 | stream_id: stream_id,
119 | stream_dep: stream_dep,
120 | flags: 0,
121 | }
122 | }
123 |
124 | /// Returns the length of the payload of the current frame
125 | /// Priority frame must be 5 octets
126 | fn payload_len(&self) -> u32 {
127 | 5
128 | }
129 | }
130 |
131 | impl Frame for PriorityFrame {
132 | /// `Priority` frame does not take a flag
133 | type FlagType = NoFlag;
134 |
135 | /// Creates a new `PriorityFrame` with the given `RawFrame` (i.e. header and
136 | /// payload), if possible.
137 | ///
138 | /// # Returns
139 | ///
140 | /// `None` if a valid `PriorityFrame` cannot be constructed from the given
141 | /// `RawFrame`. The stream ID *MUST NOT* be 0.
142 | ///
143 | /// Otherwise, returns a newly contructed `PriorityFrame`
144 | fn from_raw(raw_frame: RawFrame) -> Option {
145 | // Unpack the header
146 | let (len, frame_type, flags, stream_id) = raw_frame.header;
147 | // Check that the frame type is correct for this frame implementation
148 | if frame_type != 0x2 {
149 | return None;
150 | }
151 | // Check that the length given in the header matches the payload
152 | // if not, soemthing went wrong and we do not consider this as
153 | // a valid frame.
154 | if (len as usize) != raw_frame.payload.len() {
155 | return None;
156 | }
157 | // Check that the length of the payload is 5 bytes
158 | // If not, this is not a valid frame
159 | if raw_frame.payload.len() != 5 {
160 | return None;
161 | }
162 | // Check that the PRIORITY frame is not associated to stream 0
163 | // If it is, this is not a valid frame
164 | if stream_id == 0 {
165 | return None;
166 | }
167 | // Extract the stream dependecy info from the payload
168 | let stream_dep = StreamDependency::parse(&raw_frame.payload);
169 |
170 | Some(PriorityFrame {
171 | stream_id: stream_id,
172 | stream_dep: stream_dep,
173 | flags: flags,
174 | })
175 | }
176 |
177 | /// `Priority` frame does not set any flags
178 | fn is_set(&self, flag: NoFlag) -> bool {
179 | true
180 | }
181 |
182 | /// Returns the `StreamId` of the stream to which the frame is associated
183 | ///
184 | fn get_stream_id(&self) -> StreamId {
185 | self.stream_id
186 | }
187 |
188 | /// Returns a `FrameHeader` based on the current state of the `Frame`.
189 | fn get_header(&self) -> FrameHeader {
190 | (self.payload_len(), 0x2, 0, self.stream_id)
191 | }
192 |
193 | /// `PriorityFrame` does not set any flags
194 | fn set_flag(&mut self, flag: NoFlag) {
195 | }
196 |
197 | /// Returns a `Vec` with the serialized representation of the frame.
198 | fn serialize(&self) -> Vec {
199 | let mut buf = Vec::with_capacity(self.payload_len() as usize);
200 | // The header
201 | buf.extend(pack_header(&self.get_header()).to_vec().into_iter());
202 | // and then the body
203 | buf.extend(self.stream_dep.serialize().to_vec().into_iter());
204 |
205 | buf
206 | }
207 | }
208 |
209 | #[cfg(test)]
210 | mod tests {
211 | use super::super::frames::{Frame, RawFrame, pack_header};
212 | use super::super::test::{build_test_frame};
213 | use super::{PriorityFrame, NoFlag, StreamDependency};
214 |
215 | /// Tests that a simple PRIORITY frame is correclty parsed.
216 | #[test]
217 | fn test_priority_frame_parse() {
218 | let dep = StreamDependency::new(0, 5, true);
219 | let payload = {
220 | let mut buf: Vec = Vec::new();
221 | buf.extend(dep.serialize().to_vec().into_iter());
222 |
223 | buf
224 | };
225 | let header = (payload.len() as u32, 0x2, 0, 1);
226 |
227 | let frame = build_test_frame::(&header, &payload);
228 |
229 | assert_eq!(frame.flags, 0);
230 | assert_eq!(frame.get_stream_id(), 1);
231 | assert_eq!(frame.stream_dep, dep);
232 | }
233 |
234 | /// Tests that a HEADERS with stream ID 0 is considered invalid.
235 | #[test]
236 | fn test_priority_frame_parse_invalid_stream_id() {
237 | let dep = StreamDependency::new(0, 5, true);
238 | let payload = {
239 | let mut buf: Vec = Vec::new();
240 | buf.extend(dep.serialize().to_vec().into_iter());
241 |
242 | buf
243 | };
244 | let header = (payload.len() as u32, 0x2, 0, 0);
245 |
246 | let frame: Option = Frame::from_raw(
247 | RawFrame::with_payload(header, payload));
248 |
249 | assert!(frame.is_none());
250 | }
251 |
252 | /// Tests that the `HeadersFrame::parse` method considers any frame with
253 | /// a frame ID other than 1 in the frame header invalid.
254 | #[test]
255 | fn priority_frame_parse_invalid_type() {
256 | let dep = StreamDependency::new(0, 5, true);
257 | let payload = {
258 | let mut buf: Vec = Vec::new();
259 | buf.extend(dep.serialize().to_vec().into_iter());
260 |
261 | buf
262 | };
263 | let header = (payload.len() as u32, 0x1, 0, 1);
264 |
265 | let frame: Option = Frame::from_raw(
266 | RawFrame::with_payload(header, payload));
267 |
268 | assert!(frame.is_none());
269 | }
270 |
271 | /// Tests that a HEADERS frame with priority gets correctly serialized.
272 | #[test]
273 | fn test_priority_frame_serialize() {
274 | let dep = StreamDependency::new(0, 5, true);
275 | let payload = {
276 | let mut buf: Vec = Vec::new();
277 | buf.extend(dep.serialize().to_vec().into_iter());
278 |
279 | buf
280 | };
281 | let header = (payload.len() as u32, 0x2, 0, 1);
282 | let expected = {
283 | let headers = pack_header(&header);
284 | let mut res: Vec = Vec::new();
285 | res.extend(headers.to_vec().into_iter());
286 | res.extend(payload.into_iter());
287 |
288 | res
289 | };
290 | let frame = PriorityFrame::new(1, dep.clone());
291 |
292 | let actual = frame.serialize();
293 |
294 | assert_eq!(expected, actual);
295 | }
296 |
297 | /// Tests that a stream dependency structure can be correctly parsed by the
298 | /// `StreamDependency::parse` method.
299 | #[test]
300 | fn test_parse_stream_dependency() {
301 | {
302 | let buf = [0, 0, 0, 1, 5];
303 |
304 | let dep = StreamDependency::parse(&buf);
305 |
306 | assert_eq!(dep.stream_id, 1);
307 | assert_eq!(dep.weight, 5);
308 | // This one was not exclusive!
309 | assert!(!dep.is_exclusive)
310 | }
311 | {
312 | // Most significant bit set => is exclusive!
313 | let buf = [128, 0, 0, 1, 5];
314 |
315 | let dep = StreamDependency::parse(&buf);
316 |
317 | assert_eq!(dep.stream_id, 1);
318 | assert_eq!(dep.weight, 5);
319 | // This one was indeed exclusive!
320 | assert!(dep.is_exclusive)
321 | }
322 | {
323 | // Most significant bit set => is exclusive!
324 | let buf = [255, 255, 255, 255, 5];
325 |
326 | let dep = StreamDependency::parse(&buf);
327 |
328 | assert_eq!(dep.stream_id, (1 << 31) - 1);
329 | assert_eq!(dep.weight, 5);
330 | // This one was indeed exclusive!
331 | assert!(dep.is_exclusive);
332 | }
333 | {
334 | let buf = [127, 255, 255, 255, 5];
335 |
336 | let dep = StreamDependency::parse(&buf);
337 |
338 | assert_eq!(dep.stream_id, (1 << 31) - 1);
339 | assert_eq!(dep.weight, 5);
340 | // This one was not exclusive!
341 | assert!(!dep.is_exclusive);
342 | }
343 | }
344 |
345 | /// Tests that a stream dependency structure can be correctly serialized by
346 | /// the `StreamDependency::serialize` method.
347 | #[test]
348 | fn test_serialize_stream_dependency() {
349 | {
350 | let buf = [0, 0, 0, 1, 5];
351 | let dep = StreamDependency::new(1, 5, false);
352 |
353 | assert_eq!(buf, dep.serialize());
354 | }
355 | {
356 | // Most significant bit set => is exclusive!
357 | let buf = [128, 0, 0, 1, 5];
358 | let dep = StreamDependency::new(1, 5, true);
359 |
360 | assert_eq!(buf, dep.serialize());
361 | }
362 | {
363 | // Most significant bit set => is exclusive!
364 | let buf = [255, 255, 255, 255, 5];
365 | let dep = StreamDependency::new((1 << 31) - 1, 5, true);
366 |
367 | assert_eq!(buf, dep.serialize());
368 | }
369 | {
370 | let buf = [127, 255, 255, 255, 5];
371 | let dep = StreamDependency::new((1 << 31) - 1, 5, false);
372 |
373 | assert_eq!(buf, dep.serialize());
374 | }
375 | }
376 | }
377 |
378 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------