diff --git a/Cargo.toml b/Cargo.toml index e69cdb66..1adebb5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,8 +55,9 @@ tracing = { version = "0.1", default-features = false, features = [ fnv = "1.0.5" slab = "0.4.2" indexmap = { version = "2", features = ["std"] } -smallvec = "1.15.0" +smallvec = { version = "1.15.0", features = ["serde"] } parking_lot = { version = "0.12.4", optional = true } +serde = { version = "1.0", features = ["derive"] } [dev-dependencies] tracing = { version = "0.1", default-features = false, features = ["std"] } diff --git a/src/frame/headers.rs b/src/frame/headers.rs index 343490fb..530987ce 100644 --- a/src/frame/headers.rs +++ b/src/frame/headers.rs @@ -4,6 +4,8 @@ use crate::frame::{Error, Frame, Head, Kind}; use crate::hpack::{self, BytesStr}; use crate::tracing; +use serde::{Deserialize, Serialize}; + use http::header::{self, HeaderName, HeaderValue}; use http::{uri, HeaderMap, Method, Request, StatusCode, Uri}; @@ -96,6 +98,40 @@ define_enum_with_values! { } } +impl Serialize for PseudoId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(match self { + PseudoId::Method => "METHOD", + PseudoId::Scheme => "SCHEME", + PseudoId::Authority => "AUTHORITY", + PseudoId::Path => "PATH", + PseudoId::Protocol => "PROTOCOL", + PseudoId::Status => "STATUS", + }) + } +} + +impl<'de> Deserialize<'de> for PseudoId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "METHOD" => PseudoId::Method, + "SCHEME" => PseudoId::Scheme, + "AUTHORITY" => PseudoId::Authority, + "PATH" => PseudoId::Path, + "PROTOCOL" => PseudoId::Protocol, + "STATUS" => PseudoId::Status, + _ => return Err(serde::de::Error::custom("invalid pseudo-header")), + }) + } +} + /// Represents the order of HTTP/2 pseudo-header fields in a header block. /// /// This structure maintains an ordered list of pseudo-header fields (such as `:method`, `:scheme`, etc.) @@ -105,7 +141,7 @@ define_enum_with_values! { /// /// Typically, a `PseudoOrder` is constructed using the [`PseudoOrderBuilder`] to enforce uniqueness /// and protocol-compliant ordering. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct PseudoOrder { ids: SmallVec<[PseudoId; PseudoId::DEFAULT_STACK_SIZE]>, } diff --git a/src/frame/priority.rs b/src/frame/priority.rs index b71b0092..4f2d2d6f 100644 --- a/src/frame/priority.rs +++ b/src/frame/priority.rs @@ -3,13 +3,14 @@ use std::hash::Hash; use crate::frame::*; use crate::tracing; use bytes::BufMut; +use serde::{Deserialize, Serialize}; use smallvec::SmallVec; /// The PRIORITY frame (type=0x2) specifies the sender-advised priority /// of a stream [Section 5.3]. It can be sent in any stream state, /// including idle or closed streams. /// [Section 5.3]: -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct Priority { /// The stream ID of the stream that this priority frame is for stream_id: StreamId, @@ -30,7 +31,7 @@ pub struct Priority { /// In HTTP/2, stream dependencies form a dependency tree where each stream /// can depend on another stream. This creates a priority hierarchy that helps /// determine the relative order in which streams should be processed. -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct StreamDependency { /// The ID of the stream dependency target dependency_id: StreamId, @@ -168,7 +169,7 @@ const DEFAULT_STACK_SIZE: usize = 8; /// in HTTP/2. This is useful for pre-configuring stream priorities or /// sending multiple PRIORITY frames at once during connection setup or /// stream reprioritization. -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct Priorities { priorities: SmallVec<[Priority; DEFAULT_STACK_SIZE]>, max_stream_id: StreamId, diff --git a/src/frame/settings.rs b/src/frame/settings.rs index de47ae5d..c539915a 100644 --- a/src/frame/settings.rs +++ b/src/frame/settings.rs @@ -3,6 +3,8 @@ use std::fmt; use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId}; use crate::tracing; use bytes::{BufMut, BytesMut}; +#[cfg(feature = "unstable")] +use serde::{Deserialize, Serialize}; use smallvec::SmallVec; define_enum_with_values! { @@ -47,6 +49,45 @@ define_enum_with_values! { } } +impl Serialize for SettingId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(match self { + SettingId::HeaderTableSize => "HEADER_TABLE_SIZE", + SettingId::EnablePush => "ENABLE_PUSH", + SettingId::MaxConcurrentStreams => "MAX_CONCURRENT_STREAMS", + SettingId::InitialWindowSize => "INITIAL_WINDOW_SIZE", + SettingId::MaxFrameSize => "MAX_FRAME_SIZE", + SettingId::MaxHeaderListSize => "MAX_HEADER_LIST_SIZE", + SettingId::EnableConnectProtocol => "ENABLE_CONNECT_PROTOCOL", + SettingId::NoRfc7540Priorities => "NO_RFC7540_PRIORITIES", + _ => return Err(serde::ser::Error::custom("invalid setting id")), + }) + } +} + +impl<'de> Deserialize<'de> for SettingId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "HEADER_TABLE_SIZE" => SettingId::HeaderTableSize, + "ENABLE_PUSH" => SettingId::EnablePush, + "MAX_CONCURRENT_STREAMS" => SettingId::MaxConcurrentStreams, + "INITIAL_WINDOW_SIZE" => SettingId::InitialWindowSize, + "MAX_FRAME_SIZE" => SettingId::MaxFrameSize, + "MAX_HEADER_LIST_SIZE" => SettingId::MaxHeaderListSize, + "ENABLE_CONNECT_PROTOCOL" => SettingId::EnableConnectProtocol, + "NO_RFC7540_PRIORITIES" => SettingId::NoRfc7540Priorities, + _ => return Err(serde::de::Error::custom("invalid setting id")), + }) + } +} + /// Represents the order of settings in a SETTINGS frame. /// /// This structure maintains an ordered list of `SettingId` values for use when encoding or decoding @@ -56,7 +97,7 @@ define_enum_with_values! { /// /// Typically, a `SettingsOrder` is constructed using the [`SettingsOrderBuilder`] to enforce uniqueness /// and protocol-compliant ordering. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct SettingsOrder { ids: SmallVec<[SettingId; SettingId::DEFAULT_STACK_SIZE]>, } @@ -138,7 +179,7 @@ impl SettingsOrderBuilder { /// Any setting with a standard (known) ID will be ignored and not included in this collection. /// This allows for safe experimentation and extension without interfering with standard settings. #[cfg(feature = "unstable")] -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct ExperimentalSettings { settings: SmallVec<[Setting; SettingId::DEFAULT_STACK_SIZE]>, } @@ -244,7 +285,7 @@ pub struct Settings { /// frame. /// /// Each setting has a value that is a 32 bit unsigned integer (6.5.1.). -#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct Setting { id: SettingId, value: u32, diff --git a/src/frame/stream_id.rs b/src/frame/stream_id.rs index 0f427dfb..5b843841 100644 --- a/src/frame/stream_id.rs +++ b/src/frame/stream_id.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + /// A stream identifier, as described in [Section 5.1.1] of RFC 7540. /// /// Streams are identified with an unsigned 31-bit integer. Streams @@ -8,7 +10,7 @@ /// new stream. /// /// [Section 5.1.1]: https://tools.ietf.org/html/rfc7540#section-5.1.1 -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub struct StreamId(u32); #[derive(Debug, Copy, Clone)]