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