| // Copyright (C) 2021, Cloudflare, Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| use crate::Bytes; |
| use crate::Token; |
| use http3::*; |
| use quic::*; |
| |
| use serde::Deserialize; |
| use serde::Serialize; |
| |
| use std::collections::BTreeMap; |
| |
| pub type ExData = BTreeMap<String, serde_json::Value>; |
| |
| pub const LOGLEVEL_URI: &str = "urn:ietf:params:qlog:events:loglevel-09"; |
| |
| pub const QUIC_URI: &str = "urn:ietf:params:qlog:events:quic-08"; |
| pub const HTTP3_URI: &str = "urn:ietf:params:qlog:events:http3-08"; |
| |
| #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Default)] |
| #[serde(untagged)] |
| pub enum EventType { |
| QuicEventType(QuicEventType), |
| |
| SecurityEventType(SecurityEventType), |
| |
| Http3EventType(Http3EventType), |
| |
| LogLevelEventType(LogLevelEventType), |
| |
| #[default] |
| None, |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| pub enum TimeFormat { |
| Absolute, |
| Delta, |
| Relative, |
| } |
| |
| #[serde_with::skip_serializing_none] |
| #[derive(Serialize, Deserialize, Clone, Debug)] |
| pub struct Event { |
| pub time: f32, |
| |
| // Strictly, the qlog 02 spec says we should have a name field in the |
| // `Event` structure. However, serde's autogenerated Deserialize code |
| // struggles to read Events properly because the `EventData` types often |
| // alias. In order to work around that, we use can use a trick that will |
| // give serde autogen all the information that it needs while also produced |
| // a legal qlog. Specifically, strongly linking an EventData enum variant |
| // with the wire-format name. |
| // |
| // The trick is to use Adjacent Tagging |
| // (https://serde.rs/enum-representations.html#adjacently-tagged) with |
| // Struct flattening (https://serde.rs/attr-flatten.html). At a high level |
| // this first creates an `EventData` JSON object: |
| // |
| // {name: <enum variant name>, data: enum variant data } |
| // |
| // and then flattens those fields into the `Event` object. |
| #[serde(flatten)] |
| pub data: EventData, |
| |
| #[serde(flatten)] |
| pub ex_data: ExData, |
| |
| pub protocol_type: Option<String>, |
| pub group_id: Option<String>, |
| |
| pub time_format: Option<TimeFormat>, |
| |
| #[serde(skip)] |
| ty: EventType, |
| } |
| |
| impl Event { |
| /// Returns a new `Event` object with the provided time and data. |
| pub fn with_time(time: f32, data: EventData) -> Self { |
| Self::with_time_ex(time, data, Default::default()) |
| } |
| |
| /// Returns a new `Event` object with the provided time, data and ex_data. |
| pub fn with_time_ex(time: f32, data: EventData, ex_data: ExData) -> Self { |
| let ty = EventType::from(&data); |
| Event { |
| time, |
| data, |
| ex_data, |
| protocol_type: Default::default(), |
| group_id: Default::default(), |
| time_format: Default::default(), |
| ty, |
| } |
| } |
| } |
| |
| impl Eventable for Event { |
| fn importance(&self) -> EventImportance { |
| self.ty.into() |
| } |
| |
| fn set_time(&mut self, time: f32) { |
| self.time = time; |
| } |
| } |
| |
| impl PartialEq for Event { |
| // custom comparison to skip over the `ty` field |
| fn eq(&self, other: &Event) -> bool { |
| self.time == other.time && |
| self.data == other.data && |
| self.ex_data == other.ex_data && |
| self.protocol_type == other.protocol_type && |
| self.group_id == other.group_id && |
| self.time_format == other.time_format |
| } |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, Debug)] |
| pub struct JsonEvent { |
| pub time: f32, |
| |
| #[serde(skip)] |
| pub importance: EventImportance, |
| |
| pub name: String, |
| pub data: serde_json::Value, |
| } |
| |
| impl Eventable for JsonEvent { |
| fn importance(&self) -> EventImportance { |
| self.importance |
| } |
| |
| fn set_time(&mut self, time: f32) { |
| self.time = time; |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, Default)] |
| pub enum EventImportance { |
| #[default] |
| Core, |
| Base, |
| Extra, |
| } |
| |
| impl EventImportance { |
| /// Returns true if this importance level is included by `other`. |
| pub fn is_contained_in(&self, other: &EventImportance) -> bool { |
| match (other, self) { |
| (EventImportance::Core, EventImportance::Core) => true, |
| |
| (EventImportance::Base, EventImportance::Core) | |
| (EventImportance::Base, EventImportance::Base) => true, |
| |
| (EventImportance::Extra, EventImportance::Core) | |
| (EventImportance::Extra, EventImportance::Base) | |
| (EventImportance::Extra, EventImportance::Extra) => true, |
| |
| (..) => false, |
| } |
| } |
| } |
| |
| impl From<EventType> for EventImportance { |
| fn from(ty: EventType) -> Self { |
| match ty { |
| EventType::QuicEventType(QuicEventType::ServerListening) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::ConnectionStarted) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::ConnectionClosed) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::ConnectionIdUpdated) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::SpinBitUpdated) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::ConnectionStateUpdated) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::PathAssigned) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::MtuUpdated) => |
| EventImportance::Extra, |
| |
| EventType::SecurityEventType(SecurityEventType::KeyUpdated) => |
| EventImportance::Base, |
| EventType::SecurityEventType(SecurityEventType::KeyDiscarded) => |
| EventImportance::Base, |
| |
| EventType::QuicEventType(QuicEventType::VersionInformation) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::AlpnInformation) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::ParametersSet) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::ParametersRestored) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::UdpDatagramsReceived) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::UdpDatagramsSent) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::UdpDatagramDropped) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::PacketReceived) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::PacketSent) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::PacketDropped) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::PacketBuffered) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::PacketsAcked) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::StreamStateUpdated) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::FramesProcessed) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::StreamDataMoved) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::DatagramDataMoved) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::MigrationStateUpdated) => |
| EventImportance::Base, |
| |
| EventType::QuicEventType(QuicEventType::RecoveryParametersSet) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::RecoveryMetricsUpdated) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::CongestionStateUpdated) => |
| EventImportance::Base, |
| EventType::QuicEventType(QuicEventType::LossTimerUpdated) => |
| EventImportance::Extra, |
| EventType::QuicEventType(QuicEventType::PacketLost) => |
| EventImportance::Core, |
| EventType::QuicEventType(QuicEventType::MarkedForRetransmit) => |
| EventImportance::Extra, |
| |
| EventType::Http3EventType(Http3EventType::ParametersSet) => |
| EventImportance::Base, |
| EventType::Http3EventType(Http3EventType::StreamTypeSet) => |
| EventImportance::Base, |
| EventType::Http3EventType(Http3EventType::FrameCreated) => |
| EventImportance::Core, |
| EventType::Http3EventType(Http3EventType::FrameParsed) => |
| EventImportance::Core, |
| EventType::Http3EventType(Http3EventType::PushResolved) => |
| EventImportance::Extra, |
| |
| _ => unimplemented!(), |
| } |
| } |
| } |
| |
| pub trait Eventable { |
| fn importance(&self) -> EventImportance; |
| |
| fn set_time(&mut self, time: f32); |
| } |
| |
| impl From<&EventData> for EventType { |
| fn from(event_data: &EventData) -> Self { |
| match event_data { |
| EventData::ServerListening { .. } => |
| EventType::QuicEventType(QuicEventType::ServerListening), |
| EventData::ConnectionStarted { .. } => |
| EventType::QuicEventType(QuicEventType::ConnectionStarted), |
| EventData::ConnectionClosed { .. } => |
| EventType::QuicEventType(QuicEventType::ConnectionClosed), |
| EventData::ConnectionIdUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::ConnectionIdUpdated), |
| EventData::SpinBitUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::SpinBitUpdated), |
| EventData::ConnectionStateUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::ConnectionStateUpdated), |
| EventData::PathAssigned { .. } => |
| EventType::QuicEventType(QuicEventType::PathAssigned), |
| EventData::MtuUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::MtuUpdated), |
| |
| EventData::KeyUpdated { .. } => |
| EventType::SecurityEventType(SecurityEventType::KeyUpdated), |
| EventData::KeyDiscarded { .. } => |
| EventType::SecurityEventType(SecurityEventType::KeyDiscarded), |
| |
| EventData::VersionInformation { .. } => |
| EventType::QuicEventType(QuicEventType::VersionInformation), |
| EventData::AlpnInformation { .. } => |
| EventType::QuicEventType(QuicEventType::AlpnInformation), |
| EventData::ParametersSet { .. } => |
| EventType::QuicEventType(QuicEventType::ParametersSet), |
| EventData::ParametersRestored { .. } => |
| EventType::QuicEventType(QuicEventType::ParametersRestored), |
| EventData::UdpDatagramsReceived { .. } => |
| EventType::QuicEventType(QuicEventType::UdpDatagramsReceived), |
| EventData::UdpDatagramsSent { .. } => |
| EventType::QuicEventType(QuicEventType::UdpDatagramsSent), |
| EventData::UdpDatagramDropped { .. } => |
| EventType::QuicEventType(QuicEventType::UdpDatagramDropped), |
| EventData::PacketReceived { .. } => |
| EventType::QuicEventType(QuicEventType::PacketReceived), |
| EventData::PacketSent { .. } => |
| EventType::QuicEventType(QuicEventType::PacketSent), |
| EventData::PacketDropped { .. } => |
| EventType::QuicEventType(QuicEventType::PacketDropped), |
| EventData::PacketBuffered { .. } => |
| EventType::QuicEventType(QuicEventType::PacketBuffered), |
| EventData::PacketsAcked { .. } => |
| EventType::QuicEventType(QuicEventType::PacketsAcked), |
| EventData::StreamStateUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::StreamStateUpdated), |
| EventData::FramesProcessed { .. } => |
| EventType::QuicEventType(QuicEventType::FramesProcessed), |
| EventData::StreamDataMoved { .. } => |
| EventType::QuicEventType(QuicEventType::StreamDataMoved), |
| EventData::DatagramDataMoved { .. } => |
| EventType::QuicEventType(QuicEventType::DatagramDataMoved), |
| EventData::MigrationStateUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::MigrationStateUpdated), |
| |
| EventData::RecoveryParametersSet { .. } => |
| EventType::QuicEventType(QuicEventType::RecoveryParametersSet), |
| EventData::MetricsUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::RecoveryMetricsUpdated), |
| EventData::CongestionStateUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::CongestionStateUpdated), |
| EventData::LossTimerUpdated { .. } => |
| EventType::QuicEventType(QuicEventType::LossTimerUpdated), |
| EventData::PacketLost { .. } => |
| EventType::QuicEventType(QuicEventType::PacketLost), |
| EventData::MarkedForRetransmit { .. } => |
| EventType::QuicEventType(QuicEventType::MarkedForRetransmit), |
| |
| EventData::H3ParametersSet { .. } => |
| EventType::Http3EventType(Http3EventType::ParametersSet), |
| EventData::H3ParametersRestored { .. } => |
| EventType::Http3EventType(Http3EventType::ParametersRestored), |
| EventData::H3StreamTypeSet { .. } => |
| EventType::Http3EventType(Http3EventType::StreamTypeSet), |
| EventData::H3FrameCreated { .. } => |
| EventType::Http3EventType(Http3EventType::FrameCreated), |
| EventData::H3FrameParsed { .. } => |
| EventType::Http3EventType(Http3EventType::FrameParsed), |
| EventData::H3PushResolved { .. } => |
| EventType::Http3EventType(Http3EventType::PushResolved), |
| |
| EventData::LogLevelError { .. } => |
| EventType::LogLevelEventType(LogLevelEventType::Error), |
| EventData::LogLevelWarning { .. } => |
| EventType::LogLevelEventType(LogLevelEventType::Warning), |
| EventData::LogLevelInfo { .. } => |
| EventType::LogLevelEventType(LogLevelEventType::Info), |
| EventData::LogLevelDebug { .. } => |
| EventType::LogLevelEventType(LogLevelEventType::Debug), |
| EventData::LogLevelVerbose { .. } => |
| EventType::LogLevelEventType(LogLevelEventType::Verbose), |
| } |
| } |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| #[serde(rename_all = "snake_case")] |
| pub enum DataRecipient { |
| User, |
| Application, |
| Transport, |
| Network, |
| Dropped, |
| } |
| |
| #[serde_with::skip_serializing_none] |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| pub struct RawInfo { |
| pub length: Option<u64>, |
| pub payload_length: Option<u64>, |
| |
| pub data: Option<Bytes>, |
| } |
| |
| #[serde_with::skip_serializing_none] |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] |
| #[serde(tag = "name", content = "data")] |
| #[allow(clippy::large_enum_variant)] |
| pub enum EventData { |
| // Connectivity |
| #[serde(rename = "quic:server_listening")] |
| ServerListening(quic::ServerListening), |
| |
| #[serde(rename = "quic:connection_started")] |
| ConnectionStarted(quic::ConnectionStarted), |
| |
| #[serde(rename = "quic:connection_closed")] |
| ConnectionClosed(quic::ConnectionClosed), |
| |
| #[serde(rename = "quic:connection_id_updated")] |
| ConnectionIdUpdated(quic::ConnectionIdUpdated), |
| |
| #[serde(rename = "quic:spin_bit_updated")] |
| SpinBitUpdated(quic::SpinBitUpdated), |
| |
| #[serde(rename = "quic:connection_state_updated")] |
| ConnectionStateUpdated(quic::ConnectionStateUpdated), |
| |
| #[serde(rename = "quic:path_assigned")] |
| PathAssigned(quic::PathAssigned), |
| |
| #[serde(rename = "quic:mtu_updated")] |
| MtuUpdated(quic::MtuUpdated), |
| |
| // Security |
| #[serde(rename = "quic:key_updated")] |
| KeyUpdated(quic::KeyUpdated), |
| |
| #[serde(rename = "quic:key_retired")] |
| KeyDiscarded(quic::KeyDiscarded), |
| |
| // Transport |
| #[serde(rename = "quic:version_information")] |
| VersionInformation(quic::QuicVersionInformation), |
| |
| #[serde(rename = "quic:alpn_information")] |
| AlpnInformation(quic::AlpnInformation), |
| |
| #[serde(rename = "quic:parameters_set")] |
| ParametersSet(quic::ParametersSet), |
| |
| #[serde(rename = "quic:parameters_restored")] |
| ParametersRestored(quic::ParametersRestored), |
| |
| #[serde(rename = "quic:datagrams_received")] |
| UdpDatagramsReceived(quic::UdpDatagramsReceived), |
| |
| #[serde(rename = "quic:datagrams_sent")] |
| UdpDatagramsSent(quic::UdpDatagramsSent), |
| |
| #[serde(rename = "quic:datagram_dropped")] |
| UdpDatagramDropped(quic::UdpDatagramDropped), |
| |
| #[serde(rename = "quic:packet_received")] |
| PacketReceived(quic::PacketReceived), |
| |
| #[serde(rename = "quic:packet_sent")] |
| PacketSent(quic::PacketSent), |
| |
| #[serde(rename = "quic:packet_dropped")] |
| PacketDropped(quic::PacketDropped), |
| |
| #[serde(rename = "quic:packet_buffered")] |
| PacketBuffered(quic::PacketBuffered), |
| |
| #[serde(rename = "quic:packets_acked")] |
| PacketsAcked(quic::PacketsAcked), |
| |
| #[serde(rename = "quic:stream_state_updated")] |
| StreamStateUpdated(quic::StreamStateUpdated), |
| |
| #[serde(rename = "quic:frames_processed")] |
| FramesProcessed(quic::FramesProcessed), |
| |
| #[serde(rename = "quic:stream_data_moved")] |
| StreamDataMoved(quic::StreamDataMoved), |
| |
| #[serde(rename = "quic:datagram_data_moved")] |
| DatagramDataMoved(quic::DatagramDataMoved), |
| |
| #[serde(rename = "quic:migration_state_updated")] |
| MigrationStateUpdated(quic::MigrationStateUpdated), |
| |
| // Recovery |
| #[serde(rename = "quic:recovery_parameters_set")] |
| RecoveryParametersSet(quic::RecoveryParametersSet), |
| |
| #[serde(rename = "quic:recovery_metrics_updated")] |
| MetricsUpdated(quic::RecoveryMetricsUpdated), |
| |
| #[serde(rename = "quic:congestion_state_updated")] |
| CongestionStateUpdated(quic::CongestionStateUpdated), |
| |
| #[serde(rename = "quic:loss_timer_updated")] |
| LossTimerUpdated(quic::LossTimerUpdated), |
| |
| #[serde(rename = "quic:packet_lost")] |
| PacketLost(quic::PacketLost), |
| |
| #[serde(rename = "quic:marked_for_retransmit")] |
| MarkedForRetransmit(quic::MarkedForRetransmit), |
| |
| // HTTP/3 |
| #[serde(rename = "http3:parameters_set")] |
| H3ParametersSet(http3::ParametersSet), |
| |
| #[serde(rename = "http3:parameters_restored")] |
| H3ParametersRestored(http3::ParametersRestored), |
| |
| #[serde(rename = "http3:stream_type_set")] |
| H3StreamTypeSet(http3::StreamTypeSet), |
| |
| #[serde(rename = "http3:frame_created")] |
| H3FrameCreated(http3::FrameCreated), |
| |
| #[serde(rename = "http3:frame_parsed")] |
| H3FrameParsed(http3::FrameParsed), |
| |
| #[serde(rename = "http3:push_resolved")] |
| H3PushResolved(http3::PushResolved), |
| |
| // LogLevel |
| #[serde(rename = "loglevel:error")] |
| LogLevelError { |
| code: Option<u64>, |
| description: Option<String>, |
| }, |
| |
| #[serde(rename = "loglevel:warning")] |
| LogLevelWarning { |
| code: Option<u64>, |
| description: Option<String>, |
| }, |
| |
| #[serde(rename = "loglevel:info")] |
| LogLevelInfo { |
| code: Option<u64>, |
| description: Option<String>, |
| }, |
| |
| #[serde(rename = "loglevel:debug")] |
| LogLevelDebug { |
| code: Option<u64>, |
| description: Option<String>, |
| }, |
| |
| #[serde(rename = "loglevel:verbose")] |
| LogLevelVerbose { |
| code: Option<u64>, |
| description: Option<String>, |
| }, |
| } |
| |
| impl EventData { |
| /// Returns size of `EventData` array of `QuicFrame`s if it exists. |
| pub fn contains_quic_frames(&self) -> Option<usize> { |
| // For some EventData variants, the frame array is optional |
| // but for others it is mandatory. |
| match self { |
| EventData::PacketSent(pkt) => pkt.frames.as_ref().map(|f| f.len()), |
| |
| EventData::PacketReceived(pkt) => |
| pkt.frames.as_ref().map(|f| f.len()), |
| |
| EventData::PacketLost(pkt) => pkt.frames.as_ref().map(|f| f.len()), |
| |
| EventData::MarkedForRetransmit(ev) => Some(ev.frames.len()), |
| EventData::FramesProcessed(ev) => Some(ev.frames.len()), |
| |
| _ => None, |
| } |
| } |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)] |
| #[serde(rename_all = "snake_case")] |
| pub enum LogLevelEventType { |
| Error, |
| Warning, |
| Info, |
| Debug, |
| Verbose, |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| #[serde(untagged)] |
| pub enum ConnectionErrorCode { |
| TransportError(TransportError), |
| CryptoError(CryptoError), |
| Value(u64), |
| } |
| |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| #[serde(untagged)] |
| pub enum ApplicationErrorCode { |
| ApplicationError(ApplicationError), |
| Value(u64), |
| } |
| |
| // TODO |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| #[serde(rename_all = "snake_case")] |
| pub enum CryptoError { |
| Prefix, |
| } |
| |
| #[serde_with::skip_serializing_none] |
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] |
| pub struct PathEndpointInfo { |
| pub ip_v4: Option<String>, |
| pub ip_v6: Option<String>, |
| pub port_v4: Option<u16>, |
| pub port_v6: Option<u16>, |
| |
| pub connection_ids: Vec<Bytes>, |
| } |
| |
| pub mod http3; |
| pub mod quic; |