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::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
327 PathTimer::Pacing => Some(TimerType::custom("pacing")),
328 PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
329 PathTimer::PathDrained => Some(TimerType::custom("discard_path")),
330 },
331 };
332
333 let Some(timer_type) = timer_type else {
334 return;
335 };
336
337 let delta = match op {
338 TimerOp::Set(instant) => instant
339 .checked_duration_since(now)
340 .map(|dur| dur.as_secs_f32() * 1000.),
341 _ => None,
342 };
343 let path_id = match timer {
344 Timer::Conn(_) => None,
345 Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
346 };
347
348 let event_type = match op {
349 TimerOp::Set(_) => TimerEventType::Set,
350 TimerOp::Expire => TimerEventType::Expired,
351 TimerOp::Cancelled => TimerEventType::Cancelled,
352 };
353
354 let event = TimerUpdated {
355 path_id,
356 timer_type: Some(timer_type),
357 timer_id: None,
358 packet_number_space: None,
359 event_type,
360 delta,
361 };
362 stream.emit_event(EventData::QuicTimerUpdated(event), now);
363 }
364
365 pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
371 #[cfg(feature = "qlog")]
372 let s = QlogSinkWithTime { sink: self, now };
373 #[cfg(not(feature = "qlog"))]
374 let s = QlogSinkWithTime {
375 _phantom: PhantomData,
376 };
377 s
378 }
379}
380
381#[derive(Clone)]
383pub(super) struct QlogSinkWithTime<'a> {
384 #[cfg(feature = "qlog")]
385 sink: &'a QlogSink,
386 #[cfg(feature = "qlog")]
387 now: Instant,
388 #[cfg(not(feature = "qlog"))]
389 _phantom: PhantomData<&'a ()>,
390}
391
392impl<'a> QlogSinkWithTime<'a> {
393 pub(super) fn emit_timer_stop(&self, timer: Timer) {
394 #[cfg(feature = "qlog")]
395 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
396 }
397
398 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
399 #[cfg(feature = "qlog")]
400 self.sink
401 .emit_timer(timer, TimerOp::Set(expire_at), self.now)
402 }
403
404 pub(super) fn emit_timer_expire(&self, timer: Timer) {
405 #[cfg(feature = "qlog")]
406 self.sink.emit_timer(timer, TimerOp::Expire, self.now)
407 }
408}
409
410#[cfg(feature = "qlog")]
411enum TimerOp {
412 Set(Instant),
413 Expire,
414 Cancelled,
415}
416
417#[derive(Default)]
419pub(crate) struct QlogSentPacket {
420 #[cfg(feature = "qlog")]
421 inner: PacketSent,
422}
423
424impl QlogSentPacket {
425 pub(crate) fn header(
427 &mut self,
428 header: &Header,
429 pn: Option<u64>,
430 encryption_level: EncryptionLevel,
431 path_id: PathId,
432 ) {
433 #[cfg(feature = "qlog")]
434 {
435 self.inner.header.scid = header.src_cid().map(stringify_cid);
436 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
437 self.inner.header.packet_number = pn;
438 self.inner.header.packet_type = encryption_level.into();
439 self.inner.header.path_id = Some(path_id.as_u32() as u64);
440 }
441 }
442
443 pub(crate) fn frame_padding(&mut self, count: usize) {
447 #[cfg(feature = "qlog")]
448 self.frame_raw(QuicFrame::Padding {
449 raw: Some(Box::new(RawInfo {
450 length: Some(count as u64),
451 payload_length: Some(count as u64),
452 data: None,
453 })),
454 });
455 }
456
457 #[cfg(feature = "qlog")]
463 fn frame_raw(&mut self, frame: QuicFrame) {
464 self.inner.frames.get_or_insert_default().push(frame);
465 }
466
467 pub(super) fn finalize(&mut self, len: usize) {
469 #[cfg(feature = "qlog")]
470 {
471 self.inner.header.length = Some(len as u16);
472 }
473 }
474
475 pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) {
476 #[cfg(feature = "qlog")]
477 self.frame_raw(frame.to_qlog());
478 }
479}
480
481pub(crate) struct QlogRecvPacket {
483 #[cfg(feature = "qlog")]
484 inner: PacketReceived,
485 #[cfg(feature = "qlog")]
486 padding: usize,
487}
488
489impl QlogRecvPacket {
490 pub(crate) fn new(len: usize) -> Self {
494 #[cfg(not(feature = "qlog"))]
495 let this = Self {};
496
497 #[cfg(feature = "qlog")]
498 let this = {
499 let mut this = Self {
500 inner: Default::default(),
501 padding: 0,
502 };
503 this.inner.header.length = Some(len as u16);
504 this
505 };
506
507 this
508 }
509
510 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
512 #[cfg(feature = "qlog")]
513 {
514 let is_0rtt = !header.is_1rtt();
515 self.inner.header.scid = header.src_cid().map(stringify_cid);
516 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
517 self.inner.header.packet_number = pn;
518 self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
519 self.inner.header.path_id = Some(path_id.as_u32() as u64);
520 }
521 }
522
523 pub(crate) fn frame(&mut self, frame: &Frame) {
525 #[cfg(feature = "qlog")]
526 {
527 if matches!(frame, Frame::Padding) {
528 self.padding += 1;
529 } else {
530 self.emit_padding();
531 self.inner
532 .frames
533 .get_or_insert_default()
534 .push(frame.to_qlog())
535 }
536 }
537 }
538
539 #[cfg(feature = "qlog")]
540 fn emit_padding(&mut self) {
541 if self.padding > 0 {
542 self.inner
543 .frames
544 .get_or_insert_default()
545 .push(QuicFrame::Padding {
546 raw: Some(Box::new(RawInfo {
547 length: Some(self.padding as u64),
548 payload_length: Some(self.padding as u64),
549 data: None,
550 })),
551 });
552 self.padding = 0;
553 }
554 }
555}
556
557#[cfg(feature = "qlog")]
559pub(crate) trait ToQlog {
560 fn to_qlog(&self) -> QuicFrame;
561}
562
563#[cfg(feature = "qlog")]
564impl<'a> ToQlog for frame::AckEncoder<'a> {
565 fn to_qlog(&self) -> QuicFrame {
566 QuicFrame::Ack {
567 ack_delay: Some(self.delay as f32),
568 acked_ranges: Some(
569 self.ranges
570 .iter()
571 .map(|range| AckRange::new(range.start, range.end))
572 .collect(),
573 ),
574 ect1: self.ecn.map(|e| e.ect1),
575 ect0: self.ecn.map(|e| e.ect0),
576 ce: self.ecn.map(|e| e.ce),
577 raw: None,
578 }
579 }
580}
581
582#[cfg(feature = "qlog")]
583impl ToQlog for frame::AckFrequency {
584 fn to_qlog(&self) -> QuicFrame {
585 QuicFrame::AckFrequency {
586 sequence_number: self.sequence.into_inner(),
587 ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(),
588 requested_max_ack_delay: self.request_max_ack_delay.into_inner(),
589 reordering_threshold: self.reordering_threshold.into_inner(),
590 raw: None,
591 }
592 }
593}
594
595#[cfg(feature = "qlog")]
596impl ToQlog for frame::AddAddress {
597 fn to_qlog(&self) -> QuicFrame {
598 QuicFrame::AddAddress {
599 sequence_number: self.seq_no.into_inner(),
600 ip_v4: match self.ip {
601 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
602 IpAddr::V6(ipv6_addr) => None,
603 },
604 ip_v6: match self.ip {
605 IpAddr::V4(ipv4_addr) => None,
606 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
607 },
608 port: self.port,
609 }
610 }
611}
612
613#[cfg(feature = "qlog")]
614impl ToQlog for frame::CloseEncoder<'_> {
615 fn to_qlog(&self) -> QuicFrame {
616 self.close.to_qlog()
617 }
618}
619
620#[cfg(feature = "qlog")]
621impl ToQlog for frame::Close {
622 fn to_qlog(&self) -> QuicFrame {
623 match self {
624 Self::Connection(f) => {
625 let (error, error_code) = transport_error(f.error_code);
626 let error = error.map(|transport_error| {
627 ConnectionClosedFrameError::TransportError(transport_error)
628 });
629 QuicFrame::ConnectionClose {
630 error_space: Some(ErrorSpace::Transport),
631 error,
632 error_code,
633 reason: String::from_utf8(f.reason.to_vec()).ok(),
634 reason_bytes: None,
635 trigger_frame_type: None,
636 }
637 }
638 Self::Application(f) => QuicFrame::ConnectionClose {
639 error_space: Some(ErrorSpace::Application),
640 error: None,
641 error_code: Some(f.error_code.into_inner()),
642 reason: String::from_utf8(f.reason.to_vec()).ok(),
643 reason_bytes: None,
644 trigger_frame_type: None,
645 },
646 }
647 }
648}
649
650#[cfg(feature = "qlog")]
651impl ToQlog for frame::Crypto {
652 fn to_qlog(&self) -> QuicFrame {
653 QuicFrame::Crypto {
654 offset: self.offset,
655 raw: Some(Box::new(RawInfo {
656 length: Some(self.data.len() as u64),
657 ..Default::default()
658 })),
659 }
660 }
661}
662
663#[cfg(feature = "qlog")]
664impl ToQlog for frame::Datagram {
665 fn to_qlog(&self) -> QuicFrame {
666 QuicFrame::Datagram {
667 raw: Some(Box::new(RawInfo {
668 length: Some(self.data.len() as u64),
669 ..Default::default()
670 })),
671 }
672 }
673}
674
675#[cfg(feature = "qlog")]
676impl ToQlog for frame::HandshakeDone {
677 fn to_qlog(&self) -> QuicFrame {
678 QuicFrame::HandshakeDone { raw: None }
679 }
680}
681
682#[cfg(feature = "qlog")]
683impl ToQlog for frame::ImmediateAck {
684 fn to_qlog(&self) -> QuicFrame {
685 QuicFrame::ImmediateAck { raw: None }
686 }
687}
688
689#[cfg(feature = "qlog")]
690impl ToQlog for frame::MaxData {
691 fn to_qlog(&self) -> QuicFrame {
692 QuicFrame::MaxData {
693 maximum: self.0.into(),
694 raw: None,
695 }
696 }
697}
698
699#[cfg(feature = "qlog")]
700impl ToQlog for frame::MaxPathId {
701 fn to_qlog(&self) -> QuicFrame {
702 QuicFrame::MaxPathId {
703 maximum_path_id: self.0.as_u32().into(),
704 raw: None,
705 }
706 }
707}
708
709#[cfg(feature = "qlog")]
710impl ToQlog for frame::MaxStreamData {
711 fn to_qlog(&self) -> QuicFrame {
712 QuicFrame::MaxStreamData {
713 stream_id: self.id.into(),
714 maximum: self.offset,
715 raw: None,
716 }
717 }
718}
719
720#[cfg(feature = "qlog")]
721impl ToQlog for frame::MaxStreams {
722 fn to_qlog(&self) -> QuicFrame {
723 QuicFrame::MaxStreams {
724 maximum: self.count,
725 stream_type: self.dir.into(),
726 raw: None,
727 }
728 }
729}
730
731#[cfg(feature = "qlog")]
732impl ToQlog for StreamsBlocked {
733 fn to_qlog(&self) -> QuicFrame {
734 QuicFrame::StreamsBlocked {
735 stream_type: self.dir.into(),
736 limit: self.limit,
737 raw: None,
738 }
739 }
740}
741
742#[cfg(feature = "qlog")]
743impl ToQlog for frame::NewConnectionId {
744 fn to_qlog(&self) -> QuicFrame {
745 match self.path_id {
746 None => QuicFrame::NewConnectionId {
747 sequence_number: self.sequence,
748 retire_prior_to: self.retire_prior_to,
749 connection_id_length: Some(self.id.len() as u8),
750 connection_id: self.id.to_string(),
751 stateless_reset_token: Some(self.reset_token.to_string()),
752 raw: None,
753 },
754 Some(path_id) => QuicFrame::PathNewConnectionId {
755 path_id: path_id.0 as u64,
756 sequence_number: self.sequence,
757 retire_prior_to: self.retire_prior_to,
758 connection_id_length: Some(self.id.len() as u8),
759 connection_id: self.id.to_string(),
760 stateless_reset_token: Some(self.reset_token.to_string()),
761 raw: None,
762 },
763 }
764 }
765}
766
767#[cfg(feature = "qlog")]
768impl ToQlog for frame::NewToken {
769 fn to_qlog(&self) -> QuicFrame {
770 QuicFrame::NewToken {
771 token: qlog::Token {
772 ty: Some(TokenType::Retry),
773 raw: Some(RawInfo {
774 data: HexSlice::maybe_string(Some(&self.token)).map(Box::new),
775 length: Some(self.token.len() as u64),
776 payload_length: None,
777 }),
778 details: None,
779 },
780 raw: None,
781 }
782 }
783}
784
785#[cfg(feature = "qlog")]
786impl ToQlog for frame::ObservedAddr {
787 fn to_qlog(&self) -> QuicFrame {
788 QuicFrame::ObservedAddress {
789 sequence_number: self.seq_no.into_inner(),
790 ip_v4: match self.ip {
791 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
792 IpAddr::V6(ipv6_addr) => None,
793 },
794 ip_v6: match self.ip {
795 IpAddr::V4(ipv4_addr) => None,
796 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
797 },
798 port: self.port,
799 raw: None,
800 }
801 }
802}
803
804#[cfg(feature = "qlog")]
805impl ToQlog for frame::PathAbandon {
806 fn to_qlog(&self) -> QuicFrame {
807 QuicFrame::PathAbandon {
808 path_id: self.path_id.as_u32().into(),
809 error_code: self.error_code.into(),
810 raw: None,
811 }
812 }
813}
814
815#[cfg(feature = "qlog")]
816impl ToQlog for frame::PathAckEncoder<'_> {
817 fn to_qlog(&self) -> QuicFrame {
818 QuicFrame::PathAck {
819 path_id: self.path_id.as_u32() as u64,
820 ack_delay: Some(self.delay as f32),
821 acked_ranges: Some(
822 self.ranges
823 .iter()
824 .map(|range| AckRange::new(range.start, range.end))
825 .collect(),
826 ),
827 ect1: self.ecn.map(|e| e.ect1),
828 ect0: self.ecn.map(|e| e.ect0),
829 ce: self.ecn.map(|e| e.ce),
830 raw: None,
831 }
832 }
833}
834
835#[cfg(feature = "qlog")]
836impl ToQlog for frame::PathChallenge {
837 #[cfg(feature = "qlog")]
838 fn to_qlog(&self) -> QuicFrame {
839 QuicFrame::PathChallenge {
840 data: Some(self.to_string()),
841 raw: None,
842 }
843 }
844}
845
846#[cfg(feature = "qlog")]
847impl ToQlog for frame::PathCidsBlocked {
848 fn to_qlog(&self) -> QuicFrame {
849 QuicFrame::PathCidsBlocked {
850 path_id: self.path_id.as_u32().into(),
851 next_sequence_number: self.next_seq.into(),
852 raw: None,
853 }
854 }
855}
856
857#[cfg(feature = "qlog")]
858impl ToQlog for frame::PathResponse {
859 fn to_qlog(&self) -> QuicFrame {
860 QuicFrame::PathResponse {
861 data: Some(self.to_string()),
862 raw: None,
863 }
864 }
865}
866
867#[cfg(feature = "qlog")]
868impl ToQlog for frame::ReachOut {
869 fn to_qlog(&self) -> QuicFrame {
870 QuicFrame::ReachOut {
871 round: self.round.into_inner(),
872 ip_v4: match self.ip {
873 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
874 IpAddr::V6(ipv6_addr) => None,
875 },
876 ip_v6: match self.ip {
877 IpAddr::V4(ipv4_addr) => None,
878 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
879 },
880 port: self.port,
881 }
882 }
883}
884
885#[cfg(feature = "qlog")]
886impl ToQlog for frame::Ping {
887 fn to_qlog(&self) -> QuicFrame {
888 QuicFrame::Ping { raw: None }
889 }
890}
891
892#[cfg(feature = "qlog")]
893impl ToQlog for frame::PathStatusAvailable {
894 fn to_qlog(&self) -> QuicFrame {
895 QuicFrame::PathStatusAvailable {
896 path_id: self.path_id.as_u32().into(),
897 path_status_sequence_number: self.status_seq_no.into(),
898 raw: None,
899 }
900 }
901}
902
903#[cfg(feature = "qlog")]
904impl ToQlog for frame::PathStatusBackup {
905 fn to_qlog(&self) -> QuicFrame {
906 QuicFrame::PathStatusBackup {
907 path_id: self.path_id.as_u32().into(),
908 path_status_sequence_number: self.status_seq_no.into(),
909 raw: None,
910 }
911 }
912}
913
914#[cfg(feature = "qlog")]
915impl ToQlog for frame::PathsBlocked {
916 fn to_qlog(&self) -> QuicFrame {
917 QuicFrame::PathsBlocked {
918 maximum_path_id: self.0.as_u32().into(),
919 raw: None,
920 }
921 }
922}
923
924#[cfg(feature = "qlog")]
925impl ToQlog for frame::ResetStream {
926 fn to_qlog(&self) -> QuicFrame {
927 QuicFrame::ResetStream {
928 stream_id: self.id.into(),
929 error_code: Some(self.error_code.into_inner()),
930 final_size: self.final_offset.into(),
931 error: ApplicationError::Unknown,
932 raw: None,
933 }
934 }
935}
936
937#[cfg(feature = "qlog")]
938impl ToQlog for frame::StopSending {
939 fn to_qlog(&self) -> QuicFrame {
940 QuicFrame::StopSending {
941 stream_id: self.id.into(),
942 error_code: Some(self.error_code.into_inner()),
943 error: ApplicationError::Unknown,
944 raw: None,
945 }
946 }
947}
948
949#[cfg(feature = "qlog")]
950impl ToQlog for frame::RetireConnectionId {
951 fn to_qlog(&self) -> QuicFrame {
952 match self.path_id {
953 None => QuicFrame::RetireConnectionId {
954 sequence_number: self.sequence,
955 raw: None,
956 },
957 Some(path_id) => QuicFrame::PathRetireConnectionId {
958 path_id: path_id.0 as u64,
959 sequence_number: self.sequence,
960 raw: None,
961 },
962 }
963 }
964}
965
966#[cfg(feature = "qlog")]
967impl ToQlog for frame::RemoveAddress {
968 fn to_qlog(&self) -> QuicFrame {
969 QuicFrame::RemoveAddress {
970 sequence_number: self.seq_no.into_inner(),
971 }
972 }
973}
974
975#[cfg(feature = "qlog")]
976impl ToQlog for frame::StreamMetaEncoder {
977 fn to_qlog(&self) -> QuicFrame {
978 let meta = &self.meta;
979 QuicFrame::Stream {
980 stream_id: meta.id.into(),
981 offset: Some(meta.offsets.start),
982 fin: Some(meta.fin),
983 raw: Some(Box::new(RawInfo {
984 length: Some(meta.offsets.end - meta.offsets.start),
985 ..Default::default()
986 })),
987 }
988 }
989}
990
991#[cfg(feature = "qlog")]
992impl Frame {
993 pub(crate) fn to_qlog(&self) -> QuicFrame {
995 match self {
996 Self::Padding => QuicFrame::Padding {
997 raw: Some(Box::new(RawInfo {
998 length: None,
999 payload_length: Some(1),
1000 data: None,
1001 })),
1002 },
1003 Self::Ping => frame::Ping.to_qlog(),
1004 Self::Ack(f) => QuicFrame::Ack {
1005 ack_delay: Some(f.delay as f32),
1006 acked_ranges: Some(
1007 f.iter()
1008 .map(|range| AckRange::new(range.start, range.end))
1009 .collect(),
1010 ),
1011 ect1: f.ecn.as_ref().map(|e| e.ect1),
1012 ect0: f.ecn.as_ref().map(|e| e.ect0),
1013 ce: f.ecn.as_ref().map(|e| e.ce),
1014 raw: None,
1015 },
1016 Self::ResetStream(f) => f.to_qlog(),
1017 Self::StopSending(f) => f.to_qlog(),
1018 Self::Crypto(f) => f.to_qlog(),
1019 Self::NewToken(f) => f.to_qlog(),
1020 Self::Stream(s) => QuicFrame::Stream {
1021 stream_id: s.id.into(),
1022 offset: Some(s.offset),
1023 fin: Some(s.fin),
1024 raw: Some(Box::new(RawInfo {
1025 length: Some(s.data.len() as u64),
1026 ..Default::default()
1027 })),
1028 },
1029 Self::MaxData(v) => v.to_qlog(),
1030 Self::MaxStreamData(f) => f.to_qlog(),
1031 Self::MaxStreams(f) => f.to_qlog(),
1032 Self::DataBlocked(DataBlocked(offset)) => QuicFrame::DataBlocked {
1033 limit: *offset,
1034 raw: None,
1035 },
1036 Self::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
1037 QuicFrame::StreamDataBlocked {
1038 stream_id: (*id).into(),
1039 limit: *offset,
1040 raw: None,
1041 }
1042 }
1043 Self::StreamsBlocked(StreamsBlocked { dir, limit }) => QuicFrame::StreamsBlocked {
1044 stream_type: (*dir).into(),
1045 limit: *limit,
1046 raw: None,
1047 },
1048 Self::NewConnectionId(f) => f.to_qlog(),
1049 Self::RetireConnectionId(f) => f.to_qlog(),
1050 Self::PathChallenge(f) => f.to_qlog(),
1051 Self::PathResponse(f) => f.to_qlog(),
1052 Self::Close(close) => close.to_qlog(),
1053 Self::Datagram(d) => d.to_qlog(),
1054 Self::HandshakeDone => frame::HandshakeDone.to_qlog(),
1055 Self::PathAck(ack) => QuicFrame::PathAck {
1056 path_id: ack.path_id.as_u32().into(),
1057 ack_delay: Some(ack.delay as f32),
1058 ect1: ack.ecn.as_ref().map(|e| e.ect1),
1059 ect0: ack.ecn.as_ref().map(|e| e.ect0),
1060 ce: ack.ecn.as_ref().map(|e| e.ce),
1061 raw: None,
1062 acked_ranges: Some(
1063 ack.ranges
1064 .iter()
1065 .map(|range| AckRange::new(range.start, range.end))
1066 .collect(),
1067 ),
1068 },
1069 Self::PathAbandon(frame) => frame.to_qlog(),
1070 Self::PathStatusAvailable(frame) => frame.to_qlog(),
1071 Self::PathStatusBackup(frame) => frame.to_qlog(),
1072 Self::PathsBlocked(frame) => frame.to_qlog(),
1073 Self::PathCidsBlocked(frame) => frame.to_qlog(),
1074 Self::MaxPathId(f) => f.to_qlog(),
1075 Self::AckFrequency(f) => f.to_qlog(),
1076 Self::ImmediateAck => frame::ImmediateAck.to_qlog(),
1077 Self::ObservedAddr(f) => f.to_qlog(),
1078 Self::AddAddress(f) => f.to_qlog(),
1079 Self::ReachOut(f) => f.to_qlog(),
1080 Self::RemoveAddress(f) => f.to_qlog(),
1081 }
1082 }
1083}
1084
1085#[cfg(feature = "qlog")]
1086impl From<crate::Dir> for StreamType {
1087 fn from(value: crate::Dir) -> Self {
1088 match value {
1089 crate::Dir::Bi => Self::Bidirectional,
1090 crate::Dir::Uni => Self::Unidirectional,
1091 }
1092 }
1093}
1094
1095#[cfg(feature = "qlog")]
1096fn packet_type(space: SpaceKind, is_0rtt: bool) -> PacketType {
1097 match space {
1098 SpaceKind::Initial => PacketType::Initial,
1099 SpaceKind::Handshake => PacketType::Handshake,
1100 SpaceKind::Data if is_0rtt => PacketType::ZeroRtt,
1101 SpaceKind::Data => PacketType::OneRtt,
1102 }
1103}
1104
1105#[cfg(feature = "qlog")]
1106impl From<EncryptionLevel> for PacketType {
1107 fn from(encryption_level: EncryptionLevel) -> Self {
1108 match encryption_level {
1109 EncryptionLevel::Initial => Self::Initial,
1110 EncryptionLevel::Handshake => Self::Handshake,
1111 EncryptionLevel::ZeroRtt => Self::ZeroRtt,
1112 EncryptionLevel::OneRtt => Self::OneRtt,
1113 }
1114 }
1115}
1116
1117#[cfg(feature = "qlog")]
1118fn stringify_cid(cid: ConnectionId) -> String {
1119 format!("{cid}")
1120}
1121
1122#[cfg(feature = "qlog")]
1123fn tuple_endpoint_info(
1124 ip: Option<IpAddr>,
1125 port: Option<u16>,
1126 cid: Option<ConnectionId>,
1127) -> TupleEndpointInfo {
1128 let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
1129 Some(addr) => match addr {
1130 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
1131 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
1132 },
1133 None => (None, None, None, None),
1134 };
1135 TupleEndpointInfo {
1136 ip_v4,
1137 port_v4,
1138 ip_v6,
1139 port_v6,
1140 connection_ids: cid.map(|cid| vec![cid.to_string()]),
1141 }
1142}
1143
1144#[cfg(feature = "qlog")]
1145fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
1146 let transport_error = match code {
1147 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
1148 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
1149 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
1150 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
1151 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
1152 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
1153 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
1154 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
1155 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
1156 Some(quic::TransportError::TransportParameterError)
1157 }
1158 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
1159 Some(quic::TransportError::ConnectionIdLimitError)
1160 }
1161 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
1162 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
1163 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
1164 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
1165 Some(quic::TransportError::CryptoBufferExceeded)
1166 }
1167 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
1168 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
1169 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
1170 TransportErrorCode::APPLICATION_ABANDON_PATH => {
1172 Some(quic::TransportError::ApplicationAbandonPath)
1173 }
1174 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
1175 Some(quic::TransportError::PathResourceLimitReached)
1176 }
1177 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
1178 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
1179 Some(quic::TransportError::NoCidsAvailableForPath)
1180 }
1181 _ => None,
1182 };
1183 let code = match transport_error {
1184 None => Some(code.into()),
1185 Some(_) => None,
1186 };
1187 (transport_error, code)
1188}
1189
1190#[cfg(feature = "qlog")]
1191fn fmt_tuple_id(path_id: u64) -> String {
1192 format!("p{path_id}")
1193}
1194
1195#[cfg(feature = "qlog")]
1196impl TransportParameters {
1197 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
1198 ParametersSet {
1199 initiator: Some(initiator),
1200 resumption_allowed: None,
1201 early_data_enabled: None,
1202 tls_cipher: None,
1203 original_destination_connection_id: self
1204 .original_dst_cid
1205 .as_ref()
1206 .map(ToString::to_string),
1207 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
1208 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
1209 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
1210 disable_active_migration: Some(self.disable_active_migration),
1211 max_idle_timeout: Some(self.max_idle_timeout.into()),
1212 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1213 ack_delay_exponent: Some(self.ack_delay_exponent.into()),
1214 max_ack_delay: Some(self.max_ack_delay.into()),
1215 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1216 initial_max_data: Some(self.initial_max_data.into()),
1217 initial_max_stream_data_bidi_local: Some(
1218 self.initial_max_stream_data_bidi_local.into(),
1219 ),
1220 initial_max_stream_data_bidi_remote: Some(
1221 self.initial_max_stream_data_bidi_remote.into(),
1222 ),
1223 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1224 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1225 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1226 preferred_address: self.preferred_address.as_ref().map(Into::into),
1227 min_ack_delay: self.min_ack_delay.map(Into::into),
1228 address_discovery: self.address_discovery_role.to_qlog(),
1229 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
1230 max_remote_nat_traversal_addresses: self
1231 .max_remote_nat_traversal_addresses
1232 .map(|v| u64::from(v.get())),
1233 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1234 grease_quic_bit: Some(self.grease_quic_bit),
1235 unknown_parameters: Default::default(),
1236 }
1237 }
1238
1239 fn to_qlog_restored(self) -> ParametersRestored {
1240 ParametersRestored {
1241 disable_active_migration: Some(self.disable_active_migration),
1242 max_idle_timeout: Some(self.max_idle_timeout.into()),
1243 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1244 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1245 initial_max_data: Some(self.initial_max_data.into()),
1246 initial_max_stream_data_bidi_local: Some(
1247 self.initial_max_stream_data_bidi_local.into(),
1248 ),
1249 initial_max_stream_data_bidi_remote: Some(
1250 self.initial_max_stream_data_bidi_remote.into(),
1251 ),
1252 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1253 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1254 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1255 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1256 grease_quic_bit: Some(self.grease_quic_bit),
1257 }
1258 }
1259}
1260
1261#[cfg(feature = "qlog")]
1262impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
1263 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
1264 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
1265 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
1266 let ip_v4 = value
1267 .address_v4
1268 .map(|addr| addr.ip().to_string())
1269 .unwrap_or_default();
1270 let ip_v6 = value
1271 .address_v6
1272 .map(|addr| addr.ip().to_string())
1273 .unwrap_or_default();
1274 let connection_id = value.connection_id.to_string();
1275 let stateless_reset_token = value.stateless_reset_token.to_string();
1276
1277 Self {
1278 ip_v4,
1279 ip_v6,
1280 port_v4,
1281 port_v6,
1282 connection_id,
1283 stateless_reset_token,
1284 }
1285 }
1286}
1287
1288#[cfg(feature = "qlog")]
1289impl crate::address_discovery::Role {
1290 fn to_qlog(self) -> Option<AddressDiscoveryRole> {
1291 match (self.send, self.receive) {
1292 (false, false) => None,
1293 (true, false) => Some(AddressDiscoveryRole::SendOnly),
1294 (false, true) => Some(AddressDiscoveryRole::ReceiveOnly),
1295 (true, true) => Some(AddressDiscoveryRole::Both),
1296 }
1297 }
1298}