/// This is only used by header decoder therefore all errors are `Error::Decompression`. /// A header block is read entirely before decoding it, therefore if there is not enough /// data in the buffer an error `Error::Decompression` will be return. pub(crate) struct ReceiverBufferWrapper<'a> {
buf: &'a [u8],
offset: usize,
}
impl ReadByte for ReceiverBufferWrapper<'_> { fn read_byte(&mutself) -> Res<u8> { ifself.offset == self.buf.len() {
Err(Error::Decompression)
} else { let b = self.buf[self.offset]; self.offset += 1;
Ok(b)
}
}
}
/// The function decodes varint with a prefixed, i.e. ignores `prefix_len` bits of the first /// byte. /// `ReceiverBufferWrapper` is only used for decoding header blocks. The header blocks are read /// entirely before a decoding starts, therefore any incomplete varint because of reaching the /// end of a buffer will be treated as the `Error::Decompression` error. pubfn read_prefixed_int(&mutself, prefix_len: u8) -> Res<u64> {
debug_assert!(prefix_len < 8);
/// Do not use `LiteralReader` here to avoid copying data. /// The function decoded a literal with a prefix: /// 1) ignores `prefix_len` bits of the first byte, /// 2) reads "huffman bit" /// 3) decode varint that is the length of a literal /// 4) reads the literal /// 5) performs huffman decoding if needed. /// /// `ReceiverBufferWrapper` is only used for decoding header blocks. The header blocks are read /// entirely before a decoding starts, therefore any incomplete varint or literal because of /// reaching the end of a buffer will be treated as the `Error::Decompression` error. pubfn read_literal_from_buffer(&mutself, prefix_len: u8) -> Res<Vec<u8>> {
debug_assert!(prefix_len < 7);
let first_byte = self.read_byte()?; let use_huffman = (first_byte & (0x80 >> prefix_len)) != 0; letmut int_reader = IntReader::new(first_byte, prefix_len + 1); let length: usize = int_reader
.read(self)?
.try_into()
.ok()
.filter(|&l| l <= LiteralReader::MAX_LEN)
.ok_or(Error::Decompression)?; if use_huffman {
huffman::decode(self.slice(length)?)
} else {
Ok(self.slice(length)?.to_vec())
}
}
fn slice(&mutself, len: usize) -> Res<&[u8]> { let end = self.offset.checked_add(len).ok_or(Error::Decompression)?; if end > self.buf.len() {
Err(Error::Decompression)
} else { let start = self.offset; self.offset = end;
Ok(&self.buf[start..self.offset])
}
}
}
/// This is varint reader that can take into account a prefix. #[derive(Debug)] #[expect(clippy::module_name_repetitions, reason = "This is OK.")] pubstruct IntReader {
value: u64,
cnt: u8,
done: bool,
}
impl IntReader { /// `IntReader` is created by supplying the first byte and prefix length. /// A varint may take only one byte, In that case already the first by has set state to done. /// /// # Panics /// /// When `prefix_len` is 8 or larger. #[must_use] pubfn new(first_byte: u8, prefix_len: u8) -> Self {
debug_assert!(prefix_len < 8, "prefix cannot larger than 7"); let mask = if prefix_len == 0 { 0xff
} else {
(1 << (8 - prefix_len)) - 1
}; let value = u64::from(first_byte & mask);
/// # Panics /// /// Never, but rust doesn't know that. #[must_use] pubfn make(first_byte: u8, prefixes: &[Prefix]) -> Self { for prefix in prefixes { if prefix.cmp_prefix(first_byte) { returnSelf::new(first_byte, prefix.len());
}
}
unreachable!();
}
/// This function reads bytes until the varint is decoded or until stream/buffer does not /// have any more date. /// /// # Errors /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow`, /// 3) Any `ReadByte`'s error pubfn read<R: ReadByte>(&mutself, s: &mut R) -> Res<u64> { letmut b: u8; while !self.done {
b = s.read_byte()?;
/// This is decoder of a literal with a prefix: /// 1) ignores `prefix_len` bits of the first byte, /// 2) reads "huffman bit" /// 3) decode varint that is the length of a literal /// 4) reads the literal /// 5) performs huffman decoding if needed. #[derive(Debug, Default)] #[expect(clippy::module_name_repetitions, reason = "This is OK.")] pubstruct LiteralReader {
state: LiteralReaderState,
literal: Vec<u8>,
use_huffman: bool,
}
impl LiteralReader { /// Maximum length for a literal string in QPACK encoding. /// /// RFC 9204 requires implementations to set their own limits for string literal /// lengths to prevent denial-of-service attacks. The RFC does not mandate a /// specific value, stating only that limits "SHOULD be large enough to process /// the largest individual field the HTTP implementation can be configured to /// accept." /// /// The Gecko limit is in `network.http.max_response_header_size` and defaults to /// 393216 bytes (384 KB), see `modules/libpref/init/StaticPrefList.yaml`. We use /// the same limit. pub(crate) const MAX_LEN: usize = 384 * 1024;
/// Creates `LiteralReader` with the first byte. This constructor is always used /// when a literal has a prefix. /// For literals without a prefix please use the default constructor. /// /// # Panics /// /// If `prefix_len` is 8 or more. #[must_use] pubfn new_with_first_byte(first_byte: u8, prefix_len: u8) -> Self {
assert!(prefix_len < 8); Self {
state: LiteralReaderState::ReadLength {
reader: IntReader::new(first_byte, prefix_len + 1),
},
literal: Vec::new(),
use_huffman: (first_byte & (0x80 >> prefix_len)) != 0,
}
}
/// This function reads bytes until the literal is decoded or until stream/buffer does not /// have any more date ready. /// /// # Errors /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow` /// 3) Any `ReadByte`'s error /// /// It returns value if reading the literal is done or None if it needs more data. /// /// # Panics /// /// When this object is complete. pubfn read<T: ReadByte + Reader>(&mutself, s: &mut T) -> Res<Vec<u8>> { loop {
qdebug!("state = {:?}", self.state); match &mutself.state {
LiteralReaderState::ReadHuffman => { let b = s.read_byte()?;
/// This is a helper function used only by `ReceiverBufferWrapper`, therefore it returns /// `Error::Decompression` if any error happens. /// /// # Errors /// /// If an parsing error occurred, the function returns `BadUtf8`. pubfn parse_utf8(v: &[u8]) -> Res<&str> {
str::from_utf8(v).map_err(|_| Error::BadUtf8)
}
#[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] pub(crate) mod test_receiver {
// data has not been received yet, reading IntReader will return Err(Error::NeedMoreData).
assert_eq!(reader.read(&mut test_receiver), Err(Error::NeedMoreData));
// Write one byte.
test_receiver.write(&buf[1..2]); // data has not been received yet, reading IntReader will return Err(Error::NeedMoreData).
assert_eq!(reader.read(&mut test_receiver), Err(Error::NeedMoreData));
// Write one byte.
test_receiver.write(&buf[2..]); // Now prefixed int is complete.
assert_eq!(reader.read(&mut test_receiver), Ok(*value));
}
#[test] fn read_non_utf8_huffman_literal() { // Test non-UTF8 data with Huffman encoding // 0xE4 is 'ä' in ISO-8859-1 (extended ASCII), which is invalid UTF-8 let non_utf8_data = &[0xE4u8]; let encoded = huffman::encode(non_utf8_data);
// Build a QPACK literal: [huffman_bit | length][data] // For prefix_len=3, the huffman bit is at position (0x80 >> 3) = 0x10 letmut buf = Vec::new(); #[expect(clippy::cast_possible_truncation, reason = "Test data is small")] let len = encoded.len() as u8;
buf.push(0x10 | len); // Huffman bit set + length
buf.extend_from_slice(&encoded);
letmut buffer = ReceiverBufferWrapper::new(&buf); let result = buffer.read_literal_from_buffer(3).unwrap();
assert_eq!(result, non_utf8_data);
}
#[test] fn read_non_utf8_plain_literal() { // Test non-UTF8 data without Huffman encoding // 0xFF, 0xFE are invalid UTF-8 sequences let non_utf8_data = &[0xFFu8, 0xFEu8];
// Build a QPACK literal without Huffman: [length][data] // For prefix_len=3, no huffman bit letmut buf = Vec::new(); #[expect(clippy::cast_possible_truncation, reason = "Test data is small")] let len = non_utf8_data.len() as u8;
buf.push(len); // No Huffman bit, just length
buf.extend_from_slice(non_utf8_data);
letmut buffer = ReceiverBufferWrapper::new(&buf); let result = buffer.read_literal_from_buffer(3).unwrap();
assert_eq!(result, non_utf8_data);
}
/// Create a [`LiteralReader`] and [`TestReceiver`] for a literal with the given length. fn literal_reader_for_test(literal_len: usize) -> (LiteralReader, TestReceiver) { const PREFIX_LEN: u8 = 3; letmut data = Encoder::default();
data.encode_literal( false,
Prefix::new(0x00, PREFIX_LEN),
&vec![b'a'; literal_len],
); let reader = LiteralReader::new_with_first_byte(data.as_ref()[0], PREFIX_LEN); letmut test_receiver = TestReceiver::default();
test_receiver.write(&data.as_ref()[1..]);
(reader, test_receiver)
}
/// Test that [`LiteralReader`] rejects literals exceeding [`MAX_LEN`]. /// /// This prevents denial-of-service attacks where a malicious QPACK encoder /// sends an extremely large length value to trigger excessive memory allocation. /// RFC 9204 requires implementations to set their own limits for string literal /// lengths. #[test] fn literal_exceeding_max_len_rejected() { let (mut reader, mut test_receiver) = literal_reader_for_test(LiteralReader::MAX_LEN + 1);
assert_eq!(reader.read(&mut test_receiver), Err(Error::Decoding));
}
/// Test that [`LiteralReader`] accepts literals at exactly [`MAX_LEN`]. #[test] fn literal_at_max_len_accepted() { let (mut reader, mut test_receiver) = literal_reader_for_test(LiteralReader::MAX_LEN); let result = reader.read(&mut test_receiver).unwrap();
assert_eq!(result.len(), LiteralReader::MAX_LEN);
}
#[test] fn buffer_wrapper_rejects_oversized_literal() { const PREFIX_LEN: u8 = 3; // Encode only the length field (MAX_LEN + 1) without allocating the actual data. // The validation should fail before attempting to read the literal content. letmut data = Encoder::default();
data.encode_prefixed_encoded_int(
Prefix::new(0x00, PREFIX_LEN + 1),
(LiteralReader::MAX_LEN + 1) as u64,
); letmut buffer = ReceiverBufferWrapper::new(data.as_ref());
assert_eq!(
buffer.read_literal_from_buffer(PREFIX_LEN),
Err(Error::Decompression)
);
}
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.