const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4;
const HTTP2_FRAME_PRIORITY_LEN: usize = 1;
const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4;
+//TODO make this configurable
+pub const HTTP2_MAX_TABLESIZE: u32 = 0x10000; // 65536
#[repr(u8)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Debug)]
}
}
+pub struct HTTP2DynTable {
+ pub table: Vec<parser::HTTP2FrameHeaderBlock>,
+ pub current_size: usize,
+ pub max_size: usize,
+ pub overflow: u8,
+}
+
+impl HTTP2DynTable {
+ pub fn new() -> Self {
+ Self {
+ table: Vec::with_capacity(64),
+ current_size: 0,
+ max_size: 4096, //default value
+ overflow: 0,
+ }
+ }
+}
+
pub struct HTTP2State {
tx_id: u64,
request_frame_size: u32,
response_frame_size: u32,
- dynamic_headers_ts: Vec<parser::HTTP2FrameHeaderBlock>,
- dynamic_headers_tc: Vec<parser::HTTP2FrameHeaderBlock>,
+ dynamic_headers_ts: HTTP2DynTable,
+ dynamic_headers_tc: HTTP2DynTable,
transactions: Vec<HTTP2Transaction>,
progress: HTTP2ConnectionState,
pub files: HTTP2Files,
// the headers are encoded on one byte
// with a fixed number of static headers, and
// a variable number of dynamic headers
- dynamic_headers_ts: Vec::with_capacity(256 - parser::HTTP2_STATIC_HEADERS_NUMBER),
- dynamic_headers_tc: Vec::with_capacity(256 - parser::HTTP2_STATIC_HEADERS_NUMBER),
+ dynamic_headers_ts: HTTP2DynTable::new(),
+ dynamic_headers_tc: HTTP2DynTable::new(),
transactions: Vec::new(),
progress: HTTP2ConnectionState::Http2StateInit,
files: HTTP2Files::new(),
Some(parser::HTTP2FrameType::SETTINGS) => {
match parser::http2_parse_frame_settings(input) {
Ok((_, set)) => {
+ for i in 0..set.len() {
+ if set[i].id == parser::HTTP2SettingsId::SETTINGSHEADERTABLESIZE {
+ //set for both endpoints ? to be tested
+ self.dynamic_headers_tc.max_size = set[i].value as usize;
+ self.dynamic_headers_ts.max_size = set[i].value as usize;
+ if set[i].value > HTTP2_MAX_TABLESIZE {
+ //mark potential overflow
+ self.dynamic_headers_tc.overflow = 1;
+ self.dynamic_headers_ts.overflow = 1;
+ } else {
+ //reset in case peer set a lower value, to be tested
+ self.dynamic_headers_tc.overflow = 0;
+ self.dynamic_headers_ts.overflow = 0;
+ }
+ }
+ }
//we could set an event on remaining data
return HTTP2FrameTypeData::SETTINGS(set);
}
*/
use super::huffman;
+use crate::http2::http2::{HTTP2DynTable, HTTP2_MAX_TABLESIZE};
use nom::character::complete::digit1;
use nom::combinator::rest;
use nom::error::ErrorKind;
pub const HTTP2_STATIC_HEADERS_NUMBER: usize = 61;
-fn http2_frame_header_static(
- n: u64, dyn_headers: &Vec<HTTP2FrameHeaderBlock>,
-) -> Option<HTTP2FrameHeaderBlock> {
+fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP2FrameHeaderBlock> {
let (name, value) = match n {
1 => (":authority", ""),
2 => (":method", "GET"),
error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0,
sizeupdate: 0,
});
- } else if dyn_headers.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize {
+ } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize {
return Some(HTTP2FrameHeaderBlock {
name: Vec::new(),
value: Vec::new(),
sizeupdate: 0,
});
} else {
- let indyn = dyn_headers.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER);
+ let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER);
let headcopy = HTTP2FrameHeaderBlock {
- name: dyn_headers[indyn].name.to_vec(),
- value: dyn_headers[indyn].value.to_vec(),
+ name: dyn_headers.table[indyn].name.to_vec(),
+ value: dyn_headers.table[indyn].value.to_vec(),
error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
sizeupdate: 0,
};
}
fn http2_parse_headers_block_indexed<'a>(
- input: &'a [u8], dyn_headers: &Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
bits!(
}
fn http2_parse_headers_block_literal_common<'a>(
- input: &'a [u8], index: u64, dyn_headers: &Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], index: u64, dyn_headers: &HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
let (i3, name, error) = if index == 0 {
match http2_parse_headers_block_string(input) {
}
fn http2_parse_headers_block_literal_incindex<'a>(
- input: &'a [u8], dyn_headers: &mut Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
bits!(
error: head.error,
sizeupdate: 0,
};
- dyn_headers.push(headcopy);
- let mut dynsize = 0;
- //we may spend less CPU by storing in memory
- for i in 0..dyn_headers.len() {
- dynsize += 32 + dyn_headers[i].name.len() + dyn_headers[i].value.len();
- }
- //TODO keep track of settings + updates instead of magic default value
- while dynsize > 4096 {
- dynsize -= 32 + dyn_headers[0].name.len() + dyn_headers[0].value.len();
- dyn_headers.remove(0);
+ if head.error == HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess {
+ dyn_headers.current_size += 32 + headcopy.name.len() + headcopy.value.len();
+ //in case of overflow, best effort is to keep first headers
+ if dyn_headers.overflow > 0 {
+ if dyn_headers.overflow == 1 {
+ if dyn_headers.current_size <= (HTTP2_MAX_TABLESIZE as usize) {
+ //overflow had not yet happened
+ dyn_headers.table.push(headcopy);
+ } else if dyn_headers.current_size > dyn_headers.max_size {
+ //overflow happens, we cannot replace evicted headers
+ dyn_headers.overflow = 2;
+ }
+ }
+ } else {
+ dyn_headers.table.push(headcopy);
+ }
+ while dyn_headers.current_size > dyn_headers.max_size && dyn_headers.table.len() > 0
+ {
+ dyn_headers.current_size -=
+ 32 + dyn_headers.table[0].name.len() + dyn_headers.table[0].value.len();
+ dyn_headers.table.remove(0);
+ }
}
return Ok((r, head));
}
}
fn http2_parse_headers_block_literal_noindex<'a>(
- input: &'a [u8], dyn_headers: &Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
bits!(
}
fn http2_parse_headers_block_literal_neverindex<'a>(
- input: &'a [u8], dyn_headers: &Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
bits!(
return Ok((i3, varval));
}
-fn http2_parse_headers_block_dynamic_size(input: &[u8]) -> IResult<&[u8], HTTP2FrameHeaderBlock> {
+fn http2_parse_headers_block_dynamic_size<'a>(
+ input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
+) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
bits!(
input,
},
));
}
+ if (maxsize2 as usize) < dyn_headers.max_size {
+ dyn_headers.max_size = maxsize2 as usize;
+ //may evict entries
+ while dyn_headers.current_size > dyn_headers.max_size {
+ dyn_headers.current_size -=
+ 32 + dyn_headers.table[0].name.len() + dyn_headers.table[0].value.len();
+ dyn_headers.table.remove(0);
+ }
+ }
return Ok((
i3,
HTTP2FrameHeaderBlock {
}
fn http2_parse_headers_block<'a>(
- input: &'a [u8], dyn_headers: &mut Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
//caller garantees o have at least one byte
if input[0] & 0x80 != 0 {
} else if input[0] & 0x40 != 0 {
return http2_parse_headers_block_literal_incindex(input, dyn_headers);
} else if input[0] & 0x20 != 0 {
- return http2_parse_headers_block_dynamic_size(input);
+ return http2_parse_headers_block_dynamic_size(input, dyn_headers);
} else if input[0] & 0x10 != 0 {
return http2_parse_headers_block_literal_neverindex(input, dyn_headers);
} else {
const HTTP2_FLAG_HEADER_PRIORITY: u8 = 0x20;
pub fn http2_parse_frame_headers<'a>(
- input: &'a [u8], flags: u8, dyn_headers: &mut Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameHeaders> {
let (i2, padlength) = cond!(input, flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)?;
let (mut i3, priority) = cond!(
}
pub fn http2_parse_frame_push_promise<'a>(
- input: &'a [u8], flags: u8, dyn_headers: &mut Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FramePushPromise> {
let (i2, padlength) = cond!(input, flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)?;
let (mut i3, stream_id) = bits!(i2, tuple!(take_bits!(1u8), take_bits!(31u32)))?;
}
pub fn http2_parse_frame_continuation<'a>(
- input: &'a [u8], dyn_headers: &mut Vec<HTTP2FrameHeaderBlock>,
+ input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
) -> IResult<&'a [u8], HTTP2FrameContinuation> {
let mut i3 = input;
let mut blocks = Vec::new();
#[test]
fn test_http2_parse_header() {
let buf0: &[u8] = &[0x82];
- let mut dynh: Vec<HTTP2FrameHeaderBlock> =
- Vec::with_capacity(255 - HTTP2_STATIC_HEADERS_NUMBER);
+ let mut dynh = HTTP2DynTable::new();
let r0 = http2_parse_headers_block(buf0, &mut dynh);
match r0 {
Ok((remainder, hd)) => {
assert_eq!(hd.value, "*/*".as_bytes().to_vec());
// And we should have no bytes left.
assert_eq!(remainder.len(), 0);
- assert_eq!(dynh.len(), 1);
+ assert_eq!(dynh.table.len(), 1);
}
Err(Err::Incomplete(_)) => {
panic!("Result should not have been incomplete.");
assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec());
// And we should have no bytes left.
assert_eq!(remainder.len(), 0);
- assert_eq!(dynh.len(), 2);
+ assert_eq!(dynh.table.len(), 2);
}
Err(Err::Incomplete(_)) => {
panic!("Result should not have been incomplete.");
assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec());
// And we should have no bytes left.
assert_eq!(remainder.len(), 0);
- assert_eq!(dynh.len(), 2);
+ assert_eq!(dynh.table.len(), 2);
}
Err(Err::Incomplete(_)) => {
panic!("Result should not have been incomplete.");
Ok((remainder, hd)) => {
assert_eq!(hd.error, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0);
assert_eq!(remainder.len(), 0);
- assert_eq!(dynh.len(), 2);
+ assert_eq!(dynh.table.len(), 2);
}
Err(Err::Incomplete(_)) => {
panic!("Result should not have been incomplete.");
assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec());
// And we should have no bytes left.
assert_eq!(remainder.len(), 0);
- assert_eq!(dynh.len(), 2);
+ assert_eq!(dynh.table.len(), 2);
}
Err(Err::Incomplete(_)) => {
panic!("Result should not have been incomplete.");