use http::header::{self, HeaderName, HeaderValue}; use http::{uri, HeaderMap, Method, Request, StatusCode, Uri};
use bytes::{BufMut, Bytes, BytesMut};
use std::fmt; use std::io::Cursor;
type EncodeBuf<'a> = bytes::buf::Limit<&'a mut BytesMut>; /// Header frame /// /// This could be either a request or a response. #[derive(Eq, PartialEq)] pubstruct Headers { /// The ID of the stream with which this frame is associated.
stream_id: StreamId,
/// The stream dependency information, if any.
stream_dep: Option<StreamDependency>,
/// The header block fragment
header_block: HeaderBlock,
/// Loads the header frame but doesn't actually do HPACK decoding. /// /// HPACK decoding is done in the `load_hpack` step. pubfn load(head: Head, mut src: BytesMut) -> Result<(Self, BytesMut), Error> { let flags = HeadersFlag(head.flag()); letmut pad = 0;
if head.stream_id().is_zero() { return Err(Error::InvalidStreamId);
}
// Read the padding length if flags.is_padded() { if src.is_empty() { return Err(Error::MalformedMessage);
}
pad = src[0] as usize;
// Drop the padding let _ = src.split_to(1);
}
// Read the stream dependency let stream_dep = if flags.is_priority() { if src.len() < 5 { return Err(Error::MalformedMessage);
} let stream_dep = StreamDependency::load(&src[..5])?;
if stream_dep.dependency_id() == head.stream_id() { return Err(Error::InvalidDependencyId);
}
// Drop the next 5 bytes let _ = src.split_to(5);
Some(stream_dep)
} else {
None
};
if pad > 0 { if pad > src.len() { return Err(Error::TooMuchPadding);
}
pubfn encode( self,
encoder: &mut hpack::Encoder,
dst: &mut EncodeBuf<'_>,
) -> Option<Continuation> { // At this point, the `is_end_headers` flag should always be set
debug_assert!(self.flags.is_end_headers());
// Get the HEADERS frame head let head = self.head();
pubfn validate_request(req: &Request<()>) -> Result<(), PushPromiseHeaderError> { use PushPromiseHeaderError::*; // The spec has some requirements for promised request headers // [https://httpwg.org/specs/rfc7540.html#PushRequests]
// A promised request "that indicates the presence of a request body // MUST reset the promised stream with a stream error" iflet Some(content_length) = req.headers().get(header::CONTENT_LENGTH) { let parsed_length = parse_u64(content_length.as_bytes()); if parsed_length != Ok(0) { return Err(InvalidContentLength(parsed_length));
}
} // "The server MUST include a method in the :method pseudo-header field // that is safe and cacheable" if !Self::safe_and_cacheable(req.method()) { return Err(NotSafeAndCacheable);
}
pubfn encode( self,
encoder: &mut hpack::Encoder,
dst: &mut EncodeBuf<'_>,
) -> Option<Continuation> { // At this point, the `is_end_headers` flag should always be set
debug_assert!(self.flags.is_end_headers());
let head = self.head(); let promised_id = self.promised_id;
fn head(&self) -> Head {
Head::new(Kind::PushPromise, self.flags.into(), self.stream_id)
}
/// Consume `self`, returning the parts of the frame pubfn into_parts(self) -> (Pseudo, HeaderMap) {
(self.header_block.pseudo, self.header_block.fields)
}
}
// If the URI includes a scheme component, add it to the pseudo headers // // TODO: Scheme must be set... iflet Some(scheme) = parts.scheme {
pseudo.set_scheme(scheme);
}
// If the URI includes an authority component, add it to the pseudo // headers iflet Some(authority) = parts.authority {
pseudo.set_authority(BytesStr::from(authority.as_str()));
}
// At this point, we don't know how big the h2 frame will be. // So, we write the head with length 0, then write the body, and // finally write the length once we know the size.
head.encode(0, dst);
let payload_pos = dst.get_ref().len();
f(dst);
// Now, encode the header payload let continuation = ifself.hpack.len() > dst.remaining_mut() {
dst.put_slice(&self.hpack.split_to(dst.remaining_mut()));
// Compute the header block length let payload_len = (dst.get_ref().len() - payload_pos) as u64;
// Write the frame length let payload_len_be = payload_len.to_be_bytes();
assert!(payload_len_be[0..5].iter().all(|b| *b == 0));
(dst.get_mut()[head_pos..head_pos + 3]).copy_from_slice(&payload_len_be[5..]);
if continuation.is_some() { // There will be continuation frames, so the `is_end_headers` flag // must be unset
debug_assert!(dst.get_ref()[head_pos + 4] & END_HEADERS == END_HEADERS);
dst.get_mut()[head_pos + 4] -= END_HEADERS;
}
continuation
}
}
// ===== impl Iter =====
impl Iterator for Iter { type Item = hpack::Header<Option<HeaderName>>;
macro_rules! set_pseudo {
($field:ident, $val:expr) => {{ if reg {
tracing::trace!("load_hpack; header malformed -- pseudo not at head of block");
malformed = true;
} elseifself.pseudo.$field.is_some() {
tracing::trace!("load_hpack; header malformed -- repeated pseudo");
malformed = true;
} else { let __val = $val;
headers_size +=
decoded_header_size(stringify!($field).len() + 1, __val.as_str().len()); if headers_size < max_header_list_size { self.pseudo.$field = Some(__val);
} elseif !self.is_over_size {
tracing::trace!("load_hpack; header list size over max"); self.is_over_size = true;
}
}
}};
}
letmut cursor = Cursor::new(src);
// If the header frame is malformed, we still have to continue decoding // the headers. A malformed header frame is a stream level error, but // the hpack state is connection level. In order to maintain correct // state for other streams, the hpack decoding process must complete. let res = decoder.decode(&mut cursor, |header| { usecrate::hpack::Header::*;
match header {
Field { name, value } => { // Connection level header fields are not supported and must // result in a protocol error.
if name == header::CONNECTION
|| name == header::TRANSFER_ENCODING
|| name == header::UPGRADE
|| name == "keep-alive"
|| name == "proxy-connection"
{
tracing::trace!("load_hpack; connection level header");
malformed = true;
} elseif name == header::TE && value != "trailers" {
tracing::trace!( "load_hpack; TE header not set to trailers; val={:?}",
value
);
malformed = true;
} else {
reg = true;
/// Calculates the size of the currently decoded header list. /// /// According to http://httpwg.org/specs/rfc7540.html#SETTINGS_MAX_HEADER_LIST_SIZE /// /// > The value is based on the uncompressed size of header fields, /// > including the length of the name and value in octets plus an /// > overhead of 32 octets for each header field. fn calculate_header_list_size(&self) -> usize {
macro_rules! pseudo_size {
($name:ident) => {{ self.pseudo
.$name
.as_ref()
.map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len()))
.unwrap_or(0)
}};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.