Handle padding & stream priority when parsing headers frame

This commit is contained in:
Carl Lerche
2017-08-10 13:55:36 -07:00
parent e7c07b0b12
commit 50e0ad2f2a
4 changed files with 54 additions and 9 deletions

View File

@@ -14,7 +14,7 @@ impl GoAway {
unimplemented!(); unimplemented!();
} }
let last_stream_id = StreamId::parse(&payload[..4]); let (last_stream_id, _) = StreamId::parse(&payload[..4]);
let error_code = unpack_octets_4!(payload, 4, u32); let error_code = unpack_octets_4!(payload, 4, u32);
Ok(GoAway { Ok(GoAway {

View File

@@ -38,10 +38,12 @@ impl Head {
/// Parse an HTTP/2.0 frame header /// Parse an HTTP/2.0 frame header
pub fn parse(header: &[u8]) -> Head { pub fn parse(header: &[u8]) -> Head {
let (stream_id, _) = StreamId::parse(&header[5..]);
Head { Head {
kind: Kind::new(header[3]), kind: Kind::new(header[3]),
flag: header[4], flag: header[4],
stream_id: StreamId::parse(&header[5..]), stream_id,
} }
} }

View File

@@ -7,10 +7,11 @@ use http::{self, request, response, version, uri, Method, StatusCode, Uri};
use http::{Request, Response}; use http::{Request, Response};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use bytes::{BytesMut, Bytes}; use bytes::{BytesMut, Bytes, Buf};
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use string::String; use string::String;
use std::fmt;
use std::io::Cursor; use std::io::Cursor;
/// Header frame /// Header frame
@@ -35,7 +36,7 @@ pub struct Headers {
flags: HeadersFlag, flags: HeadersFlag,
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq)]
pub struct HeadersFlag(u8); pub struct HeadersFlag(u8);
#[derive(Debug)] #[derive(Debug)]
@@ -127,7 +128,36 @@ impl Headers {
{ {
let flags = HeadersFlag(head.flag()); let flags = HeadersFlag(head.flag());
assert!(!flags.is_priority(), "unimplemented stream priority"); // Read the padding length
if flags.is_padded() {
let pad = src.get_u8() as usize;
// Truncate the last `pad` bytes.
let len = src.get_ref().len() - pad;
src.get_mut().truncate(len);
}
// Read the stream dependency
let stream_dep = if flags.is_priority() {
let mut buf = [0u8; 4];
// Read the next 4 bytes
src.copy_to_slice(&mut buf);
// Parse the stream ID and exclusive flag
let (stream_id, is_exclusive) = StreamId::parse(&buf);
// Read the weight
let weight = src.get_u8();
Some(StreamDependency {
stream_id,
weight,
is_exclusive,
})
} else {
None
};
let mut pseudo = Pseudo::default(); let mut pseudo = Pseudo::default();
let mut fields = HeaderMap::new(); let mut fields = HeaderMap::new();
@@ -169,7 +199,7 @@ impl Headers {
Ok(Headers { Ok(Headers {
stream_id: head.stream_id(), stream_id: head.stream_id(),
stream_dep: None, stream_dep: stream_dep,
fields: fields, fields: fields,
pseudo: pseudo, pseudo: pseudo,
flags: flags, flags: flags,
@@ -312,7 +342,7 @@ impl PushPromise {
// TODO: Handle padding // TODO: Handle padding
let promised_id = StreamId::parse(&payload[..4]); let (promised_id, _) = StreamId::parse(&payload[..4]);
Ok(PushPromise { Ok(PushPromise {
stream_id: head.stream_id(), stream_id: head.stream_id(),
@@ -476,3 +506,14 @@ impl From<HeadersFlag> for u8 {
src.0 src.0
} }
} }
impl fmt::Debug for HeadersFlag {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("HeadersFlag")
.field("end_stream", &self.is_end_stream())
.field("end_headers", &self.is_end_headers())
.field("padded", &self.is_padded())
.field("priority", &self.is_priority())
.finish()
}
}

View File

@@ -9,11 +9,13 @@ const STREAM_ID_MASK: u32 = 1 << 31;
impl StreamId { impl StreamId {
/// Parse the stream ID /// Parse the stream ID
#[inline] #[inline]
pub fn parse(buf: &[u8]) -> StreamId { pub fn parse(buf: &[u8]) -> (StreamId, bool) {
let unpacked = BigEndian::read_u32(buf); let unpacked = BigEndian::read_u32(buf);
let flag = unpacked & STREAM_ID_MASK == STREAM_ID_MASK;
// Now clear the most significant bit, as that is reserved and MUST be // Now clear the most significant bit, as that is reserved and MUST be
// ignored when received. // ignored when received.
StreamId(unpacked & !STREAM_ID_MASK) (StreamId(unpacked & !STREAM_ID_MASK), flag)
} }
pub fn is_client_initiated(&self) -> bool { pub fn is_client_initiated(&self) -> bool {