use bytes::{Buf, Bytes, BytesMut}; use http::header; use http::method::{self, Method}; use http::status::{self, StatusCode};
use std::cmp; use std::collections::VecDeque; use std::io::Cursor; use std::str::Utf8Error;
/// Decodes headers using HPACK #[derive(Debug)] pubstruct Decoder { // Protocol indicated that the max table size will update
max_size_update: Option<usize>,
last_max_update: usize,
table: Table,
buffer: BytesMut,
}
/// Represents all errors that can be encountered while performing the decoding /// of an HPACK header set. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pubenum DecoderError {
InvalidRepresentation,
InvalidIntegerPrefix,
InvalidTableIndex,
InvalidHuffmanCode,
InvalidUtf8,
InvalidStatusCode,
InvalidPseudoheader,
InvalidMaxDynamicSize,
IntegerOverflow,
NeedMore(NeedMore),
}
enum Representation { /// Indexed header field representation /// /// An indexed header field representation identifies an entry in either the /// static table or the dynamic table (see Section 2.3). /// /// # Header encoding /// /// ```text /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 1 | Index (7+) | /// +---+---------------------------+ /// ```
Indexed,
/// Literal Header Field with Incremental Indexing /// /// A literal header field with incremental indexing representation results /// in appending a header field to the decoded header list and inserting it /// as a new entry into the dynamic table. /// /// # Header encoding /// /// ```text /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 0 | 1 | Index (6+) | /// +---+---+-----------------------+ /// | H | Value Length (7+) | /// +---+---------------------------+ /// | Value String (Length octets) | /// +-------------------------------+ /// ```
LiteralWithIndexing,
/// Literal Header Field without Indexing /// /// A literal header field without indexing representation results in /// appending a header field to the decoded header list without altering the /// dynamic table. /// /// # Header encoding /// /// ```text /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 0 | 0 | 0 | 0 | Index (4+) | /// +---+---+-----------------------+ /// | H | Value Length (7+) | /// +---+---------------------------+ /// | Value String (Length octets) | /// +-------------------------------+ /// ```
LiteralWithoutIndexing,
/// Literal Header Field Never Indexed /// /// A literal header field never-indexed representation results in appending /// a header field to the decoded header list without altering the dynamic /// table. Intermediaries MUST use the same representation for encoding this /// header field. /// /// ```text /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 0 | 0 | 0 | 1 | Index (4+) | /// +---+---+-----------------------+ /// | H | Value Length (7+) | /// +---+---------------------------+ /// | Value String (Length octets) | /// +-------------------------------+ /// ```
LiteralNeverIndexed,
/// Dynamic Table Size Update /// /// A dynamic table size update signals a change to the size of the dynamic /// table. /// /// # Header encoding /// /// ```text /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 0 | 0 | 1 | Max size (5+) | /// +---+---------------------------+ /// ```
SizeUpdate,
}
/// Decodes the headers found in the given buffer. pubfn decode<F>(
&mutself,
src: &mut Cursor<&mut BytesMut>, mut f: F,
) -> Result<(), DecoderError> where
F: FnMut(Header),
{ useself::Representation::*;
let span = tracing::trace_span!("hpack::decode"); let _e = span.enter();
tracing::trace!("decode");
whilelet Some(ty) = peek_u8(src) { // At this point we are always at the beginning of the next block // within the HPACK data. The type of the block can always be // determined from the first byte. match Representation::load(ty)? {
Indexed => {
tracing::trace!(rem = src.remaining(), kind = %"Indexed");
can_resize = false; let entry = self.decode_indexed(src)?;
consume(src);
f(entry);
}
LiteralWithIndexing => {
tracing::trace!(rem = src.remaining(), kind = %"LiteralWithIndexing");
can_resize = false; let entry = self.decode_literal(src, true)?;
// Insert the header into the table self.table.insert(entry.clone());
consume(src);
if new_size > self.last_max_update { return Err(DecoderError::InvalidMaxDynamicSize);
}
tracing::debug!(
from = self.table.size(),
to = new_size, "Decoder changed max table size"
);
self.table.set_max_size(new_size);
Ok(())
}
fn decode_indexed(&self, buf: &mut Cursor<&mut BytesMut>) -> Result<Header, DecoderError> { let index = decode_int(buf, 7)?; self.table.get(index)
}
fn decode_literal(
&mutself,
buf: &mut Cursor<&mut BytesMut>,
index: bool,
) -> Result<Header, DecoderError> { let prefix = if index { 6 } else { 4 };
// Extract the table index for the name, or 0 if not indexed let table_idx = decode_int(buf, prefix)?;
// First, read the header name if table_idx == 0 { let old_pos = buf.position(); let name_marker = self.try_decode_string(buf)?; let value_marker = self.try_decode_string(buf)?;
buf.set_position(old_pos); // Read the name as a literal let name = name_marker.consume(buf); let value = value_marker.consume(buf);
Header::new(name, value)
} else { let e = self.table.get(table_idx)?; let value = self.decode_string(buf)?;
// The first bit in the first byte contains the huffman encoded flag. let huff = match peek_u8(buf) {
Some(hdr) => (hdr & HUFF_FLAG) == HUFF_FLAG,
None => return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream)),
};
// Decode the string length using 7 bit prefix let len = decode_int(buf, 7)?;
if len > buf.remaining() {
tracing::trace!(len, remaining = buf.remaining(), "decode_string underflow",); return Err(DecoderError::NeedMore(NeedMore::StringUnderflow));
}
let offset = (buf.position() - old_pos) as usize; if huff { let ret = { let raw = &buf.chunk()[..len];
huffman::decode(raw, &mutself.buffer).map(|buf| StringMarker {
offset,
len,
string: Some(BytesMut::freeze(buf)),
})
};
fn decode_int<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderError> { // The octet limit is chosen such that the maximum allowed *value* can // never overflow an unsigned 32-bit integer. The maximum value of any // integer that can be encoded with 5 octets is ~2^28 const MAX_BYTES: usize = 5; const VARINT_MASK: u8 = 0b0111_1111; const VARINT_FLAG: u8 = 0b1000_0000;
if !buf.has_remaining() { return Err(DecoderError::NeedMore(NeedMore::IntegerUnderflow));
}
let mask = if prefix_size == 8 { 0xFF
} else {
(1u8 << prefix_size).wrapping_sub(1)
};
letmut ret = (buf.get_u8() & mask) as usize;
if ret < mask as usize { // Value fits in the prefix bits return Ok(ret);
}
// The int did not fit in the prefix bits, so continue reading. // // The total number of bytes used to represent the int. The first byte was // the prefix, so start at 1. letmut bytes = 1;
// The rest of the int is stored as a varint -- 7 bits for the value and 1 // bit to indicate if it is the last byte. letmut shift = 0;
while buf.has_remaining() { let b = buf.get_u8();
bytes += 1;
ret += ((b & VARINT_MASK) as usize) << shift;
shift += 7;
if b & VARINT_FLAG == 0 { return Ok(ret);
}
if bytes == MAX_BYTES { // The spec requires that this situation is an error return Err(DecoderError::IntegerOverflow);
}
}
fn consume(buf: &mut Cursor<&mut BytesMut>) { // remove bytes from the internal BytesMut when they have been successfully // decoded. This is a more permanent cursor position, which will be // used to resume if decoding was only partial.
take(buf, 0);
}
/// Returns the entry located at the given index. /// /// The table is 1-indexed and constructed in such a way that the first /// entries belong to the static table, followed by entries in the dynamic /// table. They are merged into a single index address space, though. /// /// This is according to the [HPACK spec, section 2.3.3.] /// (http://http2.github.io/http2-spec/compression.html#index.address.space) pubfn get(&self, index: usize) -> Result<Header, DecoderError> { if index == 0 { return Err(DecoderError::InvalidTableIndex);
}
if index <= 61 { return Ok(get_static(index));
}
// Convert the index for lookup in the entries structure. matchself.entries.get(index - 62) {
Some(e) => Ok(e.clone()),
None => Err(DecoderError::InvalidTableIndex),
}
}
fn insert(&mutself, entry: Header) { let len = entry.len();
self.reserve(len);
ifself.size + len <= self.max_size { self.size += len;
// Track the entry self.entries.push_front(entry);
}
}
fn set_max_size(&mutself, size: usize) { self.max_size = size; // Make the table size fit within the new constraints. self.consolidate();
}
fn consolidate(&mutself) { whileself.size > self.max_size {
{ let last = matchself.entries.back() {
Some(x) => x,
None => { // Can never happen as the size of the table must reach // 0 by the time we've exhausted all elements.
panic!("Size of table != 0, but no headers left!");
}
};
#[test] fn test_decode_continuation_header_with_non_huff_encoded_name() { letmut de = Decoder::new(0); let value = huff_encode(b"bar"); letmut buf = BytesMut::new(); // header name is non_huff encoded
buf.extend([0b01000000, 3]);
buf.extend(b"foo"); // header value is partial
buf.extend([0x80 | 3]);
buf.extend(&value[0..1]);
letmut res = vec![]; let e = de
.decode(&mut Cursor::new(&mut buf), |h| {
res.push(h);
})
.unwrap_err(); // decode error because the header value is partial
assert_eq!(e, DecoderError::NeedMore(NeedMore::StringUnderflow));
// extend buf with the remaining header value
buf.extend(&value[1..]);
de.decode(&mut Cursor::new(&mut buf), |h| {
res.push(h);
})
.unwrap();
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.