├── .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 |