// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::{fmt::Debug, io::Write}; use neqo_common::{Decoder, Encoder}; use neqo_crypto::random; use neqo_transport::StreamId; use crate::{frames::reader::FrameDecoder, settings::HSettings, Error, Priority, Res}; pub(crate) type HFrameType = u64; pub const H3_FRAME_TYPE_DATA: HFrameType = 0x0; pub const H3_FRAME_TYPE_HEADERS: HFrameType = 0x1; pub const H3_FRAME_TYPE_CANCEL_PUSH: HFrameType = 0x3; pub const H3_FRAME_TYPE_SETTINGS: HFrameType = 0x4; pub const H3_FRAME_TYPE_PUSH_PROMISE: HFrameType = 0x5; pub const H3_FRAME_TYPE_GOAWAY: HFrameType = 0x7; pub const H3_FRAME_TYPE_MAX_PUSH_ID: HFrameType = 0xd; pub const H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST: HFrameType = 0xf0700; pub const H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH: HFrameType = 0xf0701; pub const H3_RESERVED_FRAME_TYPES: &[HFrameType] = &[0x2, 0x6, 0x8, 0x9]; // data for DATA frame is not read into HFrame::Data. #[derive(PartialEq, Eq, Debug)] pub enum HFrame { Data { len: u64, // length of the data }, Headers { header_block: Vec, }, CancelPush { push_id: u64, }, Settings { settings: HSettings, }, PushPromise { push_id: u64, header_block: Vec, }, Goaway { stream_id: StreamId, }, MaxPushId { push_id: u64, }, Grease, PriorityUpdateRequest { element_id: u64, priority: Priority, }, PriorityUpdatePush { element_id: u64, priority: Priority, }, } impl HFrame { fn get_type(&self) -> HFrameType { match self { Self::Data { .. } => H3_FRAME_TYPE_DATA, Self::Headers { .. } => H3_FRAME_TYPE_HEADERS, Self::CancelPush { .. } => H3_FRAME_TYPE_CANCEL_PUSH, Self::Settings { .. } => H3_FRAME_TYPE_SETTINGS, Self::PushPromise { .. } => H3_FRAME_TYPE_PUSH_PROMISE, Self::Goaway { .. } => H3_FRAME_TYPE_GOAWAY, Self::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID, Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST, Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH, Self::Grease => { let r = random(7); Decoder::from(&r).decode_uint(7).unwrap() * 0x1f + 0x21 } } } pub fn encode(&self, enc: &mut Encoder) { enc.encode_varint(self.get_type()); match self { Self::Data { len } => { // DATA frame only encode the length here. enc.encode_varint(*len); } Self::Headers { header_block } => { enc.encode_vvec(header_block); } Self::CancelPush { push_id } => { enc.encode_vvec_with(|enc_inner| { enc_inner.encode_varint(*push_id); }); } Self::Settings { settings } => { settings.encode_frame_contents(enc); } Self::PushPromise { push_id, header_block, } => { enc.encode_varint((header_block.len() + (Encoder::varint_len(*push_id))) as u64); enc.encode_varint(*push_id); enc.encode(header_block); } Self::Goaway { stream_id } => { enc.encode_vvec_with(|enc_inner| { enc_inner.encode_varint(stream_id.as_u64()); }); } Self::MaxPushId { push_id } => { enc.encode_vvec_with(|enc_inner| { enc_inner.encode_varint(*push_id); }); } Self::Grease => { // Encode some number of random bytes. let r = random(8); enc.encode_vvec(&r[1..usize::from(1 + (r[0] & 0x7))]); } Self::PriorityUpdateRequest { element_id, priority, } | Self::PriorityUpdatePush { element_id, priority, } => { let mut update_frame = Encoder::new(); update_frame.encode_varint(*element_id); let mut priority_enc: Vec = Vec::new(); write!(priority_enc, "{priority}").unwrap(); update_frame.encode(&priority_enc); enc.encode_varint(update_frame.len() as u64); enc.encode(update_frame.as_ref()); } } } } impl FrameDecoder for HFrame { fn frame_type_allowed(frame_type: u64) -> Res<()> { if H3_RESERVED_FRAME_TYPES.contains(&frame_type) { return Err(Error::HttpFrameUnexpected); } Ok(()) } fn decode(frame_type: u64, frame_len: u64, data: Option<&[u8]>) -> Res> { if frame_type == H3_FRAME_TYPE_DATA { Ok(Some(HFrame::Data { len: frame_len })) } else if let Some(payload) = data { let mut dec = Decoder::from(payload); Ok(match frame_type { H3_FRAME_TYPE_DATA => unreachable!("DATA frame has been handled already."), H3_FRAME_TYPE_HEADERS => Some(HFrame::Headers { header_block: dec.decode_remainder().to_vec(), }), H3_FRAME_TYPE_CANCEL_PUSH => Some(HFrame::CancelPush { push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, }), H3_FRAME_TYPE_SETTINGS => { let mut settings = HSettings::default(); settings.decode_frame_contents(&mut dec).map_err(|e| { if e == Error::HttpSettings { e } else { Error::HttpFrame } })?; Some(HFrame::Settings { settings }) } H3_FRAME_TYPE_PUSH_PROMISE => Some(HFrame::PushPromise { push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, header_block: dec.decode_remainder().to_vec(), }), H3_FRAME_TYPE_GOAWAY => Some(HFrame::Goaway { stream_id: StreamId::new(dec.decode_varint().ok_or(Error::HttpFrame)?), }), H3_FRAME_TYPE_MAX_PUSH_ID => Some(HFrame::MaxPushId { push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, }), H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST | H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH => { let element_id = dec.decode_varint().ok_or(Error::HttpFrame)?; let priority = dec.decode_remainder(); let priority = Priority::from_bytes(priority)?; if frame_type == H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST { Some(HFrame::PriorityUpdateRequest { element_id, priority, }) } else { Some(HFrame::PriorityUpdatePush { element_id, priority, }) } } _ => None, }) } else { Ok(None) } } fn is_known_type(frame_type: u64) -> bool { matches!( frame_type, H3_FRAME_TYPE_DATA | H3_FRAME_TYPE_HEADERS | H3_FRAME_TYPE_CANCEL_PUSH | H3_FRAME_TYPE_SETTINGS | H3_FRAME_TYPE_PUSH_PROMISE | H3_FRAME_TYPE_GOAWAY | H3_FRAME_TYPE_MAX_PUSH_ID | H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST | H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH ) } }