// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0 > or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT >, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt::{
self , Display, Formatter};
use neqo_common::{Encoder, Header, qdebug};
use neqo_transport::{Connection, StreamId};
use crate ::{
Error, Res, Settings,
decoder_instructions::DecoderInstruction,
encoder_instructions::{DecodedEncoderInstruction, EncoderInstructionReader},
header_block::{HeaderDecoder, HeaderDecoderResult},
reader::{ReadByte, Reader, ReceiverConnWrapper},
stats::Stats,
table::HeaderTable,
};
pub const QPACK_UNI_STREAM_TYPE_DECODER: u64 =
0 x3;
#[ derive(Debug)]
pub struct Decoder {
instruction_reader: EncoderInstructionReader,
table: HeaderTable,
acked_inserts: u64,
max_entries: u64,
send_buf: Encoder,
local_stream_id: Option<StreamId>,
max_table_size: u64,
max_blocked_streams: usize,
blocked_streams: Vec<(StreamId, u64)>,
// stream_id and requested inserts count.
stats: Stats,
}
impl Decoder {
/// # Panics
///
/// If settings include invalid values.
#[ must_use]
pub fn new(qpack_settings: &Settings) ->
Self {
qdebug!(
"Decoder: creating a new qpack decoder" );
let mut send_buf = Encoder::default();
send_buf.encode_varint(QPACK_UNI_STREAM_TYPE_DECODER);
let max_blocked_streams = usize::from(qpack_settings.max_blocked_streams);
Self {
instruction_reader: EncoderInstructionReader::default(),
table: HeaderTable::new(
false ),
acked_inserts:
0 ,
max_entries: qpack_settings.max_table_size_decoder >>
5 ,
send_buf,
local_stream_id: None,
max_table_size: qpack_settings.max_table_size_decoder,
max_blocked_streams,
blocked_streams: Vec::with_capacity(max_blocked_streams),
stats: Stats::default(),
}
}
#[ must_use]
const fn capacity(&
self ) -> u64 {
self .table.capacity()
}
/// Returns a list of unblocked streams.
///
/// # Errors
///
/// May return: `ClosedCriticalStream` if stream has been closed or `EncoderStream`
/// in case of any other transport error.
pub fn receive(&
mut self , conn: &
mut Conne
ction, stream_id: StreamId) -> Res<Vec<StreamId>> {
let base_old = self .table.base();
self .read_instructions(conn, stream_id)
.map_err(|e| map_error(&e))?;
let base_new = self .table.base();
if base_old == base_new {
return Ok(Vec::new());
}
Ok(self
.blocked_streams
.extract_if(.., |(_, req)| *req <= base_new)
.map(|(id, _)| id)
.collect())
}
fn read_instructions(&mut self , conn: &mut Connection, stream_id: StreamId) -> Res<()> {
let mut recv = ReceiverConnWrapper::new(conn, stream_id);
self .process_instructions(&mut recv)
}
pub (crate ) fn process_instructions<T: ReadByte + Reader>(&mut self , recv: &mut T) -> Res<()> {
loop {
match self .instruction_reader.read_instructions(recv) {
Ok(instruction) => self .execute_instruction(instruction)?,
Err(Error::NeedMoreData) => break Ok(()),
Err(e) => break Err(e),
}
}
}
fn execute_instruction(&mut self , instruction: DecodedEncoderInstruction) -> Res<()> {
match instruction {
DecodedEncoderInstruction::Capacity { value } => self .set_capacity(value)?,
DecodedEncoderInstruction::InsertWithNameRefStatic { index, value } => {
Error::map_error(
self .table.insert_with_name_ref(true , index, &value),
Error::EncoderStream,
)?;
self .stats.dynamic_table_inserts += 1 ;
}
DecodedEncoderInstruction::InsertWithNameRefDynamic { index, value } => {
Error::map_error(
self .table.insert_with_name_ref(false , index, &value),
Error::EncoderStream,
)?;
self .stats.dynamic_table_inserts += 1 ;
}
DecodedEncoderInstruction::InsertWithNameLiteral { name, value } => {
Error::map_error(
self .table.insert(&name, &value).map(|_| ()),
Error::EncoderStream,
)?;
self .stats.dynamic_table_inserts += 1 ;
}
DecodedEncoderInstruction::Duplicate { index } => {
Error::map_error(self .table.duplicate(index), Error::EncoderStream)?;
self .stats.dynamic_table_inserts += 1 ;
}
DecodedEncoderInstruction::NoInstruction => {
unreachable!("This can be call only with an instruction" );
}
}
Ok(())
}
fn set_capacity(&mut self , cap: u64) -> Res<()> {
qdebug!("[{self}] received instruction capacity cap={cap}" );
if cap > self .max_table_size {
return Err(Error::EncoderStream);
}
self .table.set_capacity(cap)
}
fn header_ack(&mut self , stream_id: StreamId, required_inserts: u64) {
DecoderInstruction::HeaderAck { stream_id }.marshal(&mut self .send_buf);
if required_inserts > self .acked_inserts {
self .acked_inserts = required_inserts;
}
}
pub fn cancel_stream(&mut self , stream_id: StreamId) {
if self .table.capacity() > 0 {
self .blocked_streams.retain(|(id, _)| *id != stream_id);
DecoderInstruction::StreamCancellation { stream_id }.marshal(&>mut self .send_buf);
}
}
/// # Errors
///
/// May return [`Error::Internal`] if the decoder stream is not initialized,
/// or [`Error::DecoderStream`] if sending on the decoder stream fails.
///
/// # Panics
///
/// Never, but rust doesn't know that.
pub fn send(&mut self , conn: &mut Connection) -> Res<()> {
// Encode increment instruction if needed.
let increment = self .table.base() - self .acked_inserts;
if increment > 0 {
DecoderInstruction::InsertCountIncrement { increment }.marshal(&mut self .send_buf);
self .acked_inserts = self .table.base();
}
if !self .send_buf.is_empty() && self .local_stream_id.is_some() {
let r = conn
.stream_send(
self .local_stream_id.ok_or(Error::Internal)?,
self .send_buf.as_ref(),
)
.map_err(|_| Error::DecoderStream)?;
qdebug!("[{self}] {r} bytes sent" );
self .send_buf.skip(r);
}
Ok(())
}
/// # Errors
///
/// May return `Error::Decompression` if header block is incorrect or incomplete.
pub fn refers_dynamic_table(&self , buf: &[u8]) -> Res<bool> {
HeaderDecoder::new(buf).refers_dynamic_table(self .max_entries, self .table.base())
}
/// This function returns None if the stream is blocked waiting for table insertions.
/// 'buf' must contain the complete header block.
///
/// # Errors
///
/// May return `Error::Decompression` if header block is incorrect or incomplete.
///
/// # Panics
///
/// When there is a programming error.
pub fn decode_header_block(
&mut self ,
buf: &[u8],
stream_id: StreamId,
) -> Res<Option<Vec<Header>>> {
#[ cfg(feature = "build-fuzzing-corpus" )]
crate ::fuzz::write_item_to_fuzzing_corpus(stream_id, buf);
qdebug!("[{self}] decode header block" );
let mut decoder = HeaderDecoder::new(buf);
match decoder.decode_header_block(&self .table, self .max_entries, self .table.base()) {
Ok(HeaderDecoderResult::Blocked(req_insert_cnt)) => {
if self .blocked_streams.len() > self .max_blocked_streams {
Err(Error::Decompression)
} else {
let found = self
.blocked_streams
.iter()
.any(|(id, _req)| *id == stream_id);
if !found {
self .blocked_streams.push((stream_id, req_insert_cnt));
}
Ok(None)
}
}
Ok(HeaderDecoderResult::Headers(h)) => {
if decoder.get_req_insert_cnt() != 0 {
self .header_ack(stream_id, decoder.get_req_insert_cnt());
self .stats.dynamic_table_references += 1 ;
}
Ok(Some(h))
}
Err(_) => Err(Error::Decompression),
}
}
/// # Panics
///
/// When a stream has already been added.
pub fn add_send_stream(&mut self , stream_id: StreamId) {
assert!(
self .local_stream_id.is_none(),
"Adding multiple local streams"
);
self .local_stream_id = Some(stream_id);
}
#[ must_use]
pub const fn local_stream_id(&self ) -> Option<StreamId> {
self .local_stream_id
}
#[ must_use]
pub fn stats(&self ) -> Stats {
self .stats.clone()
}
}
impl Display for Decoder {
fn fmt(&self , f: &mut Formatter) -> fmt::Result {
write!(f, "QPack {}" , self .capacity())
}
}
fn map_error(err: &Error) -> Error {
if *err == Error::ClosedCriticalStream {
Error::ClosedCriticalStream
} else {
Error::EncoderStream
}
}
#[ cfg(test)]
#[ cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use neqo_common::Header;
use neqo_transport::{StreamId, StreamType};
use test_fixture::now;
use super ::{Connection, Decoder, Error, Res};
use crate ::Settings;
const STREAM_0: StreamId = StreamId::new(0 );
struct TestDecoder {
decoder: Decoder,
send_stream_id: StreamId,
recv_stream_id: StreamId,
conn: Connection,
peer_conn: Connection,
}
fn connect() -> TestDecoder {
let (mut conn, mut peer_conn) = test_fixture::connect();
// create a stream
let recv_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
let send_stream_id = conn.stream_create(StreamType::UniDi).unwrap();
// create a decoder
let mut decoder = Decoder::new(&Settings {
max_table_size_encoder: 0 ,
max_table_size_decoder: 300 ,
max_blocked_streams: 100 ,
});
decoder.add_send_stream(send_stream_id);
TestDecoder {
decoder,
send_stream_id,
recv_stream_id,
conn,
peer_conn,
}
}
fn recv_instruction(decoder: &mut TestDecoder, encoder_instruction: &[u8], res: &Res<()>) {
_ = decoder
.peer_conn
.stream_send(decoder.recv_stream_id, encoder_instruction)
.unwrap();
let out = decoder.peer_conn.process_output(now());
drop(decoder.conn.process(out.dgram(), now()));
assert_eq!(
decoder
.decoder
.read_instructions(&mut decoder.conn, decoder.recv_stream_id),
*res
);
}
fn send_instructions_and_check(decoder: &mut TestDecoder, decoder_instruction: &[u8]) {
decoder.decoder.send(&mut decoder.conn).unwrap();
let out = decoder.conn.process_output(now());
drop(decoder.peer_conn.process(out.dgram(), now()));
let mut buf = [0 _u8; 100 ];
let (amount, fin) = decoder
.peer_conn
.stream_recv(decoder.send_stream_id, &mut buf)
.unwrap();
assert!(!fin);
assert_eq!(&buf[..amount], decoder_instruction);
}
fn decode_headers(
decoder: &mut TestDecoder,
header_block: &[u8],
headers: &[Header],
stream_id: StreamId,
) {
let decoded_headers = decoder
.decoder
.decode_header_block(header_block, stream_id)
.unwrap();
let h = decoded_headers.unwrap();
assert_eq!(h, headers);
}
fn test_instruction(
capacity: u64,
instruction: &[u8],
res: &Res<()>,
decoder_instruction: &[u8],
check_capacity: u64,
) {
let mut decoder = connect();
if capacity > 0 {
assert!(decoder.decoder.set_capacity(capacity).is_ok());
}
// recv an instruction
recv_instruction(&mut decoder, instruction, res);
// send decoder instruction and check that is what we expect.
send_instructions_and_check(&mut decoder, decoder_instruction);
if check_capacity > 0 {
assert_eq!(decoder.decoder.capacity(), check_capacity);
}
}
// test insert_with_name_ref which fails because there is not enough space in the table
#[ test]
fn recv_insert_with_name_ref_1() {
test_instruction(
0 ,
&[0 xc4, 0 x04, 0 x31, 0 x32, 0 x33, 0 x34],
&Err(Error::EncoderStream),
&[0 x03],
0 ,
);
}
// test insert_name_ref that succeeds
#[ test]
fn recv_insert_with_name_ref_2() {
test_instruction(
100 ,
&[0 xc4, 0 x04, 0 x31, 0 x32, 0 x33, 0 x34],
&Ok(()),
&[0 x03, 0 x01],
0 ,
);
}
/// `dynamic_table_inserts` counts each insert instruction.
#[ test]
fn dynamic_table_inserts_stat_increments() {
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
assert_eq!(decoder.decoder.stats().dynamic_table_inserts, 0 );
recv_instruction(&mut decoder, &[0 xc4, 0 x04, 0 x31, 0 x32, 0 x33, 0 x34], &Ok(()));
assert_eq!(decoder.decoder.stats().dynamic_table_inserts, 1 );
recv_instruction(&mut decoder, &[0 xc4, 0 x04, 0 x35, 0 x36, 0 x37, 0 x38], &Ok(()));
assert_eq!(decoder.decoder.stats().dynamic_table_inserts, 2 );
}
// test insert with name literal - succeeds
#[ test]
fn recv_insert_with_name_litarel_2() {
test_instruction(
200 ,
&[
0 x4e, 0 x63, 0 x6f, 0 x6e, 0 x74, 0 x65, 0 x6e, 0 x74, 0 x2d, 0 x6c, 0 x65, 0 x6e, 0 x67, 0 x74,
0 x68, 0 x04, 0 x31, 0 x32, 0 x33, 0 x34,
],
&Ok(()),
&[0 x03, 0 x01],
0 ,
);
}
#[ test]
fn recv_change_capacity() {
test_instruction(0 , &[0 x3f, 0 xa9, 0 x01], &Ok(()), &[0 x03], 200 );
}
#[ test]
fn recv_change_capacity_too_big() {
test_instruction(
0 ,
&[0 x3f, 0 xf1, 0 x02],
&Err(Error::EncoderStream),
&[0 x03],
0 ,
);
}
// this test tests header decoding, the header acks command and the insert count increment
// command.
#[ test]
fn duplicate() {
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(100 ).is_ok());
// receive an instruction
recv_instruction(
&mut decoder,
&[
0 x4e, 0 x63, 0 x6f, 0 x6e, 0 x74, 0 x65, 0 x6e, 0 x74, 0 x2d, 0 x6c, 0 x65, 0 x6e, 0 x67, 0 x74,
0 x68, 0 x04, 0 x31, 0 x32, 0 x33, 0 x34,
],
&Ok(()),
);
// receive the second instruction, a duplicate instruction.
recv_instruction(&mut decoder, &[0 x00], &Ok(()));
send_instructions_and_check(&mut decoder, &[0 x03, 0 x02]);
}
struct TestElement {
pub headers: Vec<Header>,
pub header_block: &'static [u8],
pub encoder_inst: &'static [u8],
}
#[ test]
fn encode_incr_encode_header_ack_some() {
// 1. Decoder receives an instruction (header and value both as literal)
// 2. Decoder process the instruction and sends an increment instruction.
// 3. Decoder receives another two instruction (header and value both as literal) and a
// header block.
// 4. Now it sends only a header ack and an increment instruction with increment==1.
let headers = vec![
Header::new("my-headera" , "my-valuea" ),
Header::new("my-headerb" , "my-valueb" ),
];
let header_block = &[0 x03, 0 x81, 0 x10, 0 x11];
let first_encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61,
];
let second_encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62, 0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61,
0 x64, 0 x65, 0 x72, 0 x63, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x63,
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, first_encoder_inst, &Ok(()));
send_instructions_and_check(&mut decoder, &[0 x03, 0 x1]);
recv_instruction(&mut decoder, second_encoder_inst, &Ok(()));
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
send_instructions_and_check(&mut decoder, &[0 x80, 0 x1]);
}
#[ test]
fn encode_incr_encode_header_ack_all() {
// 1. Decoder receives an instruction (header and value both as literal)
// 2. Decoder process the instruction and sends an increment instruction.
// 3. Decoder receives another instruction (header and value both as literal) and a header
// block.
// 4. Now it sends only a header ack.
let headers = vec![
Header::new("my-headera" , "my-valuea" ),
Header::new("my-headerb" , "my-valueb" ),
];
let header_block = &[0 x03, 0 x81, 0 x10, 0 x11];
let first_encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61,
];
let second_encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62,
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, first_encoder_inst, &Ok(()));
send_instructions_and_check(&mut decoder, &[0 x03, 0 x1]);
recv_instruction(&mut decoder, second_encoder_inst, &Ok(()));
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
send_instructions_and_check(&mut decoder, &[0 x80]);
}
#[ test]
fn header_ack_all() {
// Send two instructions to insert values into the dynamic table and then send a header
// that references them both. The result should be only a header acknowledgement.
let headers = vec![
Header::new("my-headera" , "my-valuea" ),
Header::new("my-headerb" , "my-valueb" ),
];
let header_block = &[0 x03, 0 x81, 0 x10, 0 x11];
let encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61, 0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61,
0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62,
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, encoder_inst, &Ok(()));
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
send_instructions_and_check(&mut decoder, &[0 x03, 0 x80]);
}
#[ test]
fn header_ack_and_incr_instruction() {
// Send two instructions to insert values into the dynamic table and then send a header
// that references only the first. The result should be a header acknowledgement and a
// increment instruction.
let headers = vec![Header::new("my-headera" , "my-valuea" )];
let header_block = &[0 x02, 0 x80, 0 x10];
let encoder_inst = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61, 0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61,
0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62,
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, encoder_inst, &Ok(()));
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
send_instructions_and_check(&mut decoder, &[0 x03, 0 x80, 0 x01]);
}
#[ test]
fn header_block_decoder() {
let test_cases: [TestElement; 6 ] = [
// test a header with ref to static - encode_indexed
TestElement {
headers: vec![Header::new(":method" , "GET" )],
header_block: &[0 x00, 0 x00, 0 xd1],
encoder_inst: &[],
},
// test encode_literal_with_name_ref
TestElement {
headers: vec![Header::new(":path" , "/somewhere" )],
header_block: &[
0 x00, 0 x00, 0 x51, 0 x0a, 0 x2f, 0 x73, 0 x6f, 0 x6d, 0 x65, 0 x77, 0 x68, 0 x65, 0 x72,
0 x65,
],
encoder_inst: &[],
},
// test adding a new header and encode_post_base_index, also test
// fix_header_block_prefix
TestElement {
headers: vec![Header::new("my-header" , "my-value" )],
header_block: &[0 x02, 0 x80, 0 x10],
encoder_inst: &[
0 x49, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x08, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65,
],
},
// test encode_indexed with a ref to dynamic table.
TestElement {
headers: vec![Header::new("my-header" , "my-value" )],
header_block: &[0 x02, 0 x00, 0 x80],
encoder_inst: &[],
},
// test encode_literal_with_name_ref.
TestElement {
headers: vec![Header::new("my-header" , "my-value2" )],
header_block: &[
0 x02, 0 x00, 0 x40, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x32,
],
encoder_inst: &[],
},
// test multiple headers
TestElement {
headers: vec![
Header::new(":method" , "GET" ),
Header::new(":path" , "/somewhere" ),
Header::new(":authority" , "example.com" ),
Header::new(":scheme" , "https" ),
],
header_block: &[
0 x00, 0 x01, 0 xd1, 0 x51, 0 x0a, 0 x2f, 0 x73, 0 x6f, 0 x6d, 0 x65, 0 x77, 0 x68, 0 x65,
0 x72, 0 x65, 0 x50, 0 x0b, 0 x65, 0 x78, 0 x61, 0 x6d, 0 x70, 0 x6c, 0 x65, 0 x2e, 0 x63,
0 x6f, 0 x6d, 0 xd7,
],
encoder_inst: &[],
},
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
for (i, t) in test_cases.iter().enumerate() {
// receive an instruction
if !t.encoder_inst.is_empty() {
recv_instruction(&mut decoder, t.encoder_inst, &Ok(()));
}
decode_headers(
&mut decoder,
t.header_block,
&t.headers,
StreamId::from(u64::try_from(i).unwrap()),
);
}
// test header acks and the insert count increment command
send_instructions_and_check(&mut decoder, &[0 x03, 0 x82, 0 x83, 0 x84]);
}
#[ test]
fn header_block_decoder_huffman() {
let test_cases: [TestElement; 6 ] = [
// test a header with ref to static - encode_indexed
TestElement {
headers: vec![Header::new(":method" , "GET" )],
header_block: &[0 x00, 0 x00, 0 xd1],
encoder_inst: &[],
},
// test encode_literal_with_name_ref
TestElement {
headers: vec![Header::new(":path" , "/somewhere" )],
header_block: &[
0 x00, 0 x00, 0 x51, 0 x87, 0 x61, 0 x07, 0 xa4, 0 xbe, 0 x27, 0 x2d, 0 x85,
],
encoder_inst: &[],
},
// test adding a new header and encode_post_base_index, also test
// fix_header_block_prefix
TestElement {
headers: vec![Header::new("my-header" , "my-value" )],
header_block: &[0 x02, 0 x80, 0 x10],
encoder_inst: &[
0 x67, 0 xa7, 0 xd2, 0 xd3, 0 x94, 0 x72, 0 x16, 0 xcf, 0 x86, 0 xa7, 0 xd2, 0 xdd, 0 xc7,
0 x45, 0 xa5,
],
},
// test encode_indexed with a ref to dynamic table.
TestElement {
headers: vec![Header::new("my-header" , "my-value" )],
header_block: &[0 x02, 0 x00, 0 x80],
encoder_inst: &[],
},
// test encode_literal_with_name_ref.
TestElement {
headers: vec![Header::new("my-header" , "my-value2" )],
header_block: &[
0 x02, 0 x00, 0 x40, 0 x87, 0 xa7, 0 xd2, 0 xdd, 0 xc7, 0 x45, 0 xa5, 0 x17,
],
encoder_inst: &[],
},
// test multiple headers
TestElement {
headers: vec![
Header::new(":method" , "GET" ),
Header::new(":path" , "/somewhere" ),
Header::new(":authority" , "example.com" ),
Header::new(":scheme" , "https" ),
],
header_block: &[
0 x00, 0 x01, 0 xd1, 0 x51, 0 x87, 0 x61, 0 x07, 0 xa4, 0 xbe, 0 x27, 0 x2d, 0 x85, 0 x50,
0 x88, 0 x2f, 0 x91, 0 xd3, 0 x5d, 0 x05, 0 x5c, 0 x87, 0 xa7, 0 xd7,
],
encoder_inst: &[],
},
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
for (i, t) in test_cases.iter().enumerate() {
// receive an instruction.
if !t.encoder_inst.is_empty() {
recv_instruction(&mut decoder, t.encoder_inst, &Ok(()));
}
decode_headers(
&mut decoder,
t.header_block,
&t.headers,
StreamId::from(u64::try_from(i).unwrap()),
);
}
// test header acks and the insert count increment command
send_instructions_and_check(&mut decoder, &[0 x03, 0 x82, 0 x83, 0 x84]);
}
#[ test]
fn subtract_overflow_in_header_ack() {
const HEADER_BLOCK_1: &[u8] = &[0 x03, 0 x81, 0 x10, 0 x11];
const ENCODER_INST: &[u8] = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61, 0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61,
0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62,
];
const HEADER_BLOCK_2: &[u8] = &[0 x02, 0 x80, 0 x10];
// Test for issue https://github.com/mozilla/neqo/issues/475
// Send two instructions to insert values into the dynamic table and send a header
// that references them both. This will increase number of acked inserts in the table
// to 2. Then send a header that references only one of them which shouldn't increase
// number of acked inserts.
let headers = vec![
Header::new("my-headera" , "my-valuea" ),
Header::new("my-headerb" , "my-valueb" ),
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, ENCODER_INST, &Ok(()));
decode_headers(&mut decoder, HEADER_BLOCK_1, &headers, STREAM_0);
let headers = vec![Header::new("my-headera" , "my-valuea" )];
decode_headers(&mut decoder, HEADER_BLOCK_2, &headers, STREAM_0);
}
#[ test]
fn base_larger_than_entry_count() {
// Test for issue https://github.com/mozilla/neqo/issues/533
// Send instruction that inserts 2 fields into the dynamic table and send a header that
// uses base larger than 2.
const ENCODER_INST: &[u8] = &[
0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61, 0 x64, 0 x65, 0 x72, 0 x61, 0 x09, 0 x6d, 0 x79,
0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x61, 0 x4a, 0 x6d, 0 x79, 0 x2d, 0 x68, 0 x65, 0 x61,
0 x64, 0 x65, 0 x72, 0 x62, 0 x09, 0 x6d, 0 x79, 0 x2d, 0 x76, 0 x61, 0 x6c, 0 x75, 0 x65, 0 x62,
];
const HEADER_BLOCK: &[u8] = &[0 x03, 0 x03, 0 x83, 0 x84];
let headers = vec![
Header::new("my-headerb" , "my-valueb" ),
Header::new("my-headera" , "my-valuea" ),
];
let mut decoder = connect();
assert!(decoder.decoder.set_capacity(200 ).is_ok());
recv_instruction(&mut decoder, ENCODER_INST, &Ok(()));
decode_headers(&mut decoder, HEADER_BLOCK, &headers, STREAM_0);
}
}
Messung V0.5 in Prozent C=64 H=94 G=80
¤ Dauer der Verarbeitung: 0.10 Sekunden
¤
*© Formatika GbR, Deutschland