1#![allow(unused_variables)]
12
13#[cfg(not(feature = "qlog"))]
14use std::marker::PhantomData;
15#[cfg(feature = "qlog")]
16use std::sync::{Arc, Mutex};
17use std::{
18 net::{IpAddr, SocketAddr},
19 time::Duration,
20};
21
22#[cfg(feature = "qlog")]
23use qlog::{
24 CommonFields, HexSlice, TokenType, VantagePoint,
25 events::{
26 ApplicationError, ConnectionClosedFrameError, Event, EventData, RawInfo, TupleEndpointInfo,
27 quic::{
28 self, AckRange, AddressDiscoveryRole, ConnectionStarted, ErrorSpace, PacketHeader,
29 PacketLost, PacketLostTrigger, PacketReceived, PacketSent, PacketType,
30 ParametersRestored, ParametersSet, PreferredAddress, QlogTimerType, QuicFrame,
31 StreamType, TimerEventType, TimerType, TimerUpdated, TransportInitiator, TupleAssigned,
32 },
33 },
34 streamer::QlogStreamer,
35};
36#[cfg(feature = "qlog")]
37use tracing::warn;
38
39use crate::{
40 Connection, ConnectionId, FourTuple, Frame, Instant, PathId,
41 connection::{EncryptionLevel, PathData, SentPacket, SpaceKind, timer::Timer},
42 frame::EncodableFrame,
43 packet::Header,
44 transport_parameters::TransportParameters,
45};
46#[cfg(feature = "qlog")]
47use crate::{
48 QlogConfig, Side, TransportErrorCode,
49 connection::timer::{ConnTimer, PathTimer},
50 frame::{self, DataBlocked, StreamDataBlocked, StreamsBlocked},
51};
52
53#[cfg(feature = "qlog")]
55#[derive(Clone)]
56pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>);
57
58#[cfg(feature = "qlog")]
59impl QlogStream {
60 pub(crate) fn new(
61 config: QlogConfig,
62 initial_dst_cid: ConnectionId,
63 side: Side,
64 now: Instant,
65 ) -> Result<Self, qlog::Error> {
66 let vantage_point = VantagePoint {
67 name: None,
68 ty: match side {
69 Side::Client => qlog::VantagePointType::Client,
70 Side::Server => qlog::VantagePointType::Server,
71 },
72 flow: None,
73 };
74
75 let common_fields = CommonFields {
76 group_id: Some(initial_dst_cid.to_string()),
77 ..Default::default()
78 };
79
80 let trace = qlog::TraceSeq::new(
81 config.title.clone(),
82 config.description.clone(),
83 Some(common_fields),
84 Some(vantage_point),
85 vec![],
86 );
87
88 let start_time = config.start_time.unwrap_or(now);
89
90 let mut streamer = QlogStreamer::new(
91 config.title,
92 config.description,
93 start_time,
94 trace,
95 qlog::events::EventImportance::Extra,
96 qlog::streamer::EventTimePrecision::MicroSeconds,
97 config.writer,
98 );
99
100 streamer.start_log()?;
101 Ok(Self(Arc::new(Mutex::new(streamer))))
102 }
103
104 fn emit_event(&self, event: EventData, now: Instant) {
105 self.emit_event_with_tuple_id(event, now, None);
106 }
107
108 fn emit_event_with_tuple_id(
109 &self,
110 event: EventData,
111 now: Instant,
112 network_path: Option<String>,
113 ) {
114 let mut event = Event::with_time(0.0, event);
116 event.tuple = network_path;
117 let mut qlog_streamer = self.0.lock().unwrap();
118 if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
119 warn!("could not emit qlog event: {e}");
120 }
121 }
122}
123
124#[derive(Clone, Default)]
126pub(crate) struct QlogSink {
127 #[cfg(feature = "qlog")]
128 stream: Option<QlogStream>,
129}
130
131impl QlogSink {
132 #[cfg(feature = "qlog")]
133 pub(crate) fn new(
134 config: QlogConfig,
135 initial_dst_cid: ConnectionId,
136 side: Side,
137 now: Instant,
138 ) -> Self {
139 let stream = QlogStream::new(config, initial_dst_cid, side, now)
140 .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
141 .ok();
142 Self { stream }
143 }
144
145 pub(crate) fn emit_connection_started(
146 &self,
147 now: Instant,
148 local_cid: ConnectionId,
149 remote_cid: ConnectionId,
150 remote: SocketAddr,
151 local_ip: Option<IpAddr>,
152 transport_params: &TransportParameters,
153 ) {
154 #[cfg(feature = "qlog")]
155 {
156 let Some(stream) = self.stream.as_ref() else {
157 return;
158 };
159 stream.emit_event(
160 EventData::QuicConnectionStarted(ConnectionStarted {
161 local: tuple_endpoint_info(local_ip, None, Some(local_cid)),
162 remote: tuple_endpoint_info(
163 Some(remote.ip()),
164 Some(remote.port()),
165 Some(remote_cid),
166 ),
167 }),
168 now,
169 );
170
171 let params = transport_params.to_qlog(TransportInitiator::Local);
172 let event = EventData::QuicParametersSet(Box::new(params));
173 stream.emit_event(event, now);
174 }
175 }
176
177 pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
178 #[cfg(feature = "qlog")]
179 {
180 let Some(stream) = self.stream.as_ref() else {
181 return;
182 };
183
184 let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
185 return;
186 };
187
188 stream.emit_event(EventData::QuicMetricsUpdated(metrics), now);
189 }
190 }
191
192 pub(super) fn emit_packet_lost(
193 &self,
194 pn: u64,
195 info: &SentPacket,
196 loss_delay: Duration,
197 space: SpaceKind,
198 now: Instant,
199 ) {
200 #[cfg(feature = "qlog")]
201 {
202 let Some(stream) = self.stream.as_ref() else {
203 return;
204 };
205
206 let event = PacketLost {
207 header: Some(PacketHeader {
208 packet_number: Some(pn),
209 packet_type: packet_type(space, false),
210 length: Some(info.size),
211 ..Default::default()
212 }),
213 frames: None,
214 trigger: Some(
215 match info.time_sent.saturating_duration_since(now) >= loss_delay {
216 true => PacketLostTrigger::TimeThreshold,
217 false => PacketLostTrigger::ReorderingThreshold,
218 },
219 ),
220 is_mtu_probe_packet: None,
221 };
222
223 stream.emit_event(EventData::QuicPacketLost(event), now);
224 }
225 }
226
227 pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
228 #[cfg(feature = "qlog")]
229 {
230 let Some(stream) = self.stream.as_ref() else {
231 return;
232 };
233 let params = conn.peer_params.to_qlog_restored();
234 let event = EventData::QuicParametersRestored(params);
235 stream.emit_event(event, now);
236 }
237 }
238
239 pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
240 #[cfg(feature = "qlog")]
241 {
242 let Some(stream) = self.stream.as_ref() else {
243 return;
244 };
245 let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
246 let event = EventData::QuicParametersSet(Box::new(params));
247 stream.emit_event(event, now);
248 }
249 }
250
251 pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) {
252 #[cfg(feature = "qlog")]
253 {
254 let Some(stream) = self.stream.as_ref() else {
255 return;
256 };
257 let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
258 let event = TupleAssigned {
259 tuple_id,
260 tuple_local: tuple
261 .local_ip
262 .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)),
263 tuple_remote: Some(tuple_endpoint_info(
264 Some(tuple.remote.ip()),
265 Some(tuple.remote.port()),
266 None,
267 )),
268 };
269
270 stream.emit_event(EventData::QuicTupleAssigned(event), now);
271 }
272 }
273
274 pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
275 #[cfg(feature = "qlog")]
276 {
277 let Some(stream) = self.stream.as_ref() else {
278 return;
279 };
280 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
281 stream.emit_event_with_tuple_id(EventData::QuicPacketSent(packet.inner), now, tuple_id);
282 }
283 }
284
285 pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
286 #[cfg(feature = "qlog")]
287 {
288 let Some(stream) = self.stream.as_ref() else {
289 return;
290 };
291 let mut packet = packet;
292 packet.emit_padding();
293 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
294 let event = packet.inner;
295 stream.emit_event_with_tuple_id(EventData::QuicPacketReceived(event), now, tuple_id);
296 }
297 }
298
299 #[cfg(feature = "qlog")]
304 fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
305 let Some(stream) = self.stream.as_ref() else {
306 return;
307 };
308
309 let timer_type: Option<TimerType> = match timer {
310 Timer::Conn(conn_timer) => match conn_timer {
311 ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
312 ConnTimer::Close => Some(TimerType::custom("close")),
313 ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
314 ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
315 ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
316 ConnTimer::NoAvailablePath => Some(TimerType::custom("no_available_path")),
317 ConnTimer::NatTraversalProbeRetry => {
318 Some(TimerType::custom("nat_traversal_probe_retry"))
319 }
320 },
321 Timer::PerPath(_, path_timer) => match path_timer {
322 PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
323 PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
324 PathTimer::PathValidationFailed => Some(QlogTimerType::PathValidation.into()),
325 PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
326 PathTimer::AbandonFromValidation => {
327 Some(TimerType::custom("abandon_from_validation"))
328 }
329 PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
330 PathTimer::Pacing => Some(TimerType::custom("pacing")),
331 PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
332 PathTimer::PathDrained => Some(TimerType::custom("discard_path")),
333 },
334 };
335
336 let Some(timer_type) = timer_type else {
337 return;
338 };
339
340 let delta = match op {
341 TimerOp::Set(instant) => instant
342 .checked_duration_since(now)
343 .map(|dur| dur.as_secs_f32() * 1000.),
344 _ => None,
345 };
346 let path_id = match timer {
347 Timer::Conn(_) => None,
348 Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
349 };
350
351 let event_type = match op {
352 TimerOp::Set(_) => TimerEventType::Set,
353 TimerOp::Expire => TimerEventType::Expired,
354 TimerOp::Cancelled => TimerEventType::Cancelled,
355 };
356
357 let event = TimerUpdated {
358 path_id,
359 timer_type: Some(timer_type),
360 timer_id: None,
361 packet_number_space: None,
362 event_type,
363 delta,
364 };
365 stream.emit_event(EventData::QuicTimerUpdated(event), now);
366 }
367
368 pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
374 #[cfg(feature = "qlog")]
375 let s = QlogSinkWithTime { sink: self, now };
376 #[cfg(not(feature = "qlog"))]
377 let s = QlogSinkWithTime {
378 _phantom: PhantomData,
379 };
380 s
381 }
382}
383
384#[derive(Clone)]
386pub(super) struct QlogSinkWithTime<'a> {
387 #[cfg(feature = "qlog")]
388 sink: &'a QlogSink,
389 #[cfg(feature = "qlog")]
390 now: Instant,
391 #[cfg(not(feature = "qlog"))]
392 _phantom: PhantomData<&'a ()>,
393}
394
395impl<'a> QlogSinkWithTime<'a> {
396 pub(super) fn emit_timer_stop(&self, timer: Timer) {
397 #[cfg(feature = "qlog")]
398 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
399 }
400
401 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
402 #[cfg(feature = "qlog")]
403 self.sink
404 .emit_timer(timer, TimerOp::Set(expire_at), self.now)
405 }
406
407 pub(super) fn emit_timer_expire(&self, timer: Timer) {
408 #[cfg(feature = "qlog")]
409 self.sink.emit_timer(timer, TimerOp::Expire, self.now)
410 }
411}
412
413#[cfg(feature = "qlog")]
414enum TimerOp {
415 Set(Instant),
416 Expire,
417 Cancelled,
418}
419
420#[derive(Default)]
422pub(crate) struct QlogSentPacket {
423 #[cfg(feature = "qlog")]
424 inner: PacketSent,
425}
426
427impl QlogSentPacket {
428 pub(crate) fn header(
430 &mut self,
431 header: &Header,
432 pn: Option<u64>,
433 encryption_level: EncryptionLevel,
434 path_id: PathId,
435 ) {
436 #[cfg(feature = "qlog")]
437 {
438 self.inner.header.scid = header.src_cid().map(stringify_cid);
439 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
440 self.inner.header.packet_number = pn;
441 self.inner.header.packet_type = encryption_level.into();
442 self.inner.header.path_id = Some(path_id.as_u32() as u64);
443 }
444 }
445
446 pub(crate) fn frame_padding(&mut self, count: usize) {
450 #[cfg(feature = "qlog")]
451 self.frame_raw(QuicFrame::Padding {
452 raw: Some(Box::new(RawInfo {
453 length: Some(count as u64),
454 payload_length: Some(count as u64),
455 data: None,
456 })),
457 });
458 }
459
460 #[cfg(feature = "qlog")]
465 fn frame_raw(&mut self, frame: QuicFrame) {
466 self.inner.frames.get_or_insert_default().push(frame);
467 }
468
469 pub(super) fn finalize(&mut self, len: usize) {
471 #[cfg(feature = "qlog")]
472 {
473 self.inner.header.length = Some(len as u16);
474 }
475 }
476
477 pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) {
478 #[cfg(feature = "qlog")]
479 self.frame_raw(frame.to_qlog());
480 }
481}
482
483pub(crate) struct QlogRecvPacket {
485 #[cfg(feature = "qlog")]
486 inner: PacketReceived,
487 #[cfg(feature = "qlog")]
488 padding: usize,
489}
490
491impl QlogRecvPacket {
492 pub(crate) fn new(len: usize) -> Self {
496 #[cfg(not(feature = "qlog"))]
497 let this = Self {};
498
499 #[cfg(feature = "qlog")]
500 let this = {
501 let mut this = Self {
502 inner: Default::default(),
503 padding: 0,
504 };
505 this.inner.header.length = Some(len as u16);
506 this
507 };
508
509 this
510 }
511
512 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
514 #[cfg(feature = "qlog")]
515 {
516 let is_0rtt = !header.is_1rtt();
517 self.inner.header.scid = header.src_cid().map(stringify_cid);
518 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
519 self.inner.header.packet_number = pn;
520 self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
521 self.inner.header.path_id = Some(path_id.as_u32() as u64);
522 }
523 }
524
525 pub(crate) fn frame(&mut self, frame: &Frame) {
527 #[cfg(feature = "qlog")]
528 {
529 if matches!(frame, crate::Frame::Padding) {
530 self.padding += 1;
531 } else {
532 self.emit_padding();
533 self.inner
534 .frames
535 .get_or_insert_default()
536 .push(frame.to_qlog())
537 }
538 }
539 }
540
541 #[cfg(feature = "qlog")]
542 fn emit_padding(&mut self) {
543 if self.padding > 0 {
544 self.inner
545 .frames
546 .get_or_insert_default()
547 .push(QuicFrame::Padding {
548 raw: Some(Box::new(RawInfo {
549 length: Some(self.padding as u64),
550 payload_length: Some(self.padding as u64),
551 data: None,
552 })),
553 });
554 self.padding = 0;
555 }
556 }
557}
558
559#[cfg(feature = "qlog")]
561pub(crate) trait ToQlog {
562 fn to_qlog(&self) -> QuicFrame;
563}
564
565#[cfg(feature = "qlog")]
566impl<'a> ToQlog for frame::AckEncoder<'a> {
567 fn to_qlog(&self) -> QuicFrame {
568 QuicFrame::Ack {
569 ack_delay: Some(self.delay as f32),
570 acked_ranges: Some(
571 self.ranges
572 .iter()
573 .map(|range| AckRange::new(range.start, range.end))
574 .collect(),
575 ),
576 ect1: self.ecn.map(|e| e.ect1),
577 ect0: self.ecn.map(|e| e.ect0),
578 ce: self.ecn.map(|e| e.ce),
579 raw: None,
580 }
581 }
582}
583
584#[cfg(feature = "qlog")]
585impl ToQlog for frame::AckFrequency {
586 fn to_qlog(&self) -> QuicFrame {
587 QuicFrame::AckFrequency {
588 sequence_number: self.sequence.into_inner(),
589 ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(),
590 requested_max_ack_delay: self.request_max_ack_delay.into_inner(),
591 reordering_threshold: self.reordering_threshold.into_inner(),
592 raw: None,
593 }
594 }
595}
596
597#[cfg(feature = "qlog")]
598impl ToQlog for frame::AddAddress {
599 fn to_qlog(&self) -> QuicFrame {
600 QuicFrame::AddAddress {
601 sequence_number: self.seq_no.into_inner(),
602 ip_v4: match self.ip {
603 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
604 IpAddr::V6(ipv6_addr) => None,
605 },
606 ip_v6: match self.ip {
607 IpAddr::V4(ipv4_addr) => None,
608 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
609 },
610 port: self.port,
611 }
612 }
613}
614
615#[cfg(feature = "qlog")]
616impl ToQlog for frame::CloseEncoder<'_> {
617 fn to_qlog(&self) -> QuicFrame {
618 self.close.to_qlog()
619 }
620}
621
622#[cfg(feature = "qlog")]
623impl ToQlog for frame::Close {
624 fn to_qlog(&self) -> QuicFrame {
625 match self {
626 Self::Connection(f) => {
627 let (error, error_code) = transport_error(f.error_code);
628 let error = error.map(|transport_error| {
629 ConnectionClosedFrameError::TransportError(transport_error)
630 });
631 QuicFrame::ConnectionClose {
632 error_space: Some(ErrorSpace::Transport),
633 error,
634 error_code,
635 reason: String::from_utf8(f.reason.to_vec()).ok(),
636 reason_bytes: None,
637 trigger_frame_type: None,
638 }
639 }
640 Self::Application(f) => QuicFrame::ConnectionClose {
641 error_space: Some(ErrorSpace::Application),
642 error: None,
643 error_code: Some(f.error_code.into_inner()),
644 reason: String::from_utf8(f.reason.to_vec()).ok(),
645 reason_bytes: None,
646 trigger_frame_type: None,
647 },
648 }
649 }
650}
651
652#[cfg(feature = "qlog")]
653impl ToQlog for frame::Crypto {
654 fn to_qlog(&self) -> QuicFrame {
655 QuicFrame::Crypto {
656 offset: self.offset,
657 raw: Some(Box::new(RawInfo {
658 length: Some(self.data.len() as u64),
659 ..Default::default()
660 })),
661 }
662 }
663}
664
665#[cfg(feature = "qlog")]
666impl ToQlog for frame::Datagram {
667 fn to_qlog(&self) -> QuicFrame {
668 QuicFrame::Datagram {
669 raw: Some(Box::new(RawInfo {
670 length: Some(self.data.len() as u64),
671 ..Default::default()
672 })),
673 }
674 }
675}
676
677#[cfg(feature = "qlog")]
678impl ToQlog for frame::HandshakeDone {
679 fn to_qlog(&self) -> QuicFrame {
680 QuicFrame::HandshakeDone { raw: None }
681 }
682}
683
684#[cfg(feature = "qlog")]
685impl ToQlog for frame::ImmediateAck {
686 fn to_qlog(&self) -> QuicFrame {
687 QuicFrame::ImmediateAck { raw: None }
688 }
689}
690
691#[cfg(feature = "qlog")]
692impl ToQlog for frame::MaxData {
693 fn to_qlog(&self) -> QuicFrame {
694 QuicFrame::MaxData {
695 maximum: self.0.into(),
696 raw: None,
697 }
698 }
699}
700
701#[cfg(feature = "qlog")]
702impl ToQlog for frame::MaxPathId {
703 fn to_qlog(&self) -> QuicFrame {
704 QuicFrame::MaxPathId {
705 maximum_path_id: self.0.as_u32().into(),
706 raw: None,
707 }
708 }
709}
710
711#[cfg(feature = "qlog")]
712impl ToQlog for frame::MaxStreamData {
713 fn to_qlog(&self) -> QuicFrame {
714 QuicFrame::MaxStreamData {
715 stream_id: self.id.into(),
716 maximum: self.offset,
717 raw: None,
718 }
719 }
720}
721
722#[cfg(feature = "qlog")]
723impl ToQlog for frame::MaxStreams {
724 fn to_qlog(&self) -> QuicFrame {
725 QuicFrame::MaxStreams {
726 maximum: self.count,
727 stream_type: self.dir.into(),
728 raw: None,
729 }
730 }
731}
732
733#[cfg(feature = "qlog")]
734impl ToQlog for frame::StreamsBlocked {
735 fn to_qlog(&self) -> QuicFrame {
736 QuicFrame::StreamsBlocked {
737 stream_type: self.dir.into(),
738 limit: self.limit,
739 raw: None,
740 }
741 }
742}
743
744#[cfg(feature = "qlog")]
745impl ToQlog for frame::NewConnectionId {
746 fn to_qlog(&self) -> QuicFrame {
747 match self.path_id {
748 None => QuicFrame::NewConnectionId {
749 sequence_number: self.sequence,
750 retire_prior_to: self.retire_prior_to,
751 connection_id_length: Some(self.id.len() as u8),
752 connection_id: self.id.to_string(),
753 stateless_reset_token: Some(self.reset_token.to_string()),
754 raw: None,
755 },
756 Some(path_id) => QuicFrame::PathNewConnectionId {
757 path_id: path_id.0 as u64,
758 sequence_number: self.sequence,
759 retire_prior_to: self.retire_prior_to,
760 connection_id_length: Some(self.id.len() as u8),
761 connection_id: self.id.to_string(),
762 stateless_reset_token: Some(self.reset_token.to_string()),
763 raw: None,
764 },
765 }
766 }
767}
768
769#[cfg(feature = "qlog")]
770impl ToQlog for frame::NewToken {
771 fn to_qlog(&self) -> QuicFrame {
772 QuicFrame::NewToken {
773 token: qlog::Token {
774 ty: Some(TokenType::Retry),
775 raw: Some(RawInfo {
776 data: HexSlice::maybe_string(Some(&self.token)).map(Box::new),
777 length: Some(self.token.len() as u64),
778 payload_length: None,
779 }),
780 details: None,
781 },
782 raw: None,
783 }
784 }
785}
786
787#[cfg(feature = "qlog")]
788impl ToQlog for frame::ObservedAddr {
789 fn to_qlog(&self) -> QuicFrame {
790 QuicFrame::ObservedAddress {
791 sequence_number: self.seq_no.into_inner(),
792 ip_v4: match self.ip {
793 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
794 IpAddr::V6(ipv6_addr) => None,
795 },
796 ip_v6: match self.ip {
797 IpAddr::V4(ipv4_addr) => None,
798 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
799 },
800 port: self.port,
801 raw: None,
802 }
803 }
804}
805
806#[cfg(feature = "qlog")]
807impl ToQlog for frame::PathAbandon {
808 fn to_qlog(&self) -> QuicFrame {
809 QuicFrame::PathAbandon {
810 path_id: self.path_id.as_u32().into(),
811 error_code: self.error_code.into(),
812 raw: None,
813 }
814 }
815}
816
817#[cfg(feature = "qlog")]
818impl ToQlog for frame::PathAckEncoder<'_> {
819 fn to_qlog(&self) -> QuicFrame {
820 QuicFrame::PathAck {
821 path_id: self.path_id.as_u32() as u64,
822 ack_delay: Some(self.delay as f32),
823 acked_ranges: Some(
824 self.ranges
825 .iter()
826 .map(|range| AckRange::new(range.start, range.end))
827 .collect(),
828 ),
829 ect1: self.ecn.map(|e| e.ect1),
830 ect0: self.ecn.map(|e| e.ect0),
831 ce: self.ecn.map(|e| e.ce),
832 raw: None,
833 }
834 }
835}
836
837#[cfg(feature = "qlog")]
838impl ToQlog for frame::PathChallenge {
839 #[cfg(feature = "qlog")]
840 fn to_qlog(&self) -> QuicFrame {
841 QuicFrame::PathChallenge {
842 data: Some(self.to_string()),
843 raw: None,
844 }
845 }
846}
847
848#[cfg(feature = "qlog")]
849impl ToQlog for frame::PathCidsBlocked {
850 fn to_qlog(&self) -> QuicFrame {
851 QuicFrame::PathCidsBlocked {
852 path_id: self.path_id.as_u32().into(),
853 next_sequence_number: self.next_seq.into(),
854 raw: None,
855 }
856 }
857}
858
859#[cfg(feature = "qlog")]
860impl ToQlog for frame::PathResponse {
861 fn to_qlog(&self) -> QuicFrame {
862 QuicFrame::PathResponse {
863 data: Some(self.to_string()),
864 raw: None,
865 }
866 }
867}
868
869#[cfg(feature = "qlog")]
870impl ToQlog for frame::ReachOut {
871 fn to_qlog(&self) -> QuicFrame {
872 QuicFrame::ReachOut {
873 round: self.round.into_inner(),
874 ip_v4: match self.ip {
875 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
876 IpAddr::V6(ipv6_addr) => None,
877 },
878 ip_v6: match self.ip {
879 IpAddr::V4(ipv4_addr) => None,
880 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
881 },
882 port: self.port,
883 }
884 }
885}
886
887#[cfg(feature = "qlog")]
888impl ToQlog for frame::Ping {
889 fn to_qlog(&self) -> QuicFrame {
890 QuicFrame::Ping { raw: None }
891 }
892}
893
894#[cfg(feature = "qlog")]
895impl ToQlog for frame::PathStatusAvailable {
896 fn to_qlog(&self) -> QuicFrame {
897 QuicFrame::PathStatusAvailable {
898 path_id: self.path_id.as_u32().into(),
899 path_status_sequence_number: self.status_seq_no.into(),
900 raw: None,
901 }
902 }
903}
904
905#[cfg(feature = "qlog")]
906impl ToQlog for frame::PathStatusBackup {
907 fn to_qlog(&self) -> QuicFrame {
908 QuicFrame::PathStatusBackup {
909 path_id: self.path_id.as_u32().into(),
910 path_status_sequence_number: self.status_seq_no.into(),
911 raw: None,
912 }
913 }
914}
915
916#[cfg(feature = "qlog")]
917impl ToQlog for frame::PathsBlocked {
918 fn to_qlog(&self) -> QuicFrame {
919 QuicFrame::PathsBlocked {
920 maximum_path_id: self.0.as_u32().into(),
921 raw: None,
922 }
923 }
924}
925
926#[cfg(feature = "qlog")]
927impl ToQlog for frame::ResetStream {
928 fn to_qlog(&self) -> QuicFrame {
929 QuicFrame::ResetStream {
930 stream_id: self.id.into(),
931 error_code: Some(self.error_code.into_inner()),
932 final_size: self.final_offset.into(),
933 error: ApplicationError::Unknown,
934 raw: None,
935 }
936 }
937}
938
939#[cfg(feature = "qlog")]
940impl ToQlog for frame::StopSending {
941 fn to_qlog(&self) -> QuicFrame {
942 QuicFrame::StopSending {
943 stream_id: self.id.into(),
944 error_code: Some(self.error_code.into_inner()),
945 error: ApplicationError::Unknown,
946 raw: None,
947 }
948 }
949}
950
951#[cfg(feature = "qlog")]
952impl ToQlog for frame::RetireConnectionId {
953 fn to_qlog(&self) -> QuicFrame {
954 match self.path_id {
955 None => QuicFrame::RetireConnectionId {
956 sequence_number: self.sequence,
957 raw: None,
958 },
959 Some(path_id) => QuicFrame::PathRetireConnectionId {
960 path_id: path_id.0 as u64,
961 sequence_number: self.sequence,
962 raw: None,
963 },
964 }
965 }
966}
967
968#[cfg(feature = "qlog")]
969impl ToQlog for frame::RemoveAddress {
970 fn to_qlog(&self) -> QuicFrame {
971 QuicFrame::RemoveAddress {
972 sequence_number: self.seq_no.into_inner(),
973 }
974 }
975}
976
977#[cfg(feature = "qlog")]
978impl ToQlog for frame::StreamMetaEncoder {
979 fn to_qlog(&self) -> QuicFrame {
980 let meta = &self.meta;
981 QuicFrame::Stream {
982 stream_id: meta.id.into(),
983 offset: Some(meta.offsets.start),
984 fin: Some(meta.fin),
985 raw: Some(Box::new(RawInfo {
986 length: Some(meta.offsets.end - meta.offsets.start),
987 ..Default::default()
988 })),
989 }
990 }
991}
992
993#[cfg(feature = "qlog")]
994impl Frame {
995 pub(crate) fn to_qlog(&self) -> QuicFrame {
997 match self {
998 Self::Padding => QuicFrame::Padding {
999 raw: Some(Box::new(RawInfo {
1000 length: None,
1001 payload_length: Some(1),
1002 data: None,
1003 })),
1004 },
1005 Self::Ping => frame::Ping.to_qlog(),
1006 Self::Ack(f) => QuicFrame::Ack {
1007 ack_delay: Some(f.delay as f32),
1008 acked_ranges: Some(
1009 f.iter()
1010 .map(|range| AckRange::new(range.start, range.end))
1011 .collect(),
1012 ),
1013 ect1: f.ecn.as_ref().map(|e| e.ect1),
1014 ect0: f.ecn.as_ref().map(|e| e.ect0),
1015 ce: f.ecn.as_ref().map(|e| e.ce),
1016 raw: None,
1017 },
1018 Self::ResetStream(f) => f.to_qlog(),
1019 Self::StopSending(f) => f.to_qlog(),
1020 Self::Crypto(f) => f.to_qlog(),
1021 Self::NewToken(f) => f.to_qlog(),
1022 Self::Stream(s) => QuicFrame::Stream {
1023 stream_id: s.id.into(),
1024 offset: Some(s.offset),
1025 fin: Some(s.fin),
1026 raw: Some(Box::new(RawInfo {
1027 length: Some(s.data.len() as u64),
1028 ..Default::default()
1029 })),
1030 },
1031 Self::MaxData(v) => v.to_qlog(),
1032 Self::MaxStreamData(f) => f.to_qlog(),
1033 Self::MaxStreams(f) => f.to_qlog(),
1034 Self::DataBlocked(DataBlocked(offset)) => QuicFrame::DataBlocked {
1035 limit: *offset,
1036 raw: None,
1037 },
1038 Self::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
1039 QuicFrame::StreamDataBlocked {
1040 stream_id: (*id).into(),
1041 limit: *offset,
1042 raw: None,
1043 }
1044 }
1045 Self::StreamsBlocked(StreamsBlocked { dir, limit }) => QuicFrame::StreamsBlocked {
1046 stream_type: (*dir).into(),
1047 limit: *limit,
1048 raw: None,
1049 },
1050 Self::NewConnectionId(f) => f.to_qlog(),
1051 Self::RetireConnectionId(f) => f.to_qlog(),
1052 Self::PathChallenge(f) => f.to_qlog(),
1053 Self::PathResponse(f) => f.to_qlog(),
1054 Self::Close(close) => close.to_qlog(),
1055 Self::Datagram(d) => d.to_qlog(),
1056 Self::HandshakeDone => frame::HandshakeDone.to_qlog(),
1057 Self::PathAck(ack) => QuicFrame::PathAck {
1058 path_id: ack.path_id.as_u32().into(),
1059 ack_delay: Some(ack.delay as f32),
1060 ect1: ack.ecn.as_ref().map(|e| e.ect1),
1061 ect0: ack.ecn.as_ref().map(|e| e.ect0),
1062 ce: ack.ecn.as_ref().map(|e| e.ce),
1063 raw: None,
1064 acked_ranges: Some(
1065 ack.ranges
1066 .iter()
1067 .map(|range| AckRange::new(range.start, range.end))
1068 .collect(),
1069 ),
1070 },
1071 Self::PathAbandon(frame) => frame.to_qlog(),
1072 Self::PathStatusAvailable(frame) => frame.to_qlog(),
1073 Self::PathStatusBackup(frame) => frame.to_qlog(),
1074 Self::PathsBlocked(frame) => frame.to_qlog(),
1075 Self::PathCidsBlocked(frame) => frame.to_qlog(),
1076 Self::MaxPathId(f) => f.to_qlog(),
1077 Self::AckFrequency(f) => f.to_qlog(),
1078 Self::ImmediateAck => frame::ImmediateAck.to_qlog(),
1079 Self::ObservedAddr(f) => f.to_qlog(),
1080 Self::AddAddress(f) => f.to_qlog(),
1081 Self::ReachOut(f) => f.to_qlog(),
1082 Self::RemoveAddress(f) => f.to_qlog(),
1083 }
1084 }
1085}
1086
1087#[cfg(feature = "qlog")]
1088impl From<crate::Dir> for StreamType {
1089 fn from(value: crate::Dir) -> Self {
1090 match value {
1091 crate::Dir::Bi => Self::Bidirectional,
1092 crate::Dir::Uni => Self::Unidirectional,
1093 }
1094 }
1095}
1096
1097#[cfg(feature = "qlog")]
1098fn packet_type(space: SpaceKind, is_0rtt: bool) -> PacketType {
1099 match space {
1100 SpaceKind::Initial => PacketType::Initial,
1101 SpaceKind::Handshake => PacketType::Handshake,
1102 SpaceKind::Data if is_0rtt => PacketType::ZeroRtt,
1103 SpaceKind::Data => PacketType::OneRtt,
1104 }
1105}
1106
1107#[cfg(feature = "qlog")]
1108impl From<EncryptionLevel> for PacketType {
1109 fn from(encryption_level: EncryptionLevel) -> Self {
1110 match encryption_level {
1111 EncryptionLevel::Initial => Self::Initial,
1112 EncryptionLevel::Handshake => Self::Handshake,
1113 EncryptionLevel::ZeroRtt => Self::ZeroRtt,
1114 EncryptionLevel::OneRtt => Self::OneRtt,
1115 }
1116 }
1117}
1118
1119#[cfg(feature = "qlog")]
1120fn stringify_cid(cid: ConnectionId) -> String {
1121 format!("{cid}")
1122}
1123
1124#[cfg(feature = "qlog")]
1125fn tuple_endpoint_info(
1126 ip: Option<IpAddr>,
1127 port: Option<u16>,
1128 cid: Option<ConnectionId>,
1129) -> TupleEndpointInfo {
1130 let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
1131 Some(addr) => match addr {
1132 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
1133 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
1134 },
1135 None => (None, None, None, None),
1136 };
1137 TupleEndpointInfo {
1138 ip_v4,
1139 port_v4,
1140 ip_v6,
1141 port_v6,
1142 connection_ids: cid.map(|cid| vec![cid.to_string()]),
1143 }
1144}
1145
1146#[cfg(feature = "qlog")]
1147fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
1148 let transport_error = match code {
1149 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
1150 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
1151 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
1152 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
1153 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
1154 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
1155 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
1156 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
1157 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
1158 Some(quic::TransportError::TransportParameterError)
1159 }
1160 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
1161 Some(quic::TransportError::ConnectionIdLimitError)
1162 }
1163 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
1164 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
1165 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
1166 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
1167 Some(quic::TransportError::CryptoBufferExceeded)
1168 }
1169 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
1170 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
1171 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
1172 TransportErrorCode::APPLICATION_ABANDON_PATH => {
1174 Some(quic::TransportError::ApplicationAbandonPath)
1175 }
1176 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
1177 Some(quic::TransportError::PathResourceLimitReached)
1178 }
1179 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
1180 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
1181 Some(quic::TransportError::NoCidsAvailableForPath)
1182 }
1183 _ => None,
1184 };
1185 let code = match transport_error {
1186 None => Some(code.into()),
1187 Some(_) => None,
1188 };
1189 (transport_error, code)
1190}
1191
1192#[cfg(feature = "qlog")]
1193fn fmt_tuple_id(path_id: u64) -> String {
1194 format!("p{path_id}")
1195}
1196
1197#[cfg(feature = "qlog")]
1198impl TransportParameters {
1199 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
1200 ParametersSet {
1201 initiator: Some(initiator),
1202 resumption_allowed: None,
1203 early_data_enabled: None,
1204 tls_cipher: None,
1205 original_destination_connection_id: self
1206 .original_dst_cid
1207 .as_ref()
1208 .map(ToString::to_string),
1209 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
1210 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
1211 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
1212 disable_active_migration: Some(self.disable_active_migration),
1213 max_idle_timeout: Some(self.max_idle_timeout.into()),
1214 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1215 ack_delay_exponent: Some(self.ack_delay_exponent.into()),
1216 max_ack_delay: Some(self.max_ack_delay.into()),
1217 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1218 initial_max_data: Some(self.initial_max_data.into()),
1219 initial_max_stream_data_bidi_local: Some(
1220 self.initial_max_stream_data_bidi_local.into(),
1221 ),
1222 initial_max_stream_data_bidi_remote: Some(
1223 self.initial_max_stream_data_bidi_remote.into(),
1224 ),
1225 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1226 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1227 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1228 preferred_address: self.preferred_address.as_ref().map(Into::into),
1229 min_ack_delay: self.min_ack_delay.map(Into::into),
1230 address_discovery: self.address_discovery_role.to_qlog(),
1231 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
1232 max_remote_nat_traversal_addresses: self
1233 .max_remote_nat_traversal_addresses
1234 .map(|v| u64::from(v.get())),
1235 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1236 grease_quic_bit: Some(self.grease_quic_bit),
1237 unknown_parameters: Default::default(),
1238 }
1239 }
1240
1241 fn to_qlog_restored(self) -> ParametersRestored {
1242 ParametersRestored {
1243 disable_active_migration: Some(self.disable_active_migration),
1244 max_idle_timeout: Some(self.max_idle_timeout.into()),
1245 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1246 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1247 initial_max_data: Some(self.initial_max_data.into()),
1248 initial_max_stream_data_bidi_local: Some(
1249 self.initial_max_stream_data_bidi_local.into(),
1250 ),
1251 initial_max_stream_data_bidi_remote: Some(
1252 self.initial_max_stream_data_bidi_remote.into(),
1253 ),
1254 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1255 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1256 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1257 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1258 grease_quic_bit: Some(self.grease_quic_bit),
1259 }
1260 }
1261}
1262
1263#[cfg(feature = "qlog")]
1264impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
1265 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
1266 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
1267 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
1268 let ip_v4 = value
1269 .address_v4
1270 .map(|addr| addr.ip().to_string())
1271 .unwrap_or_default();
1272 let ip_v6 = value
1273 .address_v6
1274 .map(|addr| addr.ip().to_string())
1275 .unwrap_or_default();
1276 let connection_id = value.connection_id.to_string();
1277 let stateless_reset_token = value.stateless_reset_token.to_string();
1278
1279 Self {
1280 ip_v4,
1281 ip_v6,
1282 port_v4,
1283 port_v6,
1284 connection_id,
1285 stateless_reset_token,
1286 }
1287 }
1288}
1289
1290#[cfg(feature = "qlog")]
1291impl crate::address_discovery::Role {
1292 fn to_qlog(self) -> Option<AddressDiscoveryRole> {
1293 match (self.send, self.receive) {
1294 (false, false) => None,
1295 (true, false) => Some(AddressDiscoveryRole::SendOnly),
1296 (false, true) => Some(AddressDiscoveryRole::ReceiveOnly),
1297 (true, true) => Some(AddressDiscoveryRole::Both),
1298 }
1299 }
1300}