aboutsummaryrefslogtreecommitdiff
path: root/src/h3/frame.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/h3/frame.rs')
-rw-r--r--src/h3/frame.rs202
1 files changed, 200 insertions, 2 deletions
diff --git a/src/h3/frame.rs b/src/h3/frame.rs
index bee2b3b..085524b 100644
--- a/src/h3/frame.rs
+++ b/src/h3/frame.rs
@@ -39,6 +39,10 @@ pub const MAX_PUSH_FRAME_TYPE_ID: u64 = 0xD;
const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
const SETTINGS_MAX_HEADER_LIST_SIZE: u64 = 0x6;
const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
+const SETTINGS_H3_DATAGRAM: u64 = 0x276;
+
+// Permit between 16 maximally-encoded and 128 minimally-encoded SETTINGS.
+const MAX_SETTINGS_PAYLOAD_SIZE: usize = 256;
#[derive(Clone, PartialEq)]
pub enum Frame {
@@ -58,6 +62,7 @@ pub enum Frame {
max_header_list_size: Option<u64>,
qpack_max_table_capacity: Option<u64>,
qpack_blocked_streams: Option<u64>,
+ h3_datagram: Option<u64>,
grease: Option<(u64, u64)>,
},
@@ -146,6 +151,7 @@ impl Frame {
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ h3_datagram,
grease,
} => {
let mut len = 0;
@@ -165,6 +171,11 @@ impl Frame {
len += octets::varint_len(*val);
}
+ if let Some(val) = h3_datagram {
+ len += octets::varint_len(SETTINGS_H3_DATAGRAM);
+ len += octets::varint_len(*val);
+ }
+
if let Some(val) = grease {
len += octets::varint_len(val.0);
len += octets::varint_len(val.1);
@@ -188,6 +199,11 @@ impl Frame {
b.put_varint(*val as u64)?;
}
+ if let Some(val) = h3_datagram {
+ b.put_varint(SETTINGS_H3_DATAGRAM)?;
+ b.put_varint(*val as u64)?;
+ }
+
if let Some(val) = grease {
b.put_varint(val.0)?;
b.put_varint(val.1)?;
@@ -286,6 +302,12 @@ fn parse_settings_frame(
let mut max_header_list_size = None;
let mut qpack_max_table_capacity = None;
let mut qpack_blocked_streams = None;
+ let mut h3_datagram = None;
+
+ // Reject SETTINGS frames that are too long.
+ if settings_length > MAX_SETTINGS_PAYLOAD_SIZE {
+ return Err(super::Error::ExcessiveLoad);
+ }
while b.off() < settings_length {
let setting_ty = b.get_varint()?;
@@ -304,6 +326,18 @@ fn parse_settings_frame(
qpack_blocked_streams = Some(settings_val);
},
+ SETTINGS_H3_DATAGRAM => {
+ if settings_val > 1 {
+ return Err(super::Error::SettingsError);
+ }
+
+ h3_datagram = Some(settings_val);
+ },
+
+ // Reserved values overlap with HTTP/2 and MUST be rejected
+ 0x0 | 0x2 | 0x3 | 0x4 | 0x5 =>
+ return Err(super::Error::SettingsError),
+
// Unknown Settings parameters must be ignored.
_ => (),
}
@@ -313,6 +347,7 @@ fn parse_settings_frame(
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ h3_datagram,
grease: None,
})
}
@@ -425,10 +460,11 @@ mod tests {
max_header_list_size: Some(0),
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ h3_datagram: Some(0),
grease: None,
};
- let frame_payload_len = 6;
+ let frame_payload_len = 9;
let frame_header_len = 2;
let wire_len = {
@@ -457,6 +493,7 @@ mod tests {
max_header_list_size: Some(0),
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ h3_datagram: Some(0),
grease: Some((33, 33)),
};
@@ -465,10 +502,11 @@ mod tests {
max_header_list_size: Some(0),
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ h3_datagram: Some(0),
grease: None,
};
- let frame_payload_len = 8;
+ let frame_payload_len = 11;
let frame_header_len = 2;
let wire_len = {
@@ -497,6 +535,7 @@ mod tests {
max_header_list_size: Some(1024),
qpack_max_table_capacity: None,
qpack_blocked_streams: None,
+ h3_datagram: None,
grease: None,
};
@@ -522,6 +561,71 @@ mod tests {
}
#[test]
+ fn settings_h3_dgram_only() {
+ let mut d = [42; 128];
+
+ let frame = Frame::Settings {
+ max_header_list_size: None,
+ qpack_max_table_capacity: None,
+ qpack_blocked_streams: None,
+ h3_datagram: Some(1),
+ grease: None,
+ };
+
+ let frame_payload_len = 3;
+ let frame_header_len = 2;
+
+ let wire_len = {
+ let mut b = octets::OctetsMut::with_slice(&mut d);
+ frame.to_bytes(&mut b).unwrap()
+ };
+
+ assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len as u64,
+ &d[frame_header_len..]
+ )
+ .unwrap(),
+ frame
+ );
+ }
+
+ #[test]
+ fn settings_h3_dgram_bad() {
+ let mut d = [42; 128];
+
+ let frame = Frame::Settings {
+ max_header_list_size: None,
+ qpack_max_table_capacity: None,
+ qpack_blocked_streams: None,
+ h3_datagram: Some(5),
+ grease: None,
+ };
+
+ let frame_payload_len = 3;
+ let frame_header_len = 2;
+
+ let wire_len = {
+ let mut b = octets::OctetsMut::with_slice(&mut d);
+ frame.to_bytes(&mut b).unwrap()
+ };
+
+ assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len as u64,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+ }
+
+ #[test]
fn settings_qpack_only() {
let mut d = [42; 128];
@@ -529,6 +633,7 @@ mod tests {
max_header_list_size: None,
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ h3_datagram: None,
grease: None,
};
@@ -554,6 +659,99 @@ mod tests {
}
#[test]
+ fn settings_h2_prohibited() {
+ // We need to test the prohibited values (0x0 | 0x2 | 0x3 | 0x4 | 0x5)
+ // but the quiche API doesn't support that, so use a manually created
+ // frame data buffer where d[frame_header_len] is the SETTING type field.
+ let frame_payload_len = 2u64;
+ let frame_header_len = 2;
+ let mut d = [
+ SETTINGS_FRAME_TYPE_ID as u8,
+ frame_payload_len as u8,
+ 0x0,
+ 1,
+ ];
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+
+ d[frame_header_len] = 0x2;
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+
+ d[frame_header_len] = 0x3;
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+
+ d[frame_header_len] = 0x4;
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+
+ d[frame_header_len] = 0x5;
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::SettingsError)
+ );
+ }
+
+ #[test]
+ fn settings_too_big() {
+ // We need to test a SETTINGS frame that exceeds
+ // MAX_SETTINGS_PAYLOAD_SIZE, so just craft a special buffer that look
+ // likes the frame. The payload content doesn't matter since quiche
+ // should abort before then.
+ let frame_payload_len = MAX_SETTINGS_PAYLOAD_SIZE + 1;
+ let frame_header_len = 2;
+ let d = [
+ SETTINGS_FRAME_TYPE_ID as u8,
+ frame_payload_len as u8,
+ 0x1,
+ 1,
+ ];
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len as u64,
+ &d[frame_header_len..]
+ ),
+ Err(crate::h3::Error::ExcessiveLoad)
+ );
+ }
+
+ #[test]
fn push_promise() {
let mut d = [42; 128];