use crate::codec::write_buffer::WriteBuffer;
use crate::solicit::frame::flags::*;
use crate::solicit::frame::Frame;
use crate::solicit::frame::FrameBuilder;
use crate::solicit::frame::FrameHeader;
use crate::solicit::frame::FrameIR;
use crate::solicit::frame::ParseFrameError;
use crate::solicit::frame::ParseFrameResult;
use crate::solicit::frame::RawFrame;
use crate::solicit::stream_id::StreamId;
use crate::solicit::window_size::MAX_WINDOW_SIZE;
pub const SETTINGS_FRAME_TYPE: u8 = 0x4;
#[derive(Clone, PartialEq, Debug, Copy)]
pub enum HttpSetting {
HeaderTableSize(u32),
EnablePush(bool),
MaxConcurrentStreams(u32),
InitialWindowSize(u32),
MaxFrameSize(u32),
MaxHeaderListSize(u32),
}
impl HttpSetting {
pub fn from_id(id: u16, val: u32) -> ParseFrameResult<Option<HttpSetting>> {
Ok(Some(match id {
1 => HttpSetting::HeaderTableSize(val),
2 => {
let b = match val {
0 => false,
1 => true,
_ => return Err(ParseFrameError::IncorrectSettingsPushValue(val)),
};
HttpSetting::EnablePush(b)
}
3 => HttpSetting::MaxConcurrentStreams(val),
4 => {
if val > MAX_WINDOW_SIZE {
}
HttpSetting::InitialWindowSize(val)
}
5 => {
if val < 0x4000 || val >= 0x100_0000 {
return Err(ParseFrameError::IncorrectSettingsMaxFrameSize(val));
}
HttpSetting::MaxFrameSize(val)
}
6 => HttpSetting::MaxHeaderListSize(val),
_ => return Ok(None),
}))
}
fn parse_setting(raw_setting: &[u8]) -> ParseFrameResult<Option<HttpSetting>> {
let id: u16 = ((raw_setting[0] as u16) << 8) | (raw_setting[1] as u16);
let val: u32 = unpack_octets_4!(raw_setting, 2, u32);
HttpSetting::from_id(id, val)
}
pub fn get_id(&self) -> u16 {
match *self {
HttpSetting::HeaderTableSize(_) => 1,
HttpSetting::EnablePush(_) => 2,
HttpSetting::MaxConcurrentStreams(_) => 3,
HttpSetting::InitialWindowSize(_) => 4,
HttpSetting::MaxFrameSize(_) => 5,
HttpSetting::MaxHeaderListSize(_) => 6,
}
}
pub fn get_val(&self) -> u32 {
match *self {
HttpSetting::HeaderTableSize(val)
| HttpSetting::MaxConcurrentStreams(val)
| HttpSetting::InitialWindowSize(val)
| HttpSetting::MaxFrameSize(val)
| HttpSetting::MaxHeaderListSize(val) => val,
HttpSetting::EnablePush(true) => 1,
HttpSetting::EnablePush(false) => 0,
}
}
fn serialize(&self) -> [u8; 6] {
let (id, val) = (self.get_id(), self.get_val());
[
((id >> 8) & 0x00FF) as u8,
((id) & 0x00FF) as u8,
(((val >> 24) & 0x000000FF) as u8),
(((val >> 16) & 0x000000FF) as u8),
(((val >> 8) & 0x000000FF) as u8),
(((val) & 0x000000FF) as u8),
]
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct HttpSettings {
pub header_table_size: u32,
pub enable_push: bool,
pub max_concurrent_streams: u32,
pub initial_window_size: u32,
pub max_frame_size: u32,
pub max_header_list_size: u32,
}
impl HttpSettings {
pub fn apply(&mut self, setting: HttpSetting) {
match setting {
HttpSetting::HeaderTableSize(s) => self.header_table_size = s,
HttpSetting::EnablePush(e) => self.enable_push = e,
HttpSetting::MaxConcurrentStreams(m) => self.max_concurrent_streams = m,
HttpSetting::InitialWindowSize(s) => self.initial_window_size = s,
HttpSetting::MaxFrameSize(s) => self.max_frame_size = s,
HttpSetting::MaxHeaderListSize(s) => self.max_header_list_size = s,
}
}
pub fn apply_from_frame(&mut self, frame: &SettingsFrame) {
for s in &frame.settings {
self.apply(*s);
}
}
}
#[derive(Clone, PartialEq, Debug, Copy)]
pub enum SettingsFlag {
Ack = 0x1,
}
impl Flag for SettingsFlag {
#[inline]
fn bitmask(&self) -> u8 {
*self as u8
}
fn flags() -> &'static [Self] {
static FLAGS: &'static [SettingsFlag] = &[SettingsFlag::Ack];
FLAGS
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct SettingsFrame {
pub settings: Vec<HttpSetting>,
flags: Flags<SettingsFlag>,
}
impl SettingsFrame {
pub fn new() -> SettingsFrame {
SettingsFrame {
settings: Vec::new(),
flags: Flags::default(),
}
}
pub fn new_ack() -> SettingsFrame {
SettingsFrame {
settings: Vec::new(),
flags: SettingsFlag::Ack.to_flags(),
}
}
pub fn from_settings(settings: Vec<HttpSetting>) -> SettingsFrame {
SettingsFrame {
settings,
flags: Flags::default(),
}
}
pub fn add_setting(&mut self, setting: HttpSetting) {
self.settings.push(setting);
}
pub fn set_ack(&mut self) {
self.set_flag(SettingsFlag::Ack)
}
pub fn is_ack(&self) -> bool {
self.flags.is_set(SettingsFlag::Ack)
}
fn payload_len(&self) -> u32 {
6 * self.settings.len() as u32
}
fn parse_payload(payload: &[u8]) -> ParseFrameResult<Vec<HttpSetting>> {
if payload.len() % 6 != 0 {
return Err(ParseFrameError::ProtocolError);
}
let mut settings = Vec::new();
for chunk in payload.chunks(6) {
if let Some(setting) = HttpSetting::parse_setting(chunk)? {
settings.push(setting);
}
}
Ok(settings)
}
pub fn set_flag(&mut self, flag: SettingsFlag) {
self.flags.set(flag);
}
}
impl Frame for SettingsFrame {
type FlagType = SettingsFlag;
fn from_raw(raw_frame: &RawFrame) -> ParseFrameResult<SettingsFrame> {
let FrameHeader {
payload_len,
frame_type,
flags,
stream_id,
} = raw_frame.header();
if frame_type != SETTINGS_FRAME_TYPE {
return Err(ParseFrameError::InternalError);
}
if (payload_len as usize) != raw_frame.payload().len() {
return Err(ParseFrameError::InternalError);
}
if stream_id != 0 {
return Err(ParseFrameError::StreamIdMustBeNonZero);
}
if (flags & SettingsFlag::Ack.bitmask()) != 0 {
return if payload_len == 0 {
Ok(SettingsFrame {
settings: Vec::new(),
flags: Flags::new(flags),
})
} else {
Err(ParseFrameError::ProtocolError)
};
}
let settings = SettingsFrame::parse_payload(&raw_frame.payload())?;
Ok(SettingsFrame {
settings,
flags: Flags::new(flags),
})
}
fn flags(&self) -> Flags<SettingsFlag> {
self.flags
}
fn get_stream_id(&self) -> StreamId {
0
}
fn get_header(&self) -> FrameHeader {
FrameHeader {
payload_len: self.payload_len(),
frame_type: SETTINGS_FRAME_TYPE,
flags: self.flags.0,
stream_id: 0,
}
}
}
impl FrameIR for SettingsFrame {
fn serialize_into(self, b: &mut WriteBuffer) {
b.write_header(self.get_header());
for setting in &self.settings {
b.extend_from_slice(&setting.serialize());
}
}
}
#[cfg(test)]
mod tests {
use super::{HttpSetting, SettingsFrame};
use crate::solicit::frame::FrameIR;
use crate::solicit::frame::{pack_header, Frame, FrameHeader};
use crate::solicit::tests::common::raw_frame_from_parts;
#[test]
fn test_settings_frame_parse_no_ack_one_setting() {
let payload = [0, 1, 0, 0, 0, 1];
let header = FrameHeader::new(payload.len() as u32, 4, 0, 0);
let raw = raw_frame_from_parts(header.clone(), payload.to_vec());
let frame: SettingsFrame = Frame::from_raw(&raw).unwrap();
assert_eq!(frame.settings, vec![HttpSetting::HeaderTableSize(1)]);
assert_eq!(frame.get_header(), header);
}
#[test]
fn test_settings_frame_parse_no_ack_multiple_settings() {
let settings = vec![
HttpSetting::HeaderTableSize(1),
HttpSetting::MaxHeaderListSize(5),
HttpSetting::EnablePush(false),
];
let payload = {
let mut res: Vec<u8> = Vec::new();
for s in settings.iter().map(|s| s.serialize()) {
res.extend(s.to_vec().into_iter());
}
res
};
let header = FrameHeader::new(payload.len() as u32, 4, 0, 0);
let raw = raw_frame_from_parts(header.clone(), payload.to_vec());
let frame: SettingsFrame = Frame::from_raw(&raw).unwrap();
assert_eq!(frame.settings, settings);
assert_eq!(frame.get_header(), header);
assert!(!frame.is_ack());
}
#[test]
fn test_settings_frame_parse_no_ack_duplicate_settings() {
let settings = vec![
HttpSetting::HeaderTableSize(1),
HttpSetting::MaxHeaderListSize(5),
HttpSetting::EnablePush(false),
HttpSetting::HeaderTableSize(2),
];
let payload = {
let mut res: Vec<u8> = Vec::new();
for s in settings.iter().map(|s| s.serialize()) {
res.extend(s.to_vec().into_iter());
}
res
};
let header = FrameHeader::new(payload.len() as u32, 4, 0, 0);
let raw = raw_frame_from_parts(header.clone(), payload.to_vec());
let frame: SettingsFrame = Frame::from_raw(&raw).unwrap();
assert_eq!(frame.settings, settings);
assert_eq!(frame.get_header(), header);
assert!(!frame.is_ack());
}
#[test]
fn test_settings_frame_parse_no_ack_unknown_setting() {
let settings = vec![
HttpSetting::HeaderTableSize(1),
HttpSetting::MaxHeaderListSize(5),
];
let payload = {
let mut res: Vec<u8> = Vec::new();
for s in settings.iter().map(|s| s.serialize()) {
res.extend(s.to_vec().into_iter());
}
res.extend(vec![0, 10, 0, 0, 0, 0].into_iter());
for s in settings.iter().map(|s| s.serialize()) {
res.extend(s.to_vec().into_iter());
}
res
};
let header = FrameHeader::new(payload.len() as u32, 4, 0, 0);
let raw = raw_frame_from_parts(header.clone(), payload.to_vec());
let frame: SettingsFrame = Frame::from_raw(&raw).unwrap();
assert_eq!(frame.settings.len(), 4);
assert_eq!(&frame.settings[0..2], &settings[..]);
assert_eq!(&frame.settings[2..], &settings[..]);
assert!(!frame.is_ack());
}
#[test]
fn test_settings_frame_parse_ack_no_settings() {
let payload = [];
let header = FrameHeader {
payload_len: payload.len() as u32,
frame_type: 4,
flags: 1,
stream_id: 0,
};
let raw = raw_frame_from_parts(header.clone(), payload.to_vec());
let frame: SettingsFrame = Frame::from_raw(&raw).unwrap();
assert_eq!(frame.settings, vec![]);
assert_eq!(frame.get_header(), header);
assert!(frame.is_ack());
}
#[test]
fn test_settings_frame_parse_ack_with_settings() {
let settings = [HttpSetting::EnablePush(false)];
let payload = {
let mut res: Vec<u8> = Vec::new();
for s in settings.iter().map(|s| s.serialize()) {
res.extend(s.to_vec().into_iter());
}
res
};
let header = FrameHeader::new(payload.len() as u32, 4, 1, 0);
let raw = raw_frame_from_parts(header, payload);
let frame = SettingsFrame::from_raw(&raw);
assert!(frame.is_err());
}
#[test]
fn test_settings_frame_parse_not_stream_zero() {
let payload = vec![];
let header = FrameHeader::new(payload.len() as u32, 4, 1, 1);
let raw = raw_frame_from_parts(header, payload);
let frame = SettingsFrame::from_raw(&raw);
assert!(frame.is_err());
}
#[test]
fn test_settings_frame_parse_not_multiple_of_six() {
let payload = vec![1, 2, 3];
let header = FrameHeader::new(payload.len() as u32, 4, 0, 0);
let raw = raw_frame_from_parts(header, payload);
let frame = SettingsFrame::from_raw(&raw);
assert!(frame.is_err());
}
#[test]
fn test_settings_frame_serialize_no_ack_settings() {
let mut frame = SettingsFrame::new();
frame.add_setting(HttpSetting::EnablePush(false));
let expected = {
let mut res: Vec<u8> = Vec::new();
res.extend(
pack_header(&FrameHeader {
payload_len: 6,
frame_type: 4,
flags: 0,
stream_id: 0,
})
.to_vec()
.into_iter(),
);
res.extend(
HttpSetting::EnablePush(false)
.serialize()
.to_vec()
.into_iter(),
);
res
};
let serialized = frame.serialize_into_vec();
assert_eq!(serialized, expected);
}
#[test]
fn test_settings_frame_serialize_no_ack_multiple_settings() {
let mut frame = SettingsFrame::new();
frame.add_setting(HttpSetting::EnablePush(false));
frame.add_setting(HttpSetting::MaxHeaderListSize(0));
let expected = {
let mut res: Vec<u8> = Vec::new();
res.extend(
pack_header(&FrameHeader {
payload_len: 6 * 2,
frame_type: 4,
flags: 0,
stream_id: 0,
})
.to_vec()
.into_iter(),
);
res.extend(
HttpSetting::EnablePush(false)
.serialize()
.to_vec()
.into_iter(),
);
res.extend(
HttpSetting::MaxHeaderListSize(0)
.serialize()
.to_vec()
.into_iter(),
);
res
};
let serialized = frame.serialize_into_vec();
assert_eq!(serialized, expected);
}
#[test]
fn test_settings_frame_serialize_ack() {
let frame = SettingsFrame::new_ack();
let expected = pack_header(&FrameHeader {
payload_len: 0,
frame_type: 4,
flags: 1,
stream_id: 0,
})
.to_vec();
let serialized = frame.serialize_into_vec();
assert_eq!(serialized, expected);
}
#[test]
fn test_setting_deserialize() {
{
let buf = [0, 1, 0, 0, 1, 0];
let setting = HttpSetting::parse_setting(&buf).unwrap().unwrap();
assert_eq!(setting, HttpSetting::HeaderTableSize(1 << 8));
}
{
let buf = [0, 2, 0, 0, 0, 1];
let setting = HttpSetting::parse_setting(&buf).unwrap().unwrap();
assert_eq!(setting, HttpSetting::EnablePush(true));
}
{
let buf = [0, 3, 0, 0, 0, 0];
let setting = HttpSetting::parse_setting(&buf).unwrap().unwrap();
assert_eq!(setting, HttpSetting::MaxConcurrentStreams(0));
}
{
let buf = [0, 4, 0, 0, 0, 1];
let setting = HttpSetting::parse_setting(&buf).unwrap().unwrap();
assert_eq!(setting, HttpSetting::InitialWindowSize(1));
}
{
let buf = [0, 6, 0, 0, 0, 255];
let setting = HttpSetting::parse_setting(&buf).unwrap().unwrap();
assert_eq!(setting, HttpSetting::MaxHeaderListSize((1 << 8) - 1));
}
{
let buf = [0, 7, 0, 0, 0, 255];
let setting = HttpSetting::parse_setting(&buf).unwrap();
assert!(setting.is_none());
}
{
let buf = [0, 0, 0, 0, 0, 255];
let setting = HttpSetting::parse_setting(&buf).unwrap();
assert!(setting.is_none());
}
}
#[test]
fn test_setting_serialize() {
{
let buf = [0, 1, 0, 0, 1, 0];
let setting = HttpSetting::HeaderTableSize(1 << 8);
assert_eq!(buf, setting.serialize());
}
{
let buf = [0, 2, 0, 0, 0, 1];
let setting = HttpSetting::EnablePush(true);
assert_eq!(buf, setting.serialize());
}
{
let buf = [0, 3, 0, 0, 0, 0];
let setting = HttpSetting::MaxConcurrentStreams(0);
assert_eq!(buf, setting.serialize());
}
{
let buf = [0, 4, 0, 0, 0, 1];
let setting = HttpSetting::InitialWindowSize(1);
assert_eq!(buf, setting.serialize());
}
{
let buf = [0, 5, 0, 0, 0, 255];
let setting = HttpSetting::MaxFrameSize((1 << 8) - 1);
assert_eq!(buf, setting.serialize());
}
{
let buf = [0, 6, 0, 0, 0, 255];
let setting = HttpSetting::MaxHeaderListSize((1 << 8) - 1);
assert_eq!(buf, setting.serialize());
}
}
}