use serde::Deserialize;
use serde::Serialize;
use smallvec::SmallVec;
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Event {
pub time: f32,
#[serde(flatten)]
pub data: EventData,
pub time_format: Option<TimeFormat>,
pub protocol_type: Option<String>,
pub group_id: Option<String>,
}
impl Event {
pub fn new(time: f32, data: EventData) -> Self {
Event {
time,
data,
time_format: Default::default(),
protocol_type: Default::default(),
group_id: Default::default(),
}
}
pub fn importance(&self) -> EventImportance {
self.data.importance()
}
}
impl PartialEq for Event {
fn eq(&self, other: &Event) -> bool {
self.time == other.time
&& self.data == other.data
&& self.protocol_type == other.protocol_type
&& self.group_id == other.group_id
&& self.time_format == other.time_format
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(tag = "name", content = "data")]
#[allow(clippy::large_enum_variant)]
pub enum EventData {
#[serde(rename = "connectivity:server_listening")]
ConnectivityServerListening {
ip_v4: Option<String>,
ip_v6: Option<String>,
port_v4: Option<u16>,
port_v6: Option<u16>,
retry_required: Option<bool>,
},
#[serde(rename = "connectivity:connection_started")]
ConnectivityConnectionStarted {
ip_version: Option<String>, src_ip: String,
dst_ip: String,
protocol: Option<String>,
src_port: Option<u16>,
dst_port: Option<u16>,
src_cid: Option<String>,
dst_cid: Option<String>,
},
#[serde(rename = "connectivity:connection_closed")]
ConnectivityConnectionClosed {
owner: Option<Owner>, connection_code: Option<ConnectionErrorCode>,
application_code: Option<ApplicationErrorCode>,
internal_code: Option<u32>,
reason: Option<String>,
trigger: Option<ConnectionClosedTrigger>,
},
#[serde(rename = "connectivity:connection_id_updated")]
ConnectivityConnectionIdUpdated {
owner: Option<Owner>,
old: Option<String>,
new: Option<String>,
},
#[serde(rename = "connectivity:spin_bit_updated")]
ConnectivitySpinBitUpdated { state: bool },
#[serde(rename = "connectivity:connection_state_updated")]
ConnectivityConnectionStateUpdated {
old: Option<ConnectionState>,
new: ConnectionState,
},
#[serde(rename = "connectivity:mtu_updated")]
ConnectivityMtuUpdated {
old: Option<u16>,
new: u16,
done: Option<bool>,
},
#[serde(rename = "quic:version_information")]
QuicVersionInformation {
server_versions: Option<Vec<String>>,
client_versions: Option<Vec<String>>,
chosen_version: Option<String>,
},
#[serde(rename = "quic:alpn_information")]
QuicAlpnInformation {
server_alpns: Option<Vec<String>>,
client_alpns: Option<Vec<String>>,
chosen_alpn: Option<String>,
},
#[serde(rename = "quic:parameters_set")]
QuicParametersSet {
owner: Option<Owner>,
resumption_allowed: Option<bool>,
early_data_enabled: Option<bool>,
tls_cipher: Option<String>,
original_destination_connection_id: Option<String>,
initial_source_connection_id: Option<String>,
retry_source_connection_id: Option<String>,
stateless_reset_token: Option<String>,
disable_active_migration: Option<bool>,
max_idle_timeout: Option<u64>,
max_udp_payload_size: Option<u32>,
ack_delay_exponent: Option<u16>,
max_ack_delay: Option<u16>,
active_connection_id_limit: Option<u32>,
initial_max_data: Option<u64>,
initial_max_stream_data_bidi_local: Option<u64>,
initial_max_stream_data_bidi_remote: Option<u64>,
initial_max_stream_data_uni: Option<u64>,
initial_max_streams_bidi: Option<u64>,
initial_max_streams_uni: Option<u64>,
preferred_address: Option<PreferredAddress>,
max_datagram_frame_size: Option<u64>,
grease_quic_bit: Option<bool>,
},
#[serde(rename = "quic:parameters_restored")]
QuicParametersRestored {
disable_active_migration: Option<bool>,
max_idle_timeout: Option<u64>,
max_udp_payload_size: Option<u32>,
active_connection_id_limit: Option<u32>,
initial_max_data: Option<u64>,
initial_max_stream_data_bidi_local: Option<u64>,
initial_max_stream_data_bidi_remote: Option<u64>,
initial_max_stream_data_uni: Option<u64>,
initial_max_streams_bidi: Option<u64>,
initial_max_streams_uni: Option<u64>,
},
#[serde(rename = "quic:packet_sent")]
QuicPacketSent {
header: PacketHeader,
frames: Option<SmallVec<[QuicFrame; 1]>>,
is_coalesced: Option<bool>,
retry_token: Option<Token>,
stateless_reset_token: Option<String>,
supported_versions: Option<Vec<String>>,
raw: Option<RawInfo>,
datagram_id: Option<u32>,
is_mtu_probe_packet: Option<bool>,
trigger: Option<PacketSentTrigger>,
},
#[serde(rename = "quic:packet_received")]
QuicPacketReceived {
header: PacketHeader,
frames: Option<SmallVec<[QuicFrame; 1]>>,
is_coalesced: Option<bool>,
retry_token: Option<Token>,
stateless_reset_token: Option<String>,
supported_versions: Option<Vec<String>>,
raw: Option<RawInfo>,
datagram_id: Option<u32>,
trigger: Option<PacketReceivedTrigger>,
},
#[serde(rename = "quic:packet_dropped")]
QuicPacketDropped {
header: Option<PacketHeader>,
raw: Option<RawInfo>,
datagram_id: Option<u32>,
details: Option<String>,
trigger: Option<PacketDroppedTrigger>,
},
#[serde(rename = "quic:packet_buffered")]
QuicPacketBuffered {
header: Option<PacketHeader>,
raw: Option<RawInfo>,
datagram_id: Option<u32>,
trigger: Option<PacketBufferedTrigger>,
},
#[serde(rename = "quic:packets_acked")]
QuicPacketsAcked {
packet_number_space: Option<PacketNumberSpace>,
packet_numbers: Option<Vec<u64>>,
},
#[serde(rename = "quic:datagrams_sent")]
QuicDatagramsSent {
count: Option<u16>, raw: Option<Vec<RawInfo>>, datagram_ids: Option<Vec<u32>>,
},
#[serde(rename = "quic:datagrams_received")]
QuicDatagramsReceived {
count: Option<u16>,
raw: Option<Vec<RawInfo>>,
ecn: Option<Vec<Ecn>>,
datagram_ids: Option<Vec<u32>>,
},
#[serde(rename = "quic:datagram_dropped")]
QuicDatagramDropped { raw: Option<RawInfo> },
#[serde(rename = "quic:stream_state_updated")]
QuicStreamStateUpdated {
stream_id: u64,
stream_type: Option<StreamType>,
old: Option<StreamState>,
new: StreamState,
stream_side: Option<StreamSide>,
},
#[serde(rename = "quic:frames_processed")]
QuicFramesProcessed {
frames: Vec<QuicFrame>,
packet_number: Option<u64>,
},
#[serde(rename = "quic:stream_data_moved")]
QuicStreamDataMoved {
stream_id: Option<u64>,
offset: Option<u64>,
length: Option<u64>,
from: Option<DataRecipient>,
to: Option<DataRecipient>,
raw: Option<RawInfo>,
},
#[serde(rename = "quic:datagram_data_moved")]
QuicDatagramDataMoved {
length: Option<u64>,
from: Option<DataRecipient>,
to: Option<DataRecipient>,
raw: Option<RawInfo>,
},
#[serde(rename = "security:key_updated")]
SecurityKeyUpdated {
key_type: KeyType,
old: Option<String>,
new: String,
generation: Option<u32>, trigger: Option<KeyUpdateOrRetiredTrigger>,
},
#[serde(rename = "security:key_retired")]
SecurityKeyDiscarded {
key_type: KeyType,
key: Option<String>,
generation: Option<u32>,
trigger: Option<KeyUpdateOrRetiredTrigger>, },
#[serde(rename = "recovery:parameters_set")]
RecoveryParametersSet {
reordering_threshold: Option<u16>,
time_threshold: Option<f32>,
timer_granularity: Option<u16>,
initial_rtt: Option<f32>,
max_datagram_size: Option<u32>,
initial_congestion_window: Option<u64>,
minimum_congestion_window: Option<u32>,
loss_reduction_factor: Option<f32>,
persistent_congestion_threshold: Option<u16>,
},
#[serde(rename = "recovery:metrics_updated")]
RecoveryMetricsUpdated {
min_rtt: Option<f32>,
smoothed_rtt: Option<f32>,
latest_rtt: Option<f32>,
rtt_variance: Option<f32>,
pto_count: Option<u16>,
congestion_window: Option<u64>,
bytes_in_flight: Option<u64>,
ssthresh: Option<u64>,
packets_in_flight: Option<u64>,
pacing_rate: Option<u64>,
},
#[serde(rename = "recovery:congestion_state_updated")]
RecoveryCongestionStateUpdated {
old: Option<String>,
new: String,
trigger: Option<CongestionStateUpdatedTrigger>,
},
#[serde(rename = "recovery:loss_timer_updated")]
RecoveryLossTimerUpdated {
timer_type: Option<TimerType>,
packet_number_space: Option<PacketNumberSpace>,
event_type: LossTimerEventType,
delta: Option<f32>,
},
#[serde(rename = "recovery:packet_lost")]
RecoveryPacketLost {
header: Option<PacketHeader>,
frames: Option<Vec<QuicFrame>>,
is_mtu_probe_packet: Option<bool>,
trigger: Option<PacketLostTrigger>,
},
#[serde(rename = "recovery:marked_for_retransmit")]
RecoveryMarkedForRetransmit { frames: Vec<QuicFrame> },
#[serde(rename = "recovery:ecn_state_updated")]
RecoveryEcnStateUpdated {
old: Option<EcnState>,
new: EcnState,
},
#[serde(rename = "h3:parameters_set")]
H3ParametersSet {
owner: Option<Owner>,
#[serde(alias = "max_header_list_size")]
max_field_section_size: Option<u64>,
max_table_capacity: Option<u64>,
blocked_streams_count: Option<u64>,
enable_connect: Option<u64>,
h3_datagram: Option<u64>,
waits_for_settings: Option<bool>,
},
#[serde(rename = "h3:parameters_restored")]
H3ParametersRestored {
#[serde(alias = "max_header_list_size")]
max_field_section_size: Option<u64>,
max_table_capacity: Option<u64>,
blocked_streams_count: Option<u64>,
enable_connect_protocol: Option<u64>,
h3_datagram: Option<u64>,
},
#[serde(rename = "h3:stream_type_set")]
H3StreamTypeSet {
owner: Option<Owner>,
stream_id: u64,
stream_type: Http3StreamType,
stream_type_value: Option<u64>,
associated_push_id: Option<u64>,
},
H3PriorityUpdated {
stream_id: Option<u64>,
push_id: Option<u64>,
old: Option<String>,
new: String,
},
#[serde(rename = "h3:frame_created")]
H3FrameCreated {
stream_id: u64,
length: Option<u64>,
frame: Http3Frame,
raw: Option<RawInfo>,
},
#[serde(rename = "h3:frame_parsed")]
H3FrameParsed {
stream_id: u64,
length: Option<u64>,
frame: Http3Frame,
raw: Option<RawInfo>,
},
#[serde(rename = "h3:push_resolved")]
H3PushResolved {
push_id: Option<u64>,
stream_id: Option<u64>,
decision: Http3PushDecision,
},
#[serde(rename = "qpack:state_updated")]
QpackStateUpdated {
owner: Option<Owner>,
dynamic_table_capacity: Option<u64>,
dynamic_table_size: Option<u64>,
known_received_count: Option<u64>,
current_insert_count: Option<u64>,
},
#[serde(rename = "qpack:stream_state_updated")]
QpackStreamStateUpdated {
stream_id: u64,
state: QpackStreamState,
},
#[serde(rename = "qpack:dynamic_table_updated")]
QpackDynamicTableUpdated {
owner: Owner,
update_type: QpackUpdateType,
entries: Vec<QpackDynamicTableEntry>,
},
#[serde(rename = "qpack:headers_encoded")]
QpackHeadersEncoded {
stream_id: Option<u64>,
headers: Option<HttpHeader>,
block_prefix: QpackHeaderBlockPrefix,
header_block: Vec<QpackHeaderBlockRepresentation>,
raw: Option<RawInfo>,
},
#[serde(rename = "qpack:headers_decoded")]
QpackHeadersDecoded {
stream_id: Option<u64>,
headers: Option<HttpHeader>,
block_prefix: QpackHeaderBlockPrefix,
header_block: Vec<QpackHeaderBlockRepresentation>,
raw: Option<RawInfo>,
},
#[serde(rename = "qpack:instruction_created")]
QpackInstructionCreated {
instruction: QPackInstruction,
raw: Option<RawInfo>,
},
#[serde(rename = "qpack:instruction_parsed")]
QpackInstructionParsed {
instruction: QPackInstruction,
raw: Option<RawInfo>,
},
#[serde(rename = "generic:internal_error")]
GenericInternalError {
code: Option<u64>,
description: Option<String>,
},
#[serde(rename = "generic:internal_warning")]
GenericInternalWarning {
code: Option<u64>,
description: Option<String>,
},
#[serde(rename = "generic:info")]
GenericInternalInfo { message: String },
#[serde(rename = "generic:debug")]
GenericInternalDebug { message: String },
#[serde(rename = "generic:verbose")]
GenericInternalVerbose { message: String },
#[serde(rename = "generic:marker")]
SimulationMarker {
marker_type: String,
message: Option<String>,
},
}
impl EventData {
pub fn importance(&self) -> EventImportance {
use crate::qlog::EventData::*;
match *self {
ConnectivityServerListening { .. } => EventImportance::Extra,
ConnectivityConnectionStarted { .. } => EventImportance::Base,
ConnectivityConnectionIdUpdated { .. } => EventImportance::Base,
ConnectivitySpinBitUpdated { .. } => EventImportance::Base,
ConnectivityConnectionStateUpdated { .. } => EventImportance::Base,
ConnectivityMtuUpdated { .. } => EventImportance::Extra,
QuicParametersSet { .. } => EventImportance::Core,
QuicDatagramsReceived { .. } => EventImportance::Extra,
QuicDatagramsSent { .. } => EventImportance::Extra,
QuicDatagramDropped { .. } => EventImportance::Extra,
QuicPacketReceived { .. } => EventImportance::Core,
QuicPacketSent { .. } => EventImportance::Core,
QuicPacketDropped { .. } => EventImportance::Base,
QuicPacketBuffered { .. } => EventImportance::Base,
QuicStreamStateUpdated { .. } => EventImportance::Base,
QuicFramesProcessed { .. } => EventImportance::Extra,
QuicStreamDataMoved { .. } => EventImportance::Base,
SecurityKeyUpdated { .. } => EventImportance::Base,
SecurityKeyDiscarded { .. } => EventImportance::Base,
RecoveryParametersSet { .. } => EventImportance::Base,
RecoveryMetricsUpdated { .. } => EventImportance::Core,
RecoveryCongestionStateUpdated { .. } => EventImportance::Base,
RecoveryLossTimerUpdated { .. } => EventImportance::Extra,
RecoveryPacketLost { .. } => EventImportance::Core,
RecoveryMarkedForRetransmit { .. } => EventImportance::Extra,
H3ParametersSet { .. } => EventImportance::Base,
H3StreamTypeSet { .. } => EventImportance::Base,
H3FrameCreated { .. } => EventImportance::Core,
H3FrameParsed { .. } => EventImportance::Core,
H3PushResolved { .. } => EventImportance::Extra,
QpackStateUpdated { .. } => EventImportance::Base,
QpackStreamStateUpdated { .. } => EventImportance::Base,
QpackDynamicTableUpdated { .. } => EventImportance::Extra,
QpackHeadersEncoded { .. } => EventImportance::Base,
QpackHeadersDecoded { .. } => EventImportance::Base,
QpackInstructionCreated { .. } => EventImportance::Base,
QpackInstructionParsed { .. } => EventImportance::Base,
_ => unimplemented!(),
}
}
}
#[derive(Clone, PartialEq, PartialOrd)]
pub enum EventImportance {
Core = 0,
Base = 1,
Extra = 2,
}
impl EventImportance {
pub fn is_contained_in(&self, other: &EventImportance) -> bool {
self <= other
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub enum TimeFormat {
Absolute,
Delta,
Relative,
}
#[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<String>,
}
#[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),
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum CryptoError {
Prefix,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Owner {
Local,
Remote,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionClosedTrigger {
Clean,
HandshakeTimeout,
IdleTimeout,
Error,
StatelessReset,
VersionMismatch,
Application,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionState {
Attempted,
PeerValidated,
HandshakeStarted,
EarlyWrite,
HandshakeCompleted,
HandshakeConfirmed,
Closing,
Draining,
Closed,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
#[serde(rename_all = "snake_case")]
pub enum PacketType {
Initial,
Handshake,
#[serde(rename = "0RTT")]
ZeroRtt,
#[serde(rename = "1RTT")]
OneRtt,
Retry,
VersionNegotiation,
#[default]
Unknown,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketNumberSpace {
Initial,
Handshake,
ApplicationData,
}
#[serde_with::skip_serializing_none]
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
pub struct PacketHeader {
pub packet_type: PacketType,
pub packet_number: u64,
pub flags: Option<u8>,
pub token: Option<Token>,
pub length: Option<u16>,
pub version: Option<String>,
pub scil: Option<u8>,
pub dcil: Option<u8>,
pub scid: Option<String>,
pub dcid: Option<String>,
}
impl PacketHeader {
#[allow(clippy::too_many_arguments)]
pub fn new(
packet_type: PacketType,
packet_number: u64,
flags: Option<u8>,
token: Option<Token>,
length: Option<u16>,
version: Option<u32>,
scid: Option<&[u8]>,
dcid: Option<&[u8]>,
) -> Self {
let (scil, scid) = match scid {
Some(cid) => (Some(cid.len() as u8), Some(hex::encode(cid))),
None => (None, None),
};
let (dcil, dcid) = match dcid {
Some(cid) => (Some(cid.len() as u8), Some(hex::encode(cid))),
None => (None, None),
};
let version = version.map(|v| format!("{v:x?}"));
PacketHeader {
packet_type,
packet_number,
flags,
token,
length,
version,
scil,
dcil,
scid,
dcid,
}
}
pub fn new_with_type(
ty: PacketType,
packet_number: u64,
version: Option<u32>,
scid: Option<&[u8]>,
dcid: Option<&[u8]>,
) -> Self {
match ty {
PacketType::OneRtt => {
PacketHeader::new(ty, packet_number, None, None, None, None, None, None)
}
_ => PacketHeader::new(ty, packet_number, None, None, None, version, scid, dcid),
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Token {
#[serde(rename(serialize = "type"))]
pub token_type: Option<TokenType>,
pub details: Option<String>,
pub raw: Option<RawInfo>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum TokenType {
Retry,
Resumption,
}
#[allow(clippy::enum_variant_names)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum KeyType {
ServerInitialSecret,
ClientInitialSecret,
ServerHandshakeSecret,
ClientHandshakeSecret,
#[serde(rename = "server_0rtt_secret")]
Server0RttSecret,
#[serde(rename = "client_0rtt_secret")]
Client0RttSecret,
#[serde(rename = "server_1rtt_secret")]
Server1RttSecret,
#[serde(rename = "client_1rtt_secret")]
Client1RttSecret,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Ecn {
#[serde(rename = "Not-ECT")]
NotEct,
#[serde(rename = "ECT(1)")]
Ect1,
#[serde(rename = "ECT(0)")]
Ect0,
#[serde(rename = "CE")]
Ce,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StreamType {
Bidirectional,
Unidirectional,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ErrorSpace {
TransportError,
ApplicationError,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum TransportError {
NoError,
InternalError,
ConnectionRefused,
FlowControlError,
StreamLimitError,
StreamStateError,
FinalSizeError,
FrameEncodingError,
TransportParameterError,
ConnectionIdLimitError,
ProtocolViolation,
InvalidToken,
ApplicationError,
CryptoBufferExceeded,
KeyUpdateError,
AeadLimitReached,
NoViablePath,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketSentTrigger {
RetransmitReordered,
RetransmitTimeout,
PtoProbe,
RetransmitCrypto,
CcBandwidthProbe,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketReceivedTrigger {
KeysUnavailable,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketDroppedTrigger {
InternalError,
Rejected,
Unsupported,
Invalid,
ConnectionUnknown,
DecryptionFailure,
General,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketBufferedTrigger {
Backpressure,
KeysUnavailable,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum LossTimerEventType {
Set,
Expired,
Cancelled,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(untagged)]
pub enum AckedRanges {
Single(Vec<Vec<u64>>),
Double(Vec<(u64, u64)>),
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QuicFrameTypeName {
Padding,
Ping,
Ack,
ResetStream,
StopSending,
Crypto,
NewToken,
Stream,
MaxData,
MaxStreamData,
MaxStreams,
DataBlocked,
StreamDataBlocked,
StreamsBlocked,
NewConnectionId,
RetireConnectionId,
PathChallenge,
PathResponse,
ConnectionClose,
ApplicationClose,
HandshakeDone,
Datagram,
Unknown,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(tag = "frame_type")]
#[serde(rename_all = "snake_case")]
pub enum QuicFrame {
Padding,
Ping,
Ack {
ack_delay: Option<f32>,
acked_ranges: Option<AckedRanges>,
ect1: Option<u64>,
ect0: Option<u64>,
ce: Option<u64>,
},
ResetStream {
stream_id: u64,
error_code: u64,
final_size: u64,
},
StopSending {
stream_id: u64,
error_code: u64,
},
Crypto {
offset: u64,
length: u64,
},
NewToken {
token: Token,
},
Stream {
stream_id: u64,
offset: u64,
length: u64,
fin: Option<bool>,
raw: Option<RawInfo>,
},
MaxData {
maximum: u64,
},
MaxStreamData {
stream_id: u64,
maximum: u64,
},
MaxStreams {
stream_type: StreamType,
maximum: u64,
},
DataBlocked {
limit: u64,
},
StreamDataBlocked {
stream_id: u64,
limit: u64,
},
StreamsBlocked {
stream_type: StreamType,
limit: u64,
},
NewConnectionId {
sequence_number: u32,
retire_prior_to: u32,
connection_id_length: Option<u8>,
connection_id: String,
stateless_reset_token: Option<String>,
},
RetireConnectionId {
sequence_number: u32,
},
PathChallenge {
data: Option<String>,
},
PathResponse {
data: Option<String>,
},
ConnectionClose {
error_space: Option<ErrorSpace>,
error_code: Option<u64>,
error_code_value: Option<u64>,
reason: Option<String>,
trigger_frame_type: Option<u64>,
},
HandshakeDone,
Datagram {
length: u64,
raw: Option<String>,
},
Unknown {
raw_frame_type: u64,
frame_type_value: Option<u64>,
raw: Option<RawInfo>,
},
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct QuicAlpnInformation {
pub server_alpns: Option<Vec<String>>,
pub client_alpns: Option<Vec<String>>,
pub chosen_alpn: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct PreferredAddress {
pub ip_v4: String,
pub ip_v6: String,
pub port_v4: u16,
pub port_v6: u16,
pub connection_id: String,
pub stateless_reset_token: String,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct QuicPacketSent {
pub header: PacketHeader,
pub is_coalesced: Option<bool>,
pub retry_token: Option<Token>,
pub stateless_reset_token: Option<String>,
pub supported_versions: Option<Vec<String>>,
pub raw: Option<RawInfo>,
pub datagram_id: Option<u32>,
pub trigger: Option<PacketSentTrigger>,
pub send_at_time: Option<f32>,
pub frames: Option<SmallVec<[QuicFrame; 1]>>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StreamState {
Idle,
Open,
HalfClosedLocal,
HalfClosedRemote,
Closed,
Ready,
Send,
DataSent,
ResetSent,
ResetReceived,
Receive,
SizeKnown,
DataRead,
ResetRead,
DataReceived,
Destroyed,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StreamSide {
Sending,
Receiving,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum CongestionStateUpdatedTrigger {
PersistentCongestion,
Ecn,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum TimerType {
Ack,
Pto,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PacketLostTrigger {
ReorderingThreshold,
TimeThreshold,
PtoExpired,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum KeyUpdateOrRetiredTrigger {
Tls,
RemoteUpdate,
LocalUpdate,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum EcnState {
Testing,
Unknown,
Failed,
Capable,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Http3StreamType {
Request,
Control,
Push,
Reserved,
Unknown,
QpackEncode,
QpackDecode,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Http3PriorityTargetStreamType {
Request,
Push,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Http3EventType {
ParametersSet,
ParametersRestored,
StreamTypeSet,
FrameCreated,
FrameParsed,
PushResolved,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ApplicationError {
HttpNoError,
HttpGeneralProtocolError,
HttpInternalError,
HttpRequestCancelled,
HttpIncompleteRequest,
HttpConnectError,
HttpFrameError,
HttpExcessiveLoad,
HttpVersionFallback,
HttpIdError,
HttpStreamCreationError,
HttpClosedCriticalStream,
HttpEarlyResponse,
HttpMissingSettings,
HttpUnexpectedFrame,
HttpRequestRejection,
HttpSettingsError,
Unknown,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct HttpHeader {
pub name: String,
pub value: String,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct Setting {
pub name: String,
pub value: u64,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Http3FrameTypeName {
Data,
Headers,
CancelPush,
Settings,
PushPromise,
Goaway,
MaxPushId,
DuplicatePush,
Reserved,
Unknown,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(tag = "frame_type")]
#[serde(rename_all = "snake_case")]
pub enum Http3Frame {
Data {
raw: Option<RawInfo>,
},
Headers {
headers: Vec<HttpHeader>,
},
CancelPush {
push_id: u64,
},
Settings {
settings: Vec<Setting>,
},
PushPromise {
push_id: u64,
headers: Vec<HttpHeader>,
},
Goaway {
id: u64,
},
MaxPushId {
push_id: u64,
},
PriorityUpdate {
target_stream_type: Http3PriorityTargetStreamType,
prioritized_element_id: u64,
priority_field_value: String,
},
Reserved {
length: Option<u64>,
},
Unknown {
frame_type_value: u64,
raw: Option<RawInfo>,
},
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Http3PushDecision {
Claimed,
Abandoned,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackEventType {
StateUpdated,
StreamStateUpdated,
DynamicTableUpdated,
HeadersEncoded,
HeadersDecoded,
InstructionCreated,
InstructionParsed,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackUpdateType {
Added,
Evicted,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct QpackDynamicTableEntry {
pub index: u64,
pub name: Option<String>,
pub value: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct QpackHeaderBlockPrefix {
pub required_insert_count: u64,
pub sign_bit: bool,
pub delta_base: u64,
}
#[allow(clippy::enum_variant_names)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackInstructionTypeName {
SetDynamicTableCapacityInstruction,
InsertWithNameReferenceInstruction,
InsertWithoutNameReferenceInstruction,
DuplicateInstruction,
HeaderAcknowledgementInstruction,
StreamCancellationInstruction,
InsertCountIncrementInstruction,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackTableType {
Static,
Dynamic,
}
#[allow(clippy::enum_variant_names)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub enum QPackInstruction {
SetDynamicTableCapacityInstruction {
instruction_type: QpackInstructionTypeName,
capacity: u64,
},
InsertWithNameReferenceInstruction {
instruction_type: QpackInstructionTypeName,
table_type: QpackTableType,
name_index: u64,
huffman_encoded_value: bool,
value_length: Option<u64>,
value: Option<String>,
},
InsertWithoutNameReferenceInstruction {
instruction_type: QpackInstructionTypeName,
huffman_encoded_name: bool,
name_length: Option<u64>,
name: Option<String>,
huffman_encoded_value: bool,
value_length: Option<u64>,
value: Option<String>,
},
DuplicateInstruction {
instruction_type: QpackInstructionTypeName,
index: u64,
},
HeaderAcknowledgementInstruction {
instruction_type: QpackInstructionTypeName,
stream_id: String,
},
StreamCancellationInstruction {
instruction_type: QpackInstructionTypeName,
stream_id: String,
},
InsertCountIncrementInstruction {
instruction_type: QpackInstructionTypeName,
increment: u64,
},
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackHeaderBlockRepresentationTypeName {
IndexedHeaderField,
LiteralHeaderFieldWithName,
LiteralHeaderFieldWithoutName,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub enum QpackHeaderBlockRepresentation {
IndexedHeaderField {
header_field_type: QpackHeaderBlockRepresentationTypeName,
table_type: QpackTableType,
index: u64,
is_post_base: Option<bool>,
},
LiteralHeaderFieldWithName {
header_field_type: QpackHeaderBlockRepresentationTypeName,
preserve_literal: bool,
table_type: QpackTableType,
name_index: u64,
huffman_encoded_value: bool,
value_length: Option<u64>,
value: Option<String>,
is_post_base: Option<bool>,
},
LiteralHeaderFieldWithoutName {
header_field_type: QpackHeaderBlockRepresentationTypeName,
preserve_literal: bool,
huffman_encoded_name: bool,
name_length: Option<u64>,
name: Option<String>,
huffman_encoded_value: bool,
value_length: Option<u64>,
value: Option<String>,
},
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QpackStreamState {
Blocked,
Unblocked,
}
#[cfg(test)]
pub mod tests {
use super::*;
pub fn new_test_pkt_hdr(packet_type: PacketType) -> PacketHeader {
PacketHeader::new(
packet_type,
0,
None,
None,
None,
Some(0x0000_0001),
Some(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]),
Some(&[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]),
)
}
#[test]
fn serialize_packet_header() {
let pkt_hdr = new_test_pkt_hdr(PacketType::Initial);
assert_eq!(
serde_json::to_string_pretty(&pkt_hdr).unwrap(),
r#"{
"packet_type": "initial",
"packet_number": 0,
"version": "1",
"scil": 8,
"dcil": 8,
"scid": "0102030405060708",
"dcid": "0807060504030201"
}"#
);
}
#[test]
fn serialize_quic_packet_sent_event() {
let pkt_hdr = new_test_pkt_hdr(PacketType::Initial);
let event_data = EventData::QuicPacketSent {
header: pkt_hdr,
frames: None,
is_coalesced: None,
retry_token: None,
stateless_reset_token: None,
supported_versions: None,
raw: Some(RawInfo {
length: Some(1200),
payload_length: Some(1173),
data: None,
}),
datagram_id: None,
is_mtu_probe_packet: None,
trigger: None,
};
let event = Event::new(1234567000.0, event_data);
assert_eq!(
serde_json::to_string_pretty(&event).unwrap(),
r#"{
"time": 1234567000.0,
"name": "quic:packet_sent",
"data": {
"header": {
"packet_type": "initial",
"packet_number": 0,
"version": "1",
"scil": 8,
"dcil": 8,
"scid": "0102030405060708",
"dcid": "0807060504030201"
},
"raw": {
"length": 1200,
"payload_length": 1173
}
}
}"#
);
}
}