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