qlog/events/
quic.rs

1// Copyright (C) 2021, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use serde::Deserialize;
28use serde::Serialize;
29
30use smallvec::SmallVec;
31
32use super::connectivity::TransportOwner;
33use super::Bytes;
34use super::DataRecipient;
35use super::RawInfo;
36use super::Token;
37use crate::HexSlice;
38use crate::StatelessResetToken;
39
40#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
41#[serde(rename_all = "snake_case")]
42pub enum PacketType {
43    Initial,
44    Handshake,
45
46    #[serde(rename = "0RTT")]
47    ZeroRtt,
48
49    #[serde(rename = "1RTT")]
50    OneRtt,
51
52    Retry,
53    VersionNegotiation,
54    #[default]
55    Unknown,
56}
57
58#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
59#[serde(rename_all = "snake_case")]
60pub enum PacketNumberSpace {
61    Initial,
62    Handshake,
63    ApplicationData,
64}
65
66#[serde_with::skip_serializing_none]
67#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
68pub struct PacketHeader {
69    pub packet_type: PacketType,
70    pub packet_number: Option<u64>,
71
72    pub flags: Option<u8>,
73    pub token: Option<Token>,
74
75    pub length: Option<u16>,
76
77    pub version: Option<Bytes>,
78
79    pub scil: Option<u8>,
80    pub dcil: Option<u8>,
81    pub scid: Option<Bytes>,
82    pub dcid: Option<Bytes>,
83}
84
85impl PacketHeader {
86    #[allow(clippy::too_many_arguments)]
87    /// Creates a new PacketHeader.
88    pub fn new(
89        packet_type: PacketType, packet_number: Option<u64>, flags: Option<u8>,
90        token: Option<Token>, length: Option<u16>, version: Option<u32>,
91        scid: Option<&[u8]>, dcid: Option<&[u8]>,
92    ) -> Self {
93        let (scil, scid) = match scid {
94            Some(cid) => (
95                Some(cid.len() as u8),
96                Some(format!("{}", HexSlice::new(&cid))),
97            ),
98
99            None => (None, None),
100        };
101
102        let (dcil, dcid) = match dcid {
103            Some(cid) => (
104                Some(cid.len() as u8),
105                Some(format!("{}", HexSlice::new(&cid))),
106            ),
107
108            None => (None, None),
109        };
110
111        let version = version.map(|v| format!("{v:x?}"));
112
113        PacketHeader {
114            packet_type,
115            packet_number,
116            flags,
117            token,
118            length,
119            version,
120            scil,
121            dcil,
122            scid,
123            dcid,
124        }
125    }
126
127    /// Creates a new PacketHeader.
128    ///
129    /// Once a QUIC connection has formed, version, dcid and scid are stable, so
130    /// there are space benefits to not logging them in every packet, especially
131    /// PacketType::OneRtt.
132    pub fn with_type(
133        ty: PacketType, packet_number: Option<u64>, version: Option<u32>,
134        scid: Option<&[u8]>, dcid: Option<&[u8]>,
135    ) -> Self {
136        match ty {
137            PacketType::OneRtt => PacketHeader::new(
138                ty,
139                packet_number,
140                None,
141                None,
142                None,
143                None,
144                None,
145                None,
146            ),
147
148            _ => PacketHeader::new(
149                ty,
150                packet_number,
151                None,
152                None,
153                None,
154                version,
155                scid,
156                dcid,
157            ),
158        }
159    }
160}
161
162#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
163#[serde(rename_all = "snake_case")]
164pub enum StreamType {
165    Bidirectional,
166    Unidirectional,
167}
168
169#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
170#[serde(rename_all = "snake_case")]
171pub enum StreamSide {
172    Sending,
173    Receiving,
174}
175
176#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
177#[serde(rename_all = "snake_case")]
178pub enum StreamState {
179    // bidirectional stream states, draft-23 3.4.
180    Idle,
181    Open,
182    HalfClosedLocal,
183    HalfClosedRemote,
184    Closed,
185
186    // sending-side stream states, draft-23 3.1.
187    Ready,
188    Send,
189    DataSent,
190    ResetSent,
191    ResetReceived,
192
193    // receive-side stream states, draft-23 3.2.
194    Receive,
195    SizeKnown,
196    DataRead,
197    ResetRead,
198
199    // both-side states
200    DataReceived,
201
202    // qlog-defined
203    Destroyed,
204}
205
206#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
207#[serde(rename_all = "snake_case")]
208pub enum ErrorSpace {
209    TransportError,
210    ApplicationError,
211}
212
213#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
214#[serde(rename_all = "snake_case")]
215pub enum TransportError {
216    NoError,
217    InternalError,
218    ConnectionRefused,
219    FlowControlError,
220    StreamLimitError,
221    StreamStateError,
222    FinalSizeError,
223    FrameEncodingError,
224    TransportParameterError,
225    ConnectionIdLimitError,
226    ProtocolViolation,
227    InvalidToken,
228    ApplicationError,
229    CryptoBufferExceeded,
230    KeyUpdateError,
231    AeadLimitReached,
232    NoViablePath,
233}
234
235#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
236#[serde(rename_all = "snake_case")]
237pub enum TransportEventType {
238    VersionInformation,
239    AlpnInformation,
240
241    ParametersSet,
242    ParametersRestored,
243
244    DatagramsSent,
245    DatagramsReceived,
246    DatagramDropped,
247
248    PacketSent,
249    PacketReceived,
250    PacketDropped,
251    PacketBuffered,
252    PacketsAcked,
253
254    FramesProcessed,
255
256    StreamStateUpdated,
257
258    DataMoved,
259}
260
261#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
262#[serde(rename_all = "snake_case")]
263pub enum PacketSentTrigger {
264    RetransmitReordered,
265    RetransmitTimeout,
266    PtoProbe,
267    RetransmitCrypto,
268    CcBandwidthProbe,
269}
270
271#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
272#[serde(rename_all = "snake_case")]
273pub enum PacketReceivedTrigger {
274    KeysUnavailable,
275}
276
277#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
278#[serde(rename_all = "snake_case")]
279pub enum PacketDroppedTrigger {
280    InternalError,
281    Rejected,
282    Unsupported,
283    Invalid,
284    ConnectionUnknown,
285    DecryptionFailure,
286    General,
287}
288
289#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
290#[serde(rename_all = "snake_case")]
291pub enum PacketBufferedTrigger {
292    Backpressure,
293    KeysUnavailable,
294}
295
296#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
297#[serde(rename_all = "snake_case")]
298pub enum SecurityEventType {
299    KeyUpdated,
300    KeyDiscarded,
301}
302
303#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
304#[serde(rename_all = "snake_case")]
305pub enum RecoveryEventType {
306    ParametersSet,
307    MetricsUpdated,
308    CongestionStateUpdated,
309    LossTimerUpdated,
310    PacketLost,
311    MarkedForRetransmit,
312}
313
314#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
315#[serde(rename_all = "snake_case")]
316pub enum CongestionStateUpdatedTrigger {
317    PersistentCongestion,
318    Ecn,
319}
320
321#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
322#[serde(rename_all = "snake_case")]
323pub enum PacketLostTrigger {
324    ReorderingThreshold,
325    TimeThreshold,
326    PtoExpired,
327}
328
329#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
330#[serde(rename_all = "snake_case")]
331pub enum LossTimerEventType {
332    Set,
333    Expired,
334    Cancelled,
335}
336
337#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
338#[serde(rename_all = "snake_case")]
339pub enum TimerType {
340    Ack,
341    Pto,
342}
343
344#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
345#[serde(untagged)]
346pub enum AckedRanges {
347    Single(Vec<Vec<u64>>),
348    Double(Vec<(u64, u64)>),
349}
350
351#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
352#[serde(rename_all = "snake_case")]
353pub enum QuicFrameTypeName {
354    Padding,
355    Ping,
356    Ack,
357    ResetStream,
358    StopSending,
359    Crypto,
360    NewToken,
361    Stream,
362    MaxData,
363    MaxStreamData,
364    MaxStreams,
365    DataBlocked,
366    StreamDataBlocked,
367    StreamsBlocked,
368    NewConnectionId,
369    RetireConnectionId,
370    PathChallenge,
371    PathResponse,
372    ConnectionClose,
373    ApplicationClose,
374    HandshakeDone,
375    Datagram,
376    #[default]
377    Unknown,
378}
379
380#[serde_with::skip_serializing_none]
381#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
382#[serde(tag = "frame_type")]
383#[serde(rename_all = "snake_case")]
384// Strictly, the qlog spec says that all these frame types have a frame_type
385// field. But instead of making that a rust object property, just use serde to
386// ensure it goes out on the wire. This means that deserialization of frames
387// also works automatically.
388pub enum QuicFrame {
389    Padding {
390        length: Option<u32>,
391        payload_length: u32,
392    },
393
394    Ping {
395        length: Option<u32>,
396        payload_length: Option<u32>,
397    },
398
399    Ack {
400        ack_delay: Option<f32>,
401        acked_ranges: Option<AckedRanges>,
402
403        ect1: Option<u64>,
404        ect0: Option<u64>,
405        ce: Option<u64>,
406
407        length: Option<u32>,
408        payload_length: Option<u32>,
409    },
410
411    ResetStream {
412        stream_id: u64,
413        error_code: u64,
414        final_size: u64,
415
416        length: Option<u32>,
417        payload_length: Option<u32>,
418    },
419
420    StopSending {
421        stream_id: u64,
422        error_code: u64,
423
424        length: Option<u32>,
425        payload_length: Option<u32>,
426    },
427
428    Crypto {
429        offset: u64,
430        length: u64,
431    },
432
433    NewToken {
434        token: Token,
435    },
436
437    Stream {
438        stream_id: u64,
439        offset: u64,
440        length: u64,
441        fin: Option<bool>,
442
443        raw: Option<RawInfo>,
444    },
445
446    MaxData {
447        maximum: u64,
448    },
449
450    MaxStreamData {
451        stream_id: u64,
452        maximum: u64,
453    },
454
455    MaxStreams {
456        stream_type: StreamType,
457        maximum: u64,
458    },
459
460    DataBlocked {
461        limit: u64,
462    },
463
464    StreamDataBlocked {
465        stream_id: u64,
466        limit: u64,
467    },
468
469    StreamsBlocked {
470        stream_type: StreamType,
471        limit: u64,
472    },
473
474    NewConnectionId {
475        sequence_number: u32,
476        retire_prior_to: u32,
477        connection_id_length: Option<u8>,
478        connection_id: Bytes,
479        stateless_reset_token: Option<StatelessResetToken>,
480    },
481
482    RetireConnectionId {
483        sequence_number: u32,
484    },
485
486    PathChallenge {
487        data: Option<Bytes>,
488    },
489
490    PathResponse {
491        data: Option<Bytes>,
492    },
493
494    ConnectionClose {
495        error_space: Option<ErrorSpace>,
496        error_code: Option<u64>,
497        error_code_value: Option<u64>,
498        reason: Option<String>,
499
500        trigger_frame_type: Option<u64>,
501    },
502
503    HandshakeDone,
504
505    Datagram {
506        length: u64,
507
508        raw: Option<Bytes>,
509    },
510
511    Unknown {
512        raw_frame_type: u64,
513        frame_type_value: Option<u64>,
514        raw: Option<RawInfo>,
515    },
516}
517
518#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
519pub struct PreferredAddress {
520    pub ip_v4: String,
521    pub ip_v6: String,
522
523    pub port_v4: u16,
524    pub port_v6: u16,
525
526    pub connection_id: Bytes,
527    pub stateless_reset_token: StatelessResetToken,
528}
529
530#[serde_with::skip_serializing_none]
531#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
532pub struct VersionInformation {
533    pub server_versions: Option<Vec<Bytes>>,
534    pub client_versions: Option<Vec<Bytes>>,
535    pub chosen_version: Option<Bytes>,
536}
537
538#[serde_with::skip_serializing_none]
539#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
540pub struct AlpnInformation {
541    pub server_alpns: Option<Vec<Bytes>>,
542    pub client_alpns: Option<Vec<Bytes>>,
543    pub chosen_alpn: Option<Bytes>,
544}
545
546#[serde_with::skip_serializing_none]
547#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
548pub struct TransportParametersSet {
549    pub owner: Option<TransportOwner>,
550
551    pub resumption_allowed: Option<bool>,
552    pub early_data_enabled: Option<bool>,
553    pub tls_cipher: Option<String>,
554    pub aead_tag_length: Option<u8>,
555
556    pub original_destination_connection_id: Option<Bytes>,
557    pub initial_source_connection_id: Option<Bytes>,
558    pub retry_source_connection_id: Option<Bytes>,
559    pub stateless_reset_token: Option<StatelessResetToken>,
560    pub disable_active_migration: Option<bool>,
561
562    pub max_idle_timeout: Option<u64>,
563    pub max_udp_payload_size: Option<u32>,
564    pub ack_delay_exponent: Option<u16>,
565    pub max_ack_delay: Option<u16>,
566    pub active_connection_id_limit: Option<u32>,
567
568    pub initial_max_data: Option<u64>,
569    pub initial_max_stream_data_bidi_local: Option<u64>,
570    pub initial_max_stream_data_bidi_remote: Option<u64>,
571    pub initial_max_stream_data_uni: Option<u64>,
572    pub initial_max_streams_bidi: Option<u64>,
573    pub initial_max_streams_uni: Option<u64>,
574
575    pub preferred_address: Option<PreferredAddress>,
576
577    pub unknown_parameters: Vec<UnknownTransportParameter>,
578}
579
580#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
581pub struct UnknownTransportParameter {
582    pub id: u64,
583    pub value: Bytes,
584}
585
586#[serde_with::skip_serializing_none]
587#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
588pub struct TransportParametersRestored {
589    pub disable_active_migration: Option<bool>,
590
591    pub max_idle_timeout: Option<u64>,
592    pub max_udp_payload_size: Option<u32>,
593    pub active_connection_id_limit: Option<u32>,
594
595    pub initial_max_data: Option<u64>,
596    pub initial_max_stream_data_bidi_local: Option<u64>,
597    pub initial_max_stream_data_bidi_remote: Option<u64>,
598    pub initial_max_stream_data_uni: Option<u64>,
599    pub initial_max_streams_bidi: Option<u64>,
600    pub initial_max_streams_uni: Option<u64>,
601}
602
603#[serde_with::skip_serializing_none]
604#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
605pub struct DatagramsReceived {
606    pub count: Option<u16>,
607
608    pub raw: Option<Vec<RawInfo>>,
609
610    pub datagram_ids: Option<Vec<u32>>,
611}
612
613#[serde_with::skip_serializing_none]
614#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
615pub struct DatagramsSent {
616    pub count: Option<u16>,
617
618    pub raw: Option<Vec<RawInfo>>,
619
620    pub datagram_ids: Option<Vec<u32>>,
621}
622
623#[serde_with::skip_serializing_none]
624#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
625pub struct DatagramDropped {
626    pub raw: Option<RawInfo>,
627}
628
629#[serde_with::skip_serializing_none]
630#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
631pub struct PacketReceived {
632    pub header: PacketHeader,
633    // `frames` is defined here in the QLog schema specification. However,
634    // our streaming serializer requires serde to put the object at the end,
635    // so we define it there and depend on serde's preserve_order feature.
636    pub is_coalesced: Option<bool>,
637
638    pub retry_token: Option<Token>,
639
640    pub stateless_reset_token: Option<StatelessResetToken>,
641
642    pub supported_versions: Option<Vec<Bytes>>,
643
644    pub raw: Option<RawInfo>,
645    pub datagram_id: Option<u32>,
646
647    pub trigger: Option<PacketReceivedTrigger>,
648
649    pub frames: Option<Vec<QuicFrame>>,
650}
651
652#[serde_with::skip_serializing_none]
653#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
654pub struct PacketSent {
655    pub header: PacketHeader,
656    // `frames` is defined here in the QLog schema specification. However,
657    // our streaming serializer requires serde to put the object at the end,
658    // so we define it there and depend on serde's preserve_order feature.
659    pub is_coalesced: Option<bool>,
660
661    pub retry_token: Option<Token>,
662
663    pub stateless_reset_token: Option<StatelessResetToken>,
664
665    pub supported_versions: Option<Vec<Bytes>>,
666
667    pub raw: Option<RawInfo>,
668    pub datagram_id: Option<u32>,
669
670    pub trigger: Option<PacketSentTrigger>,
671
672    pub send_at_time: Option<f32>,
673
674    pub frames: Option<SmallVec<[QuicFrame; 1]>>,
675}
676
677#[serde_with::skip_serializing_none]
678#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
679pub struct PacketDropped {
680    pub header: Option<PacketHeader>,
681
682    pub raw: Option<RawInfo>,
683    pub datagram_id: Option<u32>,
684
685    pub details: Option<String>,
686
687    pub trigger: Option<PacketDroppedTrigger>,
688}
689
690#[serde_with::skip_serializing_none]
691#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
692pub struct PacketBuffered {
693    pub header: Option<PacketHeader>,
694
695    pub raw: Option<RawInfo>,
696    pub datagram_id: Option<u32>,
697
698    pub trigger: Option<PacketBufferedTrigger>,
699}
700
701#[serde_with::skip_serializing_none]
702#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
703pub struct PacketsAcked {
704    pub packet_number_space: Option<PacketNumberSpace>,
705    pub packet_numbers: Option<Vec<u64>>,
706}
707
708#[serde_with::skip_serializing_none]
709#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
710pub struct StreamStateUpdated {
711    pub stream_id: u64,
712    pub stream_type: Option<StreamType>,
713
714    pub old: Option<StreamState>,
715    pub new: StreamState,
716
717    pub stream_side: Option<StreamSide>,
718}
719
720#[serde_with::skip_serializing_none]
721#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
722pub struct FramesProcessed {
723    pub frames: Vec<QuicFrame>,
724
725    pub packet_number: Option<u64>,
726}
727
728#[serde_with::skip_serializing_none]
729#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
730pub struct DataMoved {
731    pub stream_id: Option<u64>,
732    pub offset: Option<u64>,
733    pub length: Option<u64>,
734
735    pub from: Option<DataRecipient>,
736    pub to: Option<DataRecipient>,
737
738    pub raw: Option<RawInfo>,
739}
740
741#[serde_with::skip_serializing_none]
742#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
743pub struct RecoveryParametersSet {
744    pub reordering_threshold: Option<u16>,
745    pub time_threshold: Option<f32>,
746    pub timer_granularity: Option<u16>,
747    pub initial_rtt: Option<f32>,
748
749    pub max_datagram_size: Option<u32>,
750    pub initial_congestion_window: Option<u64>,
751    pub minimum_congestion_window: Option<u32>,
752    pub loss_reduction_factor: Option<f32>,
753    pub persistent_congestion_threshold: Option<u16>,
754}
755
756#[serde_with::skip_serializing_none]
757#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
758pub struct MetricsUpdated {
759    pub min_rtt: Option<f32>,
760    pub smoothed_rtt: Option<f32>,
761    pub latest_rtt: Option<f32>,
762    pub rtt_variance: Option<f32>,
763
764    pub pto_count: Option<u16>,
765
766    pub congestion_window: Option<u64>,
767    pub bytes_in_flight: Option<u64>,
768
769    pub ssthresh: Option<u64>,
770
771    // qlog defined
772    pub packets_in_flight: Option<u64>,
773
774    pub pacing_rate: Option<u64>,
775}
776
777#[serde_with::skip_serializing_none]
778#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
779pub struct CongestionStateUpdated {
780    pub old: Option<String>,
781    pub new: String,
782
783    pub trigger: Option<CongestionStateUpdatedTrigger>,
784}
785
786#[serde_with::skip_serializing_none]
787#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
788pub struct LossTimerUpdated {
789    pub timer_type: Option<TimerType>,
790    pub packet_number_space: Option<PacketNumberSpace>,
791
792    pub event_type: LossTimerEventType,
793
794    pub delta: Option<f32>,
795}
796
797#[serde_with::skip_serializing_none]
798#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
799pub struct PacketLost {
800    pub header: Option<PacketHeader>,
801
802    pub frames: Option<Vec<QuicFrame>>,
803
804    pub trigger: Option<PacketLostTrigger>,
805}
806
807#[serde_with::skip_serializing_none]
808#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
809pub struct MarkedForRetransmit {
810    pub frames: Vec<QuicFrame>,
811}
812
813#[cfg(test)]
814mod tests {
815    use super::*;
816    use crate::testing::*;
817
818    #[test]
819    fn packet_header() {
820        let pkt_hdr = make_pkt_hdr(PacketType::Initial);
821
822        let log_string = r#"{
823  "packet_type": "initial",
824  "packet_number": 0,
825  "version": "1",
826  "scil": 8,
827  "dcil": 8,
828  "scid": "7e37e4dcc6682da8",
829  "dcid": "36ce104eee50101c"
830}"#;
831
832        assert_eq!(serde_json::to_string_pretty(&pkt_hdr).unwrap(), log_string);
833    }
834}