├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md └── src ├── flag.rs ├── frame.rs ├── kind.rs ├── lib.rs └── payload.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /doc/ 15 | /target/ 16 | /examples/* 17 | !/examples/*.rs 18 | Cargo.lock 19 | 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | rust: 5 | - stable 6 | - beta 7 | - nightly 8 | 9 | script: 10 | - cargo build 11 | - cargo test --features random 12 | - cargo bench 13 | - cargo doc 14 | 15 | after_success: 16 | - if [ "$TRAVIS_PULL_REQUEST" == false && test == "TRAVIS_BRANCH" == "master" ]; then 17 | - curl https://raw.githubusercontent.com/reem/rust-gh-docs/master/make-docs.sh > docs.sh 18 | - chmod u+x docs.sh 19 | - ./docs.sh reem http2parse 20 | 21 | env: 22 | global: 23 | secure: QPYL1XUr4CyK/2DXlsYC1eCpWRpyEiqQSd/FFVR+YdP/rOJ7AyAXQqPhfgjDBQwvc6E2fUiyYjoV/xe1a757DDeZKlgd8Lp20fSDwvNt/Ejx8ueh3h3kuOtgDpIGSKX/l+XC+ltDpzjhh7bowI2/fOEf+kE53jvu9i4PiLnKdlY= 24 | 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "http2parse" 4 | version = "0.2.2" 5 | authors = ["Jonathan Reem "] 6 | repository = "https://github.com/reem/rust-http2parse.git" 7 | keywords = ["http2", "web", "parser", "frames"] 8 | description = "An HTTP2 frame parser." 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [features] 13 | random = ["rand"] 14 | 15 | [dependencies] 16 | bitflags = "0.1" 17 | byteorder = "0.3" 18 | 19 | [dependencies.rand] 20 | version = "0.3" 21 | optional = true 22 | 23 | [dev-dependencies] 24 | rand = "0.3" 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # http2parse 2 | 3 | > An HTTP2 frame parser. 4 | 5 | ## Overview 6 | 7 | A parser for HTTP2 frames. http2parse implements fast decoding 8 | of all HTTP2 frames into an efficient typed representation with 9 | no copying of frame payload data. 10 | 11 | http2parse does not implement HPACK encoding and decoding, 12 | which will likely end up in a different crate. 13 | 14 | ## Usage 15 | 16 | Use the crates.io repository; add this to your `Cargo.toml` along 17 | with the rest of your dependencies: 18 | 19 | ```toml 20 | [dependencies] 21 | http2parse = "0" 22 | ``` 23 | 24 | ## Author 25 | 26 | [Jonathan Reem](https://medium.com/@jreem) is the primary author and maintainer of http2parse. 27 | 28 | ## License 29 | 30 | MIT 31 | 32 | -------------------------------------------------------------------------------- /src/flag.rs: -------------------------------------------------------------------------------- 1 | bitflags! { 2 | #[derive(Debug)] 3 | flags Flag: u8 { 4 | const END_STREAM = 0x1, 5 | const ACK = 0x1, 6 | const END_HEADERS = 0x4, 7 | const PADDED = 0x8, 8 | const PRIORITY = 0x20 9 | } 10 | } 11 | 12 | impl Flag { 13 | pub fn new(data: u8) -> Result { 14 | match Flag::from_bits(data) { 15 | Some(v) => Ok(v), 16 | None => Err(()) 17 | } 18 | } 19 | 20 | // Note that ACK and END_STREAM are the same value, but they are only present 21 | // on different frame types. 22 | pub fn ack() -> Flag { ACK } 23 | pub fn end_stream() -> Flag { END_STREAM } 24 | pub fn end_headers() -> Flag { END_HEADERS } 25 | pub fn padded() -> Flag { PADDED } 26 | pub fn priority() -> Flag { PRIORITY } 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::Flag; 32 | 33 | const FLAG_EMPTY: u8 = 0x0; 34 | const FLAG_END_STREAM_OR_ACK: u8 = 0x1; 35 | const FLAG_END_HEADERS: u8 = 0x4; 36 | const FLAG_PADDED: u8 = 0x8; 37 | 38 | #[test] 39 | fn test_flag_empty() { 40 | assert_eq!(Flag::empty().bits(), FLAG_EMPTY); 41 | } 42 | 43 | #[test] 44 | fn test_flag_from_bits() { 45 | assert_eq!(Flag::from_bits(FLAG_EMPTY).unwrap(), Flag::empty()); 46 | assert_eq!(Flag::from_bits(FLAG_END_HEADERS | FLAG_PADDED).unwrap(), 47 | Flag::end_headers() | Flag::padded()); 48 | assert_eq!(Flag::from_bits(FLAG_END_STREAM_OR_ACK | FLAG_PADDED).unwrap(), 49 | Flag::end_stream() | Flag::padded()); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/frame.rs: -------------------------------------------------------------------------------- 1 | use {Payload, Error, Flag, Kind, StreamIdentifier, FRAME_HEADER_BYTES}; 2 | 3 | #[cfg(feature = "random")] 4 | use rand::{Rand, Rng}; 5 | 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 7 | pub struct Frame<'a> { 8 | pub header: FrameHeader, 9 | pub payload: Payload<'a> 10 | } 11 | 12 | impl<'a> Frame<'a> { 13 | pub fn parse(header: FrameHeader, buf: &[u8]) -> Result { 14 | Ok(Frame { 15 | header: header, 16 | payload: try!(Payload::parse(header, buf)) 17 | }) 18 | } 19 | 20 | /// Encodes this Frame into a buffer. 21 | pub fn encode(&self, buf: &mut [u8]) -> usize { 22 | self.header.encode(buf); 23 | self.payload.encode(&mut buf[FRAME_HEADER_BYTES..]) + FRAME_HEADER_BYTES 24 | } 25 | 26 | /// How many bytes this Frame will use in a buffer when encoding. 27 | pub fn encoded_len(&self) -> usize { 28 | FRAME_HEADER_BYTES + self.payload.encoded_len() 29 | } 30 | } 31 | 32 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 33 | pub struct FrameHeader { 34 | pub length: u32, 35 | pub kind: Kind, 36 | pub flag: Flag, 37 | pub id: StreamIdentifier, 38 | } 39 | 40 | impl FrameHeader { 41 | #[inline] 42 | pub fn parse(buf: &[u8]) -> Result { 43 | if buf.len() < FRAME_HEADER_BYTES { 44 | return Err(Error::Short); 45 | } 46 | 47 | Ok(FrameHeader { 48 | length: ((buf[0] as u32) << 16) | ((buf[1] as u32) << 8) | buf[2] as u32, 49 | kind: Kind::new(buf[3]), 50 | flag: try!(Flag::new(buf[4]).map_err(|()| { Error::BadFlag(buf[4]) })), 51 | id: StreamIdentifier::parse(&buf[5..]) 52 | }) 53 | } 54 | 55 | #[inline] 56 | pub fn encode(&self, buf: &mut [u8]) { 57 | ::encode_u24(buf, self.length); 58 | buf[3] = self.kind.encode(); 59 | buf[4] = self.flag.bits(); 60 | self.id.encode(&mut buf[5..]); 61 | } 62 | 63 | #[inline] 64 | #[cfg(feature = "random")] 65 | fn rand_for_payload(rng: &mut R, payload: &Payload) -> FrameHeader { 66 | let len = payload.encoded_len(); 67 | 68 | if len > 1 << 24 { 69 | panic!("Overlong payload for testing.") 70 | } 71 | 72 | let flags = Flag::empty() 73 | // if the payload has priority add the priority header. 74 | | if let Some(_) = payload.priority() { Flag::priority() } else { Flag::empty() }; 75 | 76 | FrameHeader { 77 | length: len as u32, 78 | kind: payload.kind(), 79 | flag: flags, 80 | ..rng.gen() 81 | } 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | #[cfg(feature = "random")] 87 | pub fn rand_for_payload(payload: &Payload) -> FrameHeader { 88 | FrameHeader::rand_for_payload(&mut ::rand::thread_rng(), payload) 89 | } 90 | 91 | #[cfg(feature = "random")] 92 | impl Rand for FrameHeader { 93 | fn rand(rng: &mut R) -> Self { 94 | FrameHeader { 95 | length: rng.gen_range(0, 1 << 24), 96 | kind: Kind::new(rng.gen_range(0, 9)), 97 | flag: *rng.choose(&[Flag::padded() | Flag::priority()]) 98 | .unwrap_or(&Flag::empty()), 99 | id: StreamIdentifier(rng.gen_range(0, 1 << 31)) 100 | } 101 | } 102 | } 103 | 104 | #[cfg(feature = "random")] 105 | impl Rand for Frame<'static> { 106 | fn rand(rng: &mut R) -> Self { 107 | let payload = rng.gen::(); 108 | let header = FrameHeader::rand_for_payload(rng, &payload); 109 | 110 | Frame { 111 | header: header, 112 | payload: payload 113 | } 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use {Kind, Flag, FrameHeader, StreamIdentifier}; 120 | #[cfg(feature = "random")] 121 | use {Frame}; 122 | 123 | #[test] 124 | fn test_frame_header_parse_empty() { 125 | assert_eq!(FrameHeader { 126 | length: 0, 127 | kind: Kind::Data, 128 | flag: Flag::empty(), 129 | id: StreamIdentifier(0) 130 | }, FrameHeader::parse(&[ 131 | 0u8, 0u8, 0u8, // length 132 | 0u8, // type/kind 133 | 0u8, // flags 134 | 0u8, 0u8, 0u8, 0u8 // reserved bit + stream identifier 135 | ]).unwrap()); 136 | } 137 | 138 | #[test] 139 | fn test_frame_header_parse_full() { 140 | assert_eq!(FrameHeader { 141 | length: 16777215, 142 | kind: Kind::Unregistered, 143 | flag: Flag::empty(), 144 | id: StreamIdentifier(2147483647) 145 | }, FrameHeader::parse(&[ 146 | 0xFF, 0xFF, 0xFF, // length 147 | 0xFF, // type/kind 148 | 0x0, // flags 149 | 0xFF, 0xFF, 0xFF, 0xFF // reserved bit + stream identifier 150 | ]).unwrap()); 151 | } 152 | 153 | #[test] 154 | fn test_frame_header_parse() { 155 | assert_eq!(FrameHeader { 156 | length: 66051, 157 | kind: Kind::Settings, 158 | flag: Flag::end_stream(), 159 | id: StreamIdentifier(101124105) 160 | }, FrameHeader::parse(&[ 161 | 0x1, 0x2, 0x3, // length 162 | 0x4, // type/kind 163 | 0x1, // flags 164 | 0x6, 0x7, 0x8, 0x9 // reserved bit + stream identifier 165 | ]).unwrap()); 166 | } 167 | 168 | #[cfg(feature = "random")] 169 | #[test] 170 | fn test_frame_header_encoding() { 171 | fn roundtrip(header: FrameHeader) { 172 | let buf = &mut [0; 9]; 173 | header.encode(buf); 174 | 175 | assert_eq!(header, FrameHeader::parse(&*buf).unwrap()) 176 | } 177 | 178 | for _ in 0..100 { 179 | roundtrip(::rand::random()) 180 | } 181 | } 182 | 183 | #[cfg(feature = "random")] 184 | #[test] 185 | fn test_frame_encoding() { 186 | fn roundtrip(buf: &mut [u8], frame: Frame) { 187 | let _ = frame.encode(buf); 188 | 189 | assert_eq!( 190 | frame, 191 | Frame::parse(FrameHeader::parse(&buf[..9]).unwrap(), 192 | &buf[9..]).unwrap()) 193 | } 194 | 195 | let mut buf = vec![0; 5000]; 196 | for _ in 0..1000 { 197 | roundtrip(&mut buf, ::rand::random()) 198 | } 199 | } 200 | 201 | #[cfg(not(feature = "random"))] 202 | #[test] 203 | fn no_frame_encoding_test_because_no_rand() {} 204 | 205 | #[bench] 206 | #[cfg(feature = "random")] 207 | fn bench_frame_parse(b: &mut ::test::Bencher) { 208 | // Each iter = 5 frames 209 | let frames = vec![::rand::random::(); 5]; 210 | let bufs = frames.iter().map(|frame| { 211 | let mut buf = vec![0; 2000]; 212 | frame.encode(&mut buf); 213 | buf 214 | }).collect::>(); 215 | 216 | b.bytes = frames.iter().map(|frame| frame.encoded_len() as u64) 217 | .fold(0, |a, b| a + b); 218 | 219 | b.iter(|| { 220 | for buf in &bufs { 221 | Frame::parse(FrameHeader::parse(&buf[..9]).unwrap(), 222 | &buf[9..]).unwrap(); 223 | } 224 | }); 225 | } 226 | 227 | #[bench] 228 | #[cfg(feature = "random")] 229 | fn bench_frame_encode(b: &mut ::test::Bencher) { 230 | let frames = vec![::rand::random::(); 5]; 231 | 232 | b.bytes = frames.iter().map(|frame| frame.encoded_len() as u64) 233 | .fold(0, |a, b| a + b); 234 | 235 | let mut buf = vec![0; 2000]; 236 | b.iter(|| { 237 | for frame in &frames { 238 | frame.encode(&mut buf); 239 | ::test::black_box(&buf); 240 | } 241 | }); 242 | } 243 | 244 | #[bench] 245 | fn bench_frame_header_parse(b: &mut ::test::Bencher) { 246 | b.bytes = ::FRAME_HEADER_BYTES as u64; 247 | 248 | b.iter(|| { 249 | let mut buf = &[ 250 | 0x1, 0x2, 0x3, // length 251 | 0x4, // type/kind 252 | 0x1, // flags 253 | 0x6, 0x7, 0x8, 0x9 // reserved bit + stream identifier 254 | ]; 255 | 256 | // Prevent constant propagation. 257 | buf = ::test::black_box(buf); 258 | 259 | // Prevent dead code elimination. 260 | ::test::black_box(FrameHeader::parse(buf).unwrap()); 261 | }); 262 | } 263 | } 264 | 265 | -------------------------------------------------------------------------------- /src/kind.rs: -------------------------------------------------------------------------------- 1 | #[repr(u8)] 2 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 3 | pub enum Kind { 4 | Data = 0, 5 | Headers = 1, 6 | Priority = 2, 7 | Reset = 3, 8 | Settings = 4, 9 | PushPromise = 5, 10 | Ping = 6, 11 | GoAway = 7, 12 | WindowUpdate = 8, 13 | Continuation = 9, 14 | Unregistered 15 | } 16 | 17 | impl Kind { 18 | pub fn new(byte: u8) -> Kind { 19 | return match byte { 20 | 0 => Kind::Data, 21 | 1 => Kind::Headers, 22 | 2 => Kind::Priority, 23 | 3 => Kind::Reset, 24 | 4 => Kind::Settings, 25 | 5 => Kind::PushPromise, 26 | 6 => Kind::Ping, 27 | 7 => Kind::GoAway, 28 | 8 => Kind::WindowUpdate, 29 | 9 => Kind::Continuation, 30 | _ => Kind::Unregistered 31 | } 32 | } 33 | 34 | pub fn encode(&self) -> u8 { 35 | match *self { 36 | Kind::Data => 0, 37 | Kind::Headers => 1, 38 | Kind::Priority => 2, 39 | Kind::Reset => 3, 40 | Kind::Settings => 4, 41 | Kind::PushPromise => 5, 42 | Kind::Ping => 6, 43 | Kind::GoAway => 7, 44 | Kind::WindowUpdate => 8, 45 | Kind::Continuation => 9, 46 | Kind::Unregistered => 255 47 | } 48 | } 49 | } 50 | 51 | #[test] 52 | fn test_encode() { 53 | for n in 0..10 { 54 | assert_eq!(Kind::new(n), Kind::new(Kind::new(n).encode())); 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(test, deny(warnings))] 2 | #![cfg_attr(test, feature(test))] 3 | #![allow(non_upper_case_globals)] 4 | // #![deny(missing_docs)] 5 | 6 | //! # http2parse 7 | //! 8 | //! An HTTP2 frame parser. 9 | //! 10 | 11 | #[macro_use] 12 | extern crate bitflags; 13 | extern crate byteorder; 14 | 15 | #[cfg(test)] 16 | extern crate test; 17 | #[cfg(any(test, feature = "random"))] 18 | extern crate rand; 19 | 20 | const FRAME_HEADER_BYTES: usize = 9; 21 | 22 | pub use kind::Kind; 23 | pub use flag::Flag; 24 | pub use frame::{Frame, FrameHeader}; 25 | pub use payload::{Payload, Priority, Setting, SettingIdentifier}; 26 | 27 | use byteorder::ByteOrder; 28 | 29 | mod kind; 30 | mod flag; 31 | mod payload; 32 | mod frame; 33 | 34 | /// Errors that can occur during parsing an HTTP/2 frame. 35 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 36 | pub enum Error { 37 | /// A full frame header was not passed. 38 | Short, 39 | 40 | /// An unsupported value was set for the flag value. 41 | BadFlag(u8), 42 | 43 | /// An unsupported value was set for the frame kind. 44 | BadKind(u8), 45 | 46 | /// The padding length was larger than the frame-header-specified 47 | /// length of the payload. 48 | TooMuchPadding(u8), 49 | 50 | /// The payload length specified by the frame header was shorter than 51 | /// necessary for the parser settings specified and the frame type. 52 | /// 53 | /// This happens if, for instance, the priority flag is set and the 54 | /// header length is shorter than a stream dependency. 55 | /// 56 | /// `PayloadLengthTooShort` should be treated as a protocol error. 57 | PayloadLengthTooShort, 58 | 59 | /// The payload length specified by the frame header of a settings frame 60 | /// was not a round multiple of the size of a single setting. 61 | PartialSettingLength, 62 | 63 | /// The payload length specified by the frame header was not the 64 | /// value necessary for the specific frame type. 65 | InvalidPayloadLength 66 | } 67 | 68 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 69 | struct ParserSettings { 70 | padding: bool, 71 | priority: bool 72 | } 73 | 74 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 75 | pub struct StreamIdentifier(pub u32); 76 | 77 | impl StreamIdentifier { 78 | pub fn parse(buf: &[u8]) -> StreamIdentifier { 79 | StreamIdentifier( 80 | byteorder::BigEndian::read_u32(buf) & ((1 << 31) - 1) 81 | ) 82 | } 83 | 84 | pub fn encode(&self, buf: &mut [u8]) -> usize { 85 | encode_u32(buf, self.0) 86 | } 87 | } 88 | 89 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 90 | pub struct ErrorCode(pub u32); 91 | 92 | pub enum HttpError { 93 | Protocol, 94 | Internal, 95 | FlowControlError, 96 | SettingsTimeout, 97 | } 98 | 99 | impl ErrorCode { 100 | pub fn parse(buf: &[u8]) -> ErrorCode { 101 | ErrorCode(byteorder::BigEndian::read_u32(buf)) 102 | } 103 | 104 | pub fn encode(&self, buf: &mut [u8]) -> usize { 105 | encode_u32(buf, self.0) 106 | } 107 | } 108 | 109 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 110 | pub struct SizeIncrement(pub u32); 111 | 112 | impl SizeIncrement { 113 | pub fn parse(buf: &[u8]) -> SizeIncrement { 114 | SizeIncrement(byteorder::BigEndian::read_u32(buf)) 115 | } 116 | 117 | pub fn encode(&self, buf: &mut [u8]) -> usize { 118 | encode_u32(buf, self.0) 119 | } 120 | } 121 | 122 | #[inline(always)] 123 | fn encode_u24(buf: &mut [u8], val: u32) -> usize { 124 | buf[0] = (val >> 16) as u8; 125 | buf[1] = (val >> 8) as u8; 126 | buf[2] = val as u8; 127 | 128 | 3 129 | } 130 | 131 | #[inline(always)] 132 | fn encode_u32(buf: &mut [u8], val: u32) -> usize { 133 | byteorder::BigEndian::write_u32(buf, val); 134 | 4 135 | } 136 | 137 | #[inline(always)] 138 | fn encode_u64(buf: &mut [u8], val: u64) -> usize { 139 | byteorder::BigEndian::write_u64(buf, val); 140 | 8 141 | } 142 | 143 | #[test] 144 | fn test_stream_id_ignores_highest_bit() { 145 | let raw1 = [0x7F, 0xFF, 0xFF, 0xFF]; 146 | let raw2 = [0xFF, 0xFF, 0xFF, 0xFF]; 147 | 148 | assert_eq!( 149 | StreamIdentifier::parse(&raw1), 150 | StreamIdentifier::parse(&raw2)); 151 | } 152 | 153 | -------------------------------------------------------------------------------- /src/payload.rs: -------------------------------------------------------------------------------- 1 | use std::{slice, mem, fmt}; 2 | use {FrameHeader, StreamIdentifier, Error, Kind, 3 | ParserSettings, ErrorCode, SizeIncrement, Flag}; 4 | 5 | use byteorder::ByteOrder; 6 | 7 | #[cfg(feature = "random")] 8 | use rand::{Rand, Rng}; 9 | 10 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 11 | pub enum Payload<'a> { 12 | Data { 13 | data: &'a [u8] 14 | }, 15 | Headers { 16 | priority: Option, 17 | block: &'a [u8] 18 | }, 19 | Priority(Priority), 20 | Reset(ErrorCode), 21 | Settings(&'a [Setting]), 22 | PushPromise { 23 | promised: StreamIdentifier, 24 | block: &'a [u8] 25 | }, 26 | Ping(u64), 27 | GoAway { 28 | last: StreamIdentifier, 29 | error: ErrorCode, 30 | data: &'a [u8] 31 | }, 32 | WindowUpdate(SizeIncrement), 33 | Continuation(&'a [u8]), 34 | Unregistered(&'a [u8]) 35 | } 36 | 37 | const PRIORITY_BYTES: u32 = 5; 38 | const PADDING_BYTES: u32 = 1; 39 | 40 | impl<'a> Payload<'a> { 41 | #[inline] 42 | pub fn kind(&self) -> Kind { 43 | use self::Payload::*; 44 | 45 | match *self { 46 | Data { .. } => Kind::Data, 47 | Headers { .. } => Kind::Headers, 48 | Priority(..) => Kind::Priority, 49 | Reset(..) => Kind::Reset, 50 | Settings(..) => Kind::Settings, 51 | PushPromise { .. } => Kind::PushPromise, 52 | Ping(..) => Kind::Ping, 53 | GoAway { .. } => Kind::GoAway, 54 | WindowUpdate(_) => Kind::WindowUpdate, 55 | Continuation(_) => Kind::Continuation, 56 | Unregistered(_) => Kind::Unregistered 57 | } 58 | } 59 | 60 | #[inline] 61 | pub fn parse(header: FrameHeader, mut buf: &'a [u8]) -> Result, Error> { 62 | let settings = ParserSettings { 63 | padding: header.flag.contains(Flag::padded()), 64 | priority: header.flag.contains(Flag::priority()) 65 | }; 66 | 67 | if buf.len() < header.length as usize { 68 | return Err(Error::Short) 69 | } 70 | 71 | let min_payload_length = 72 | if settings.priority && settings.padding { 73 | PRIORITY_BYTES + PADDING_BYTES 74 | } else if settings.priority { 75 | PRIORITY_BYTES 76 | } else if settings.padding { 77 | PADDING_BYTES 78 | } else { 79 | 0 80 | }; 81 | 82 | if header.length < min_payload_length { 83 | return Err(Error::PayloadLengthTooShort) 84 | } 85 | 86 | buf = &buf[..header.length as usize]; 87 | 88 | match header.kind { 89 | Kind::Data => Payload::parse_data(header, buf, settings), 90 | Kind::Headers => Payload::parse_headers(header, buf, settings), 91 | Kind::Priority => { 92 | let (_, priority) = try!(Priority::parse(true, buf)); 93 | Ok(Payload::Priority(priority.unwrap())) 94 | }, 95 | Kind::Reset => Payload::parse_reset(header, buf), 96 | Kind::Settings => Payload::parse_settings(header, buf), 97 | Kind::Ping => Payload::parse_ping(header, buf), 98 | Kind::GoAway => Payload::parse_goaway(header, buf), 99 | Kind::WindowUpdate => Payload::parse_window_update(header, buf), 100 | Kind::PushPromise => Payload::parse_push_promise(header, buf, settings), 101 | Kind::Continuation => Ok(Payload::Continuation(buf)), 102 | Kind::Unregistered => Ok(Payload::Unregistered(buf)) 103 | } 104 | } 105 | 106 | #[inline] 107 | pub fn encode(&self, buf: &mut [u8]) -> usize { 108 | match *self { 109 | Payload::Data { ref data } => { encode_memory(data, buf) }, 110 | Payload::Headers { ref priority, ref block } => { 111 | let priority_wrote = priority.map(|p| { p.encode(buf) }).unwrap_or(0); 112 | let block_wrote = encode_memory(block, &mut buf[priority_wrote..]); 113 | priority_wrote + block_wrote 114 | }, 115 | Payload::Reset(ref err) => { err.encode(buf) }, 116 | Payload::Settings(ref settings) => { 117 | encode_memory(Setting::to_bytes(settings), buf) 118 | }, 119 | Payload::Ping(data) => { ::encode_u64(buf, data) }, 120 | Payload::GoAway { ref data, ref last, ref error } => { 121 | let last_wrote = last.encode(buf); 122 | let buf = &mut buf[last_wrote..]; 123 | 124 | let error_wrote = error.encode(buf); 125 | let buf = &mut buf[error_wrote..]; 126 | 127 | encode_memory(data, buf) + last_wrote + error_wrote 128 | }, 129 | Payload::WindowUpdate(ref increment) => { increment.encode(buf) }, 130 | Payload::PushPromise { ref promised, ref block } => { 131 | promised.encode(buf); 132 | encode_memory(block, &mut buf[4..]) + 4 133 | }, 134 | Payload::Priority(ref priority) => { priority.encode(buf) }, 135 | Payload::Continuation(ref block) => { encode_memory(block, buf) }, 136 | Payload::Unregistered(ref block) => { encode_memory(block, buf) } 137 | } 138 | } 139 | 140 | #[inline] 141 | /// How many bytes this Payload would be encoded. 142 | pub fn encoded_len(&self) -> usize { 143 | use self::Payload::*; 144 | 145 | match *self { 146 | Data { ref data } => { data.len() }, 147 | Headers { ref priority, ref block } => { 148 | let priority_len = if priority.is_some() { 5 } else { 0 }; 149 | priority_len + block.len() 150 | }, 151 | Reset(_) => 4, 152 | Settings(ref settings) => settings.len() * mem::size_of::(), 153 | Ping(_) => 8, 154 | GoAway { ref data, .. } => 4 + 4 + data.len(), 155 | WindowUpdate(_) => 4, 156 | PushPromise { ref block, .. } => 4 + block.len(), 157 | Priority(_) => 5, 158 | Continuation(ref block) => block.len(), 159 | Unregistered(ref block) => block.len() 160 | } 161 | } 162 | 163 | #[inline] 164 | pub fn padded(&self) -> Option { 165 | None 166 | } 167 | 168 | #[inline] 169 | pub fn priority(&self) -> Option<&Priority> { 170 | match *self { 171 | Payload::Priority(ref priority) => Some(priority), 172 | Payload::Headers { ref priority, .. } => priority.as_ref(), 173 | _ => None 174 | } 175 | } 176 | 177 | #[inline] 178 | fn parse_data(header: FrameHeader, buf: &'a [u8], 179 | settings: ParserSettings) -> Result, Error> { 180 | Ok(Payload::Data { 181 | data: try!(trim_padding(settings, header, buf)) 182 | }) 183 | } 184 | 185 | #[inline] 186 | fn parse_headers(header: FrameHeader, mut buf: &'a [u8], 187 | settings: ParserSettings) -> Result, Error> { 188 | buf = try!(trim_padding(settings, header, buf)); 189 | let (buf, priority) = try!(Priority::parse(settings.priority, buf)); 190 | Ok(Payload::Headers { 191 | priority: priority, 192 | block: buf 193 | }) 194 | } 195 | 196 | #[inline] 197 | fn parse_reset(header: FrameHeader, 198 | buf: &'a [u8]) -> Result, Error> { 199 | if header.length < 4 { 200 | return Err(Error::PayloadLengthTooShort) 201 | } 202 | 203 | Ok(Payload::Reset(ErrorCode::parse(buf))) 204 | } 205 | 206 | #[inline] 207 | fn parse_settings(header: FrameHeader, 208 | buf: &'a [u8]) -> Result, Error> { 209 | if header.length % mem::size_of::() as u32 != 0 { 210 | return Err(Error::PartialSettingLength) 211 | } 212 | 213 | Ok(Payload::Settings(Setting::from_bytes(&buf[..header.length as usize]))) 214 | } 215 | 216 | #[inline] 217 | fn parse_ping(header: FrameHeader, 218 | buf: &'a [u8]) -> Result, Error> { 219 | if header.length != 8 { 220 | return Err(Error::InvalidPayloadLength) 221 | } 222 | 223 | let data = ::byteorder::BigEndian::read_u64(buf); 224 | Ok(Payload::Ping(data)) 225 | } 226 | 227 | #[inline] 228 | fn parse_goaway(header: FrameHeader, 229 | buf: &'a [u8]) -> Result, Error> { 230 | if header.length < 8 { 231 | return Err(Error::PayloadLengthTooShort) 232 | } 233 | 234 | let last = StreamIdentifier::parse(buf); 235 | let error = ErrorCode::parse(&buf[4..]); 236 | let rest = &buf[8..]; 237 | 238 | Ok(Payload::GoAway { 239 | last: last, 240 | error: error, 241 | data: rest 242 | }) 243 | } 244 | 245 | #[inline] 246 | fn parse_window_update(header: FrameHeader, 247 | buf: &'a [u8]) -> Result, Error> { 248 | if header.length != 4 { 249 | return Err(Error::InvalidPayloadLength) 250 | } 251 | 252 | Ok(Payload::WindowUpdate(SizeIncrement::parse(buf))) 253 | } 254 | 255 | #[inline] 256 | fn parse_push_promise(header: FrameHeader, mut buf: &'a [u8], 257 | settings: ParserSettings) -> Result, Error> { 258 | buf = try!(trim_padding(settings, header, buf)); 259 | 260 | if buf.len() < 4 { 261 | return Err(Error::PayloadLengthTooShort) 262 | } 263 | 264 | let promised = StreamIdentifier::parse(buf); 265 | let block = &buf[4..]; 266 | 267 | Ok(Payload::PushPromise { 268 | promised: promised, 269 | block: block 270 | }) 271 | } 272 | } 273 | 274 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 275 | pub struct Priority { 276 | exclusive: bool, 277 | dependency: StreamIdentifier, 278 | weight: u8 279 | } 280 | 281 | impl Priority { 282 | #[inline] 283 | pub fn parse(present: bool, buf: &[u8]) -> Result<(&[u8], Option), Error> { 284 | if present { 285 | Ok((&buf[5..], Some(Priority { 286 | // Most significant bit. 287 | exclusive: buf[0] & 0x7F != buf[0], 288 | dependency: StreamIdentifier::parse(buf), 289 | weight: buf[4] 290 | }))) 291 | } else { 292 | Ok((buf, None)) 293 | } 294 | } 295 | 296 | #[inline] 297 | pub fn encode(&self, buf: &mut [u8]) -> usize { 298 | let mut dependency = self.dependency; 299 | if self.exclusive { dependency.0 |= 1 << 31 } 300 | 301 | dependency.encode(buf); 302 | buf[PRIORITY_BYTES as usize - 1] = self.weight; 303 | 304 | PRIORITY_BYTES as usize 305 | } 306 | } 307 | 308 | // Settings are (u16, u32) in memory. 309 | #[repr(packed)] 310 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 311 | pub struct Setting { 312 | identifier: u16, 313 | value: u32 314 | } 315 | 316 | impl fmt::Debug for Setting { 317 | #[inline] 318 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 319 | fmt::Debug::fmt(&self.identifier(), f) 320 | } 321 | } 322 | 323 | impl Setting { 324 | #[inline] 325 | pub fn new(identifier: SettingIdentifier, value: u32) -> Setting { 326 | Setting { 327 | identifier: identifier as u16, 328 | value: value, 329 | } 330 | } 331 | 332 | #[inline] 333 | pub fn identifier(&self) -> Option { 334 | match self.identifier { 335 | 0x1 => Some(SettingIdentifier::HeaderTableSize), 336 | 0x2 => Some(SettingIdentifier::EnablePush), 337 | 0x3 => Some(SettingIdentifier::MaxConcurrentStreams), 338 | 0x4 => Some(SettingIdentifier::InitialWindowSize), 339 | 0x5 => Some(SettingIdentifier::MaxFrameSize), 340 | _ => None 341 | } 342 | } 343 | 344 | #[inline] 345 | pub fn value(&self) -> u32 { 346 | self.value 347 | } 348 | 349 | #[inline] 350 | fn to_bytes(settings: &[Setting]) -> &[u8] { 351 | unsafe { 352 | slice::from_raw_parts( 353 | settings.as_ptr() as *const u8, 354 | settings.len() * mem::size_of::()) 355 | } 356 | } 357 | 358 | #[inline] 359 | fn from_bytes(bytes: &[u8]) -> &[Setting] { 360 | unsafe { 361 | slice::from_raw_parts( 362 | bytes.as_ptr() as *const Setting, 363 | bytes.len() / mem::size_of::()) 364 | } 365 | } 366 | } 367 | 368 | #[repr(u16)] 369 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 370 | pub enum SettingIdentifier { 371 | HeaderTableSize = 0x1, 372 | EnablePush = 0x2, 373 | MaxConcurrentStreams = 0x3, 374 | InitialWindowSize = 0x4, 375 | MaxFrameSize = 0x5 376 | } 377 | 378 | #[cfg(feature = "random")] 379 | impl Rand for Payload<'static> { 380 | fn rand(rng: &mut R) -> Self { 381 | use self::Payload::*; 382 | 383 | let choices = &[ 384 | Data { 385 | data: rand_buf(rng) 386 | }, 387 | Headers { 388 | priority: rng.gen(), 389 | block: rand_buf(rng), 390 | }, 391 | Priority(rng.gen()), 392 | Reset(ErrorCode(rng.gen())), 393 | Settings(leak({ 394 | let len = rng.gen_range(0, 200); 395 | 396 | (0..len).map(|_| Setting { 397 | identifier: *rng.choose(&[ 398 | SettingIdentifier::HeaderTableSize, 399 | SettingIdentifier::EnablePush, 400 | SettingIdentifier::MaxConcurrentStreams, 401 | SettingIdentifier::InitialWindowSize, 402 | SettingIdentifier::MaxFrameSize 403 | ]).unwrap() as u16, 404 | value: rng.gen() 405 | }).collect::>()})), 406 | PushPromise { 407 | promised: StreamIdentifier(rng.gen_range(0, 1 << 31)), 408 | block: rand_buf(rng) 409 | }, 410 | Ping(rng.gen()), 411 | GoAway { 412 | last: StreamIdentifier(rng.gen_range(0, 1 << 31)), 413 | error: ErrorCode(rng.gen()), 414 | data: rand_buf(rng) 415 | }, 416 | WindowUpdate(SizeIncrement(rng.gen())), 417 | Continuation(rand_buf(rng)), 418 | Unregistered(rand_buf(rng)) 419 | ]; 420 | 421 | *rng.choose(choices).unwrap() 422 | } 423 | } 424 | 425 | #[cfg(feature = "random")] 426 | impl Rand for Priority { 427 | fn rand(rng: &mut R) -> Self { 428 | Priority { 429 | exclusive: rng.gen(), 430 | dependency: StreamIdentifier(rng.gen_range(0, 1 << 31)), 431 | weight: rng.gen() 432 | } 433 | } 434 | } 435 | 436 | #[cfg(feature = "random")] 437 | fn rand_buf(rng: &mut R) -> &'static [u8] { 438 | let len = rng.gen_range(0, 200); 439 | let mut buf = vec![0; len]; 440 | rng.fill_bytes(&mut *buf); 441 | 442 | leak(buf) 443 | } 444 | 445 | #[cfg(feature = "random")] 446 | fn leak(buf: Vec) -> &'static [T] { 447 | let result = unsafe { mem::transmute::<&[T], &'static [T]>(&*buf) }; 448 | mem::forget(buf); 449 | result 450 | } 451 | 452 | #[inline] 453 | fn trim_padding(settings: ParserSettings, header: FrameHeader, 454 | buf: &[u8]) -> Result<&[u8], Error> { 455 | if settings.padding { 456 | let pad_length = buf[0]; 457 | if pad_length as u32 > header.length { 458 | Err(Error::TooMuchPadding(pad_length)) 459 | } else { 460 | Ok(&buf[1..header.length as usize - pad_length as usize]) 461 | } 462 | } else { 463 | Ok(buf) 464 | } 465 | } 466 | 467 | #[inline] 468 | fn encode_memory(src: &[u8], mut dst: &mut [u8]) -> usize { 469 | use std::io::Write; 470 | dst.write(src).unwrap() 471 | } 472 | 473 | #[test] 474 | #[cfg(feature = "random")] 475 | fn test_specific_encode() { 476 | fn roundtrip(buf: &mut [u8], payload: Payload) { 477 | payload.encode(buf); 478 | 479 | assert_eq!(payload, Payload::parse(::frame::rand_for_payload(&payload), &buf).unwrap()); 480 | } 481 | 482 | let mut buf = vec![0; 5000]; 483 | roundtrip(&mut buf, Payload::PushPromise { promised: StreamIdentifier(2000064271), block: &[255, 108, 25, 19, 189, 134, 191, 26, 27, 56, 65, 237, 220, 161, 73, 167, 246, 154, 248, 216, 236, 6, 23, 200, 56, 128, 239, 218, 193, 25, 221, 115, 37, 74, 50, 35, 75, 254, 88, 173, 24, 193, 220, 201, 102, 114, 187, 68, 8, 59, 205, 49, 180, 217, 170, 241, 11, 155, 115, 146, 109, 160, 85, 197, 32, 243, 191, 94, 96, 143, 206, 11, 244, 4, 244, 136, 201, 232, 111, 246, 251, 139, 81, 67, 116, 16, 201, 109, 121, 170, 48, 38, 23, 99, 101, 182, 111, 110, 202, 153, 0, 230, 87, 242, 206, 72, 196, 106, 200, 243, 48, 16, 33, 205, 65, 112, 132, 150, 89, 161, 108, 231, 155, 243, 123, 92, 141, 128, 204, 33, 207] }); 484 | roundtrip(&mut buf, Payload::Ping(4513863121605750535)); 485 | } 486 | 487 | #[test] 488 | #[cfg(feature = "random")] 489 | fn test_randomized_encoded_len() { 490 | fn roundtrip(buf: &mut [u8], payload: Payload, round: usize) { 491 | let len = payload.encoded_len(); 492 | let encoded = payload.encode(buf); 493 | 494 | assert!(encoded == len, format!("Bad roundtrip! encoded={:?}, len={:?}, payload={:#?}, round={:?}", 495 | encoded, len, payload, round)) 496 | } 497 | 498 | let mut buf = vec![0; 5000]; 499 | for round in 0..1000 { 500 | roundtrip(&mut buf, ::rand::random(), round) 501 | } 502 | } 503 | 504 | #[test] 505 | #[cfg(feature = "random")] 506 | fn test_randomized_encode() { 507 | fn roundtrip(buf: &mut [u8], payload: Payload) { 508 | payload.encode(buf); 509 | 510 | assert_eq!(payload, Payload::parse(::frame::rand_for_payload(&payload), &buf).unwrap()); 511 | } 512 | 513 | let mut buf = vec![0; 5000]; 514 | for _ in 0..1000 { 515 | roundtrip(&mut buf, ::rand::random()) 516 | } 517 | } 518 | 519 | #[test] 520 | #[cfg(not(feature = "random"))] 521 | fn no_test_encoded_len_because_no_rand() {} 522 | 523 | #[test] 524 | #[cfg(not(feature = "random"))] 525 | fn no_test_encode_because_no_rand() {} 526 | 527 | --------------------------------------------------------------------------------