mod decoder;
mod encoder;
pub use decoder::{FrameHeader, decode_frame, decode_frame_header};
pub use encoder::{encode_frame, encode_frame_header, encoded_frame_len};
use core::fmt;
use std::collections::HashSet;
use crate::settings::{Setting, SettingError};
use crate::varint::VarInt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
pub enum FrameType {
Data = 0x00,
Headers = 0x01,
CancelPush = 0x03,
Settings = 0x04,
PushPromise = 0x05,
Goaway = 0x07,
MaxPushId = 0x0d,
}
impl FrameType {
pub fn from_type(t: u64) -> Option<Self> {
match t {
0x00 => Some(Self::Data),
0x01 => Some(Self::Headers),
0x03 => Some(Self::CancelPush),
0x04 => Some(Self::Settings),
0x05 => Some(Self::PushPromise),
0x07 => Some(Self::Goaway),
0x0d => Some(Self::MaxPushId),
_ => None,
}
}
pub fn is_http2_only(t: u64) -> bool {
matches!(t, 0x02 | 0x06 | 0x08 | 0x09)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Frame {
Data(DataPayload),
Headers(HeadersPayload),
Settings(SettingsPayload),
Goaway(GoawayPayload),
MaxPushId(VarInt),
Unknown(UnknownFrame),
}
impl Frame {
pub fn frame_type(&self) -> VarInt {
match self {
Self::Data(_) => VarInt::from_static(FrameType::Data as u64),
Self::Headers(_) => VarInt::from_static(FrameType::Headers as u64),
Self::Settings(_) => VarInt::from_static(FrameType::Settings as u64),
Self::Goaway(_) => VarInt::from_static(FrameType::Goaway as u64),
Self::MaxPushId(_) => VarInt::from_static(FrameType::MaxPushId as u64),
Self::Unknown(p) => p.frame_type(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnknownFrameError {
#[non_exhaustive]
KnownFrameType {
frame_type: VarInt,
},
#[non_exhaustive]
Http2OnlyFrameType {
frame_type: VarInt,
},
}
impl fmt::Display for UnknownFrameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::KnownFrameType { frame_type } => {
write!(
f,
"known HTTP/3 frame type {:#x} cannot be wrapped in Frame::Unknown",
frame_type.get()
)
}
Self::Http2OnlyFrameType { frame_type } => {
write!(
f,
"HTTP/2-only frame type {:#x} cannot be wrapped in Frame::Unknown",
frame_type.get()
)
}
}
}
}
impl core::error::Error for UnknownFrameError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownFrame {
frame_type: VarInt,
payload: Vec<u8>,
}
impl UnknownFrame {
pub fn new(frame_type: VarInt, payload: Vec<u8>) -> Result<Self, UnknownFrameError> {
let t = frame_type.get();
if FrameType::from_type(t).is_some() {
return Err(UnknownFrameError::KnownFrameType { frame_type });
}
if FrameType::is_http2_only(t) {
return Err(UnknownFrameError::Http2OnlyFrameType { frame_type });
}
Ok(Self {
frame_type,
payload,
})
}
pub const fn frame_type(&self) -> VarInt {
self.frame_type
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn into_payload(self) -> Vec<u8> {
self.payload
}
pub fn len(&self) -> usize {
self.payload.len()
}
pub fn is_empty(&self) -> bool {
self.payload.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataPayload {
data: Vec<u8>,
}
impl DataPayload {
pub fn new(data: Vec<u8>) -> Self {
Self { data }
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeadersPayload {
encoded_field_section: Vec<u8>,
}
impl HeadersPayload {
pub fn new(encoded_field_section: Vec<u8>) -> Self {
Self {
encoded_field_section,
}
}
pub fn encoded_field_section(&self) -> &[u8] {
&self.encoded_field_section
}
pub fn into_encoded_field_section(self) -> Vec<u8> {
self.encoded_field_section
}
pub fn len(&self) -> usize {
self.encoded_field_section.len()
}
pub fn is_empty(&self) -> bool {
self.encoded_field_section.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SettingsPayload {
settings: Vec<Setting>,
seen_ids: HashSet<VarInt>,
}
impl SettingsPayload {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, setting: Setting) -> Result<(), SettingError> {
let id = setting.id();
if !self.seen_ids.insert(id) {
return Err(SettingError::DuplicateId { id });
}
self.settings.push(setting);
Ok(())
}
pub fn settings(&self) -> &[Setting] {
&self.settings
}
pub fn len(&self) -> usize {
self.settings.len()
}
pub fn is_empty(&self) -> bool {
self.settings.is_empty()
}
pub fn from_settings(settings: &crate::settings::Settings) -> Self {
let mut payload = Self::new();
for setting in settings.iter() {
payload
.add(setting)
.expect("Settings::iter() yields unique IDs");
}
if let Some(wt) = &settings.wt_settings {
for setting in wt.iter() {
payload
.add(setting)
.expect("webtransport::Settings::iter() yields unique IDs");
}
}
let grease = crate::settings::Setting::from_wire(
crate::varint::VarInt::from_static(0x21),
crate::varint::VarInt::ZERO,
)
.expect("GREASE ID 0x21 は HTTP/2 専用 ID でも予約 ID でもないため from_wire は常に成功する(失敗時は実装バグ)");
payload.add(grease).expect(
"GREASE 設定 0x21 は既知 ID 群と競合しないため add は常に成功する(失敗時は実装バグ)",
);
payload
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GoawayPayload {
id: VarInt,
}
impl GoawayPayload {
pub const fn new(id: VarInt) -> Self {
Self { id }
}
pub const fn from_static(id: u64) -> Self {
Self {
id: VarInt::from_static(id),
}
}
pub const fn id(&self) -> VarInt {
self.id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_frame_type() {
let data = Frame::Data(DataPayload::new(vec![1, 2, 3]));
assert_eq!(data.frame_type().get(), 0x00);
let headers = Frame::Headers(HeadersPayload::new(vec![4, 5, 6]));
assert_eq!(headers.frame_type().get(), 0x01);
let settings = Frame::Settings(SettingsPayload::new());
assert_eq!(settings.frame_type().get(), 0x04);
let goaway = Frame::Goaway(GoawayPayload::from_static(100));
assert_eq!(goaway.frame_type().get(), 0x07);
let unknown = Frame::Unknown(
UnknownFrame::new(VarInt::from_static(0x21), vec![])
.expect("0x21 は RFC 9114 Section 7.2.8 の Reserved Frame Type"),
);
assert_eq!(unknown.frame_type().get(), 0x21);
}
#[test]
fn test_unknown_frame_rejects_all_known_types() {
for id in [0x00u64, 0x01, 0x03, 0x04, 0x05, 0x07, 0x0d] {
let frame_type = VarInt::from_static(id);
let err = UnknownFrame::new(frame_type, vec![]).unwrap_err();
assert_eq!(err, UnknownFrameError::KnownFrameType { frame_type });
}
}
#[test]
fn test_unknown_frame_rejects_all_http2_only_types() {
for id in [0x02u64, 0x06, 0x08, 0x09] {
let frame_type = VarInt::from_static(id);
let err = UnknownFrame::new(frame_type, vec![]).unwrap_err();
assert_eq!(err, UnknownFrameError::Http2OnlyFrameType { frame_type });
}
}
#[test]
fn test_unknown_frame_accepts_reserved_frame_type() {
let unknown = UnknownFrame::new(VarInt::from_static(0x21), vec![1, 2, 3])
.expect("0x21 は既知タイプでも HTTP/2 専用でもない");
assert_eq!(unknown.frame_type().get(), 0x21);
assert_eq!(unknown.payload(), &[1, 2, 3][..]);
}
}