summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-http3/src/frames/hframe.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/neqo-http3/src/frames/hframe.rs')
-rw-r--r--third_party/rust/neqo-http3/src/frames/hframe.rs227
1 files changed, 227 insertions, 0 deletions
diff --git a/third_party/rust/neqo-http3/src/frames/hframe.rs b/third_party/rust/neqo-http3/src/frames/hframe.rs
new file mode 100644
index 0000000000..83e69ba894
--- /dev/null
+++ b/third_party/rust/neqo-http3/src/frames/hframe.rs
@@ -0,0 +1,227 @@
+// 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::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<u8>,
+ },
+ CancelPush {
+ push_id: u64,
+ },
+ Settings {
+ settings: HSettings,
+ },
+ PushPromise {
+ push_id: u64,
+ header_block: Vec<u8>,
+ },
+ 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<u8> = 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<HFrame> 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<Option<HFrame>> {
+ 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
+ )
+ }
+}