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