diff --git a/.github/workflows/tquic-features.yml b/.github/workflows/tquic-features.yml new file mode 100644 index 00000000..54270377 --- /dev/null +++ b/.github/workflows/tquic-features.yml @@ -0,0 +1,30 @@ +name: Features + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + features: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Update rust + run: rustup update + - name: Build without default features + run: cargo build --all --no-default-features && cargo test --no-default-features + - name: Build with feature(s) ffi + run: cargo build --all --no-default-features -F ffi && cargo test --no-default-features -F ffi + - name: Build with feature(s) qlog + run: cargo build --all --no-default-features -F qlog && cargo test --no-default-features -F qlog + - name: Build with feature(s) ffi,qlog + run: cargo build --all --no-default-features -F ffi,qlog && cargo test --no-default-features -F ffi,qlog + diff --git a/Cargo.toml b/Cargo.toml index 21133693..e72ff607 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,19 @@ include = [ "/deps/boringssl/LICENSE", ] +[package.metadata.docs.rs] +no-default-features = true +features = ["qlog"] + [features] +default = ["qlog"] + # build the FFI API ffi = [] +# enable support for the qlog +qlog = ["dep:serde", "dep:serde_json", "dep:serde_derive", "dep:serde_with"] + [dependencies] bytes = "1" rustc-hash = "1.1" @@ -43,10 +52,10 @@ strum_macros = "0.24" rand = "0.8.5" smallvec = { version = "1.10", features = ["serde", "union"] } lru = "0.12" -serde = { version = "1.0.139", features = ["derive"] } -serde_json = { version = "1.0", features = ["preserve_order"] } -serde_derive = "1.0" -serde_with = "3.0.0" +serde = { version = "1.0.139", features = ["derive"], optional=true } +serde_json = { version = "1.0", features = ["preserve_order"], optional=true } +serde_derive = { version = "1.0", optional=true } +serde_with = { version="3.0.0", optional=true } hex = "0.4" priority-queue = "1.3.2" sfv = { version = "0.9" } diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 2af19cee..cee0ed8c 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -52,7 +52,9 @@ use crate::multipath_scheduler::*; use crate::packet; use crate::packet::PacketHeader; use crate::packet::PacketType; +#[cfg(feature = "qlog")] use crate::qlog; +#[cfg(feature = "qlog")] use crate::qlog::events; use crate::tls; use crate::tls::Keys; @@ -160,6 +162,7 @@ pub struct Connection { context: Option>, /// Qlog writer + #[cfg(feature = "qlog")] qlog: Option, /// Unique trace id for deubg logging @@ -272,6 +275,7 @@ impl Connection { events: EventQueue::default(), queues: None, context: None, + #[cfg(feature = "qlog")] qlog: None, trace_id, }; @@ -357,6 +361,7 @@ impl Connection { /// Set qlog output to the given [`writer`] /// /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html + #[cfg(feature = "qlog")] pub fn set_qlog( &mut self, writer: Box, @@ -603,6 +608,7 @@ impl Connection { // Process each QUIC frame in the QUIC packet let mut ack_eliciting_pkt = false; let mut probing_pkt = true; + #[cfg(feature = "qlog")] let mut qframes = vec![]; while !payload.is_empty() { @@ -613,6 +619,7 @@ impl Connection { if !frame.probing() { probing_pkt = false; } + #[cfg(feature = "qlog")] if self.qlog.is_some() { qframes.push(frame.to_qlog()); } @@ -622,6 +629,7 @@ impl Connection { } // Write events to qlog. + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { // Write TransportPacketReceived event to qlog. Self::qlog_quic_packet_received(qlog, &hdr, pkt_num, read, payload_len, qframes); @@ -750,6 +758,7 @@ impl Connection { space_id, &mut self.spaces, handshake_status, + #[cfg(feature = "qlog")] self.qlog.as_mut(), now, )?; @@ -1205,6 +1214,7 @@ impl Connection { self.flags.insert(AppliedPeerTransportParams); // Write TransportParametersSet event to qlog. + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { Self::qlog_quic_params_set( qlog, @@ -1427,6 +1437,7 @@ impl Connection { .on_stream_frame_acked(stream_id, offset, length); // Write QuicStreamDataMoved event to qlog + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { Self::qlog_quic_data_acked(qlog, stream_id, offset, length); } @@ -1788,6 +1799,7 @@ impl Connection { ); // Write events to qlog. + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { // Write TransportPacketSent event to qlog. let mut qframes = Vec::with_capacity(sent_pkt.frames.len()); @@ -3231,6 +3243,7 @@ impl Connection { path.space_id, &mut self.spaces, handshake_status, + #[cfg(feature = "qlog")] self.qlog.as_mut(), now, ); @@ -3238,6 +3251,7 @@ impl Connection { self.stats.lost_bytes += lost_bytes; // Write RecoveryMetricsUpdate event to qlog. + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { path.recovery.qlog_recovery_metrics_updated(qlog); } @@ -3832,6 +3846,7 @@ impl Connection { match self.streams.stream_read(stream_id, out) { Ok((read, fin)) => { // Write QuicStreamDataMoved event to qlog + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { Self::qlog_transport_data_read(qlog, stream_id, read_off.unwrap_or(0), read); } @@ -3850,6 +3865,7 @@ impl Connection { match self.streams.stream_write(stream_id, buf, fin) { Ok(written) => { // Write QuicStreamDataMoved event to qlog + #[cfg(feature = "qlog")] if let Some(qlog) = &mut self.qlog { Self::qlog_transport_data_write( qlog, @@ -4072,6 +4088,7 @@ impl Connection { } /// Write a QuicParametersSet event to the qlog. + #[cfg(feature = "qlog")] fn qlog_quic_params_set( qlog: &mut qlog::QlogWriter, params: &TransportParams, @@ -4083,6 +4100,7 @@ impl Connection { } /// Write a QuicPacketReceived event to the qlog. + #[cfg(feature = "qlog")] fn qlog_quic_packet_received( qlog: &mut qlog::QlogWriter, hdr: &PacketHeader, @@ -4118,6 +4136,7 @@ impl Connection { } /// Write a QuicPacketSent event to the qlog. + #[cfg(feature = "qlog")] fn qlog_quic_packet_sent( qlog: &mut qlog::QlogWriter, hdr: &PacketHeader, @@ -4156,6 +4175,7 @@ impl Connection { } /// Write a QuicStreamDataMoved event to the qlog. + #[cfg(feature = "qlog")] fn qlog_quic_data_acked( qlog: &mut qlog::QlogWriter, stream_id: u64, @@ -4174,6 +4194,7 @@ impl Connection { } /// Write a QuicStreamDataMoved event to the qlog. + #[cfg(feature = "qlog")] fn qlog_transport_data_read( qlog: &mut qlog::QlogWriter, stream_id: u64, @@ -4192,6 +4213,7 @@ impl Connection { } /// Write a QuicStreamDataMoved event to the qlog. + #[cfg(feature = "qlog")] fn qlog_transport_data_write( qlog: &mut qlog::QlogWriter, stream_id: u64, @@ -6277,6 +6299,7 @@ pub(crate) mod tests { } #[test] + #[cfg(feature = "qlog")] fn ping() -> Result<()> { let mut client_config = TestPair::new_test_config(false)?; client_config.enable_dplpmtud(false); @@ -7638,6 +7661,7 @@ pub(crate) mod tests { } #[test] + #[cfg(feature = "qlog")] fn conn_write_qlog() -> Result<()> { let clog = NamedTempFile::new().unwrap(); let mut cfile = clog.reopen().unwrap(); diff --git a/src/connection/recovery.rs b/src/connection/recovery.rs index bb4c9669..64ec2bcf 100644 --- a/src/connection/recovery.rs +++ b/src/connection/recovery.rs @@ -35,7 +35,9 @@ use crate::congestion_control::CongestionController; use crate::congestion_control::Pacer; use crate::connection::Timer; use crate::frame; +#[cfg(feature = "qlog")] use crate::qlog; +#[cfg(feature = "qlog")] use crate::qlog::events::EventData; use crate::ranges::RangeSet; use crate::Error; @@ -114,6 +116,7 @@ pub struct Recovery { /// It tracks the last metrics used for emitting qlog RecoveryMetricsUpdated /// event. + #[cfg(feature = "qlog")] last_metrics: RecoveryMetrics, /// Trace id. @@ -140,6 +143,7 @@ impl Recovery { cache_pkt_size: conf.max_datagram_size, last_cwnd_limited_time: None, stats: PathStats::default(), + #[cfg(feature = "qlog")] last_metrics: RecoveryMetrics::default(), trace_id: String::from(""), } @@ -228,7 +232,7 @@ impl Recovery { space_id: SpaceId, spaces: &mut PacketNumSpaceMap, handshake_status: HandshakeStatus, - qlog: Option<&mut qlog::QlogWriter>, + #[cfg(feature = "qlog")] qlog: Option<&mut qlog::QlogWriter>, now: Instant, ) -> Result<(u64, u64)> { let space = spaces.get_mut(space_id).ok_or(Error::InternalError)?; @@ -279,7 +283,12 @@ impl Recovery { } // Detect lost packets - let (lost_packets, lost_bytes) = self.detect_lost_packets(space, qlog, now); + let (lost_packets, lost_bytes) = self.detect_lost_packets( + space, + #[cfg(feature = "qlog")] + qlog, + now, + ); // Remove acked or lost packets from sent queue in batch. self.drain_sent_packets(space, now, self.rtt.smoothed_rtt()); @@ -415,7 +424,7 @@ impl Recovery { fn detect_lost_packets( &mut self, space: &mut PacketNumSpace, - mut qlog: Option<&mut qlog::QlogWriter>, + #[cfg(feature = "qlog")] mut qlog: Option<&mut qlog::QlogWriter>, now: Instant, ) -> (u64, u64) { space.loss_time = None; @@ -467,6 +476,7 @@ impl Recovery { if !unacked.pmtu_probe { latest_lost_packet = Some(unacked.clone()); } + #[cfg(feature = "qlog")] if let Some(qlog) = qlog.as_mut() { self.qlog_recovery_packet_lost(qlog, unacked); } @@ -584,7 +594,7 @@ impl Recovery { space_id: SpaceId, spaces: &mut PacketNumSpaceMap, handshake_status: HandshakeStatus, - qlog: Option<&mut qlog::QlogWriter>, + #[cfg(feature = "qlog")] qlog: Option<&mut qlog::QlogWriter>, now: Instant, ) -> (u64, u64) { let (earliest_loss_time, sid) = self.get_loss_time_and_space(space_id, spaces); @@ -596,7 +606,12 @@ impl Recovery { // Loss timer mode if earliest_loss_time.is_some() { // Time threshold loss detection. - let (lost_packets, lost_bytes) = self.detect_lost_packets(space, qlog, now); + let (lost_packets, lost_bytes) = self.detect_lost_packets( + space, + #[cfg(feature = "qlog")] + qlog, + now, + ); self.drain_sent_packets(space, now, self.rtt.smoothed_rtt()); self.set_loss_detection_timer(space_id, spaces, handshake_status, now); return (lost_packets, lost_bytes); @@ -937,6 +952,7 @@ impl Recovery { } /// Write a qlog RecoveryMetricsUpdated event if any recovery metric is updated. + #[cfg(feature = "qlog")] pub(crate) fn qlog_recovery_metrics_updated(&mut self, qlog: &mut qlog::QlogWriter) { let mut updated = false; @@ -1009,6 +1025,7 @@ impl Recovery { } /// Write a qlog RecoveryPacketLost event. + #[cfg(feature = "qlog")] pub(crate) fn qlog_recovery_packet_lost( &mut self, qlog: &mut qlog::QlogWriter, @@ -1029,6 +1046,7 @@ impl Recovery { } /// Metrics used for emitting qlog RecoveryMetricsUpdated event. +#[cfg(feature = "qlog")] #[derive(Default)] struct RecoveryMetrics { /// The minimum RTT observed on the path, ignoring ack delay @@ -1141,6 +1159,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; @@ -1150,8 +1169,14 @@ mod tests { // Advance ticks until loss timeout now = recovery.loss_detection_timer().unwrap(); - let (lost_pkts, lost_bytes) = - recovery.on_loss_detection_timeout(SpaceId::Handshake, &mut spaces, status, None, now); + let (lost_pkts, lost_bytes) = recovery.on_loss_detection_timeout( + SpaceId::Handshake, + &mut spaces, + status, + #[cfg(feature = "qlog")] + None, + now, + ); assert_eq!(lost_pkts, 1); assert_eq!(lost_bytes, 1001); assert_eq!(spaces.get(space_id).unwrap().ack_eliciting_in_flight, 0); @@ -1213,6 +1238,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; @@ -1228,6 +1254,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; @@ -1281,6 +1308,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; @@ -1291,8 +1319,14 @@ mod tests { // Advance ticks until pto timeout now = recovery.loss_detection_timer().unwrap(); - let (lost_pkts, lost_bytes) = - recovery.on_loss_detection_timeout(SpaceId::Handshake, &mut spaces, status, None, now); + let (lost_pkts, lost_bytes) = recovery.on_loss_detection_timeout( + SpaceId::Handshake, + &mut spaces, + status, + #[cfg(feature = "qlog")] + None, + now, + ); assert_eq!(recovery.pto_count, 1); assert_eq!(lost_pkts, 0); assert_eq!(lost_bytes, 0); @@ -1351,6 +1385,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; @@ -1454,7 +1489,16 @@ mod tests { vec![500..950], )); // Fake receiving duplicated ACK. - recovery.on_ack_received(&ack, 0, SpaceId::Data, &mut spaces, status, None, now)?; + recovery.on_ack_received( + &ack, + 0, + SpaceId::Data, + &mut spaces, + status, + #[cfg(feature = "qlog")] + None, + now, + )?; assert!(check_acked_packets( &spaces.get(SpaceId::Data).unwrap().sent, vec![500..950], @@ -1536,6 +1580,7 @@ mod tests { SpaceId::Handshake, &mut spaces, status, + #[cfg(feature = "qlog")] None, now, )?; diff --git a/src/ffi.rs b/src/ffi.rs index d971f249..e178ad59 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -114,6 +114,7 @@ use crate::h3::Http3Config; use crate::h3::Http3Event; use crate::h3::Http3Headers; use crate::h3::NameValue; +#[cfg(feature = "qlog")] use crate::qlog::events; use crate::tls::SslCtx; use crate::tls::TlsConfig; @@ -1390,6 +1391,7 @@ pub extern "C" fn quic_conn_set_keylog_fd(conn: &mut Connection, fd: c_int) { /// `data` is a qlog message and `argp` is user-defined data that will be passed to the callback. /// `title` and `desc` respectively refer to the "title" and "description" sections of qlog. #[no_mangle] +#[cfg(feature = "qlog")] pub extern "C" fn quic_conn_set_qlog( conn: &mut Connection, cb: extern "C" fn(data: *const u8, data_len: size_t, argp: *mut c_void), @@ -1412,6 +1414,7 @@ pub extern "C" fn quic_conn_set_qlog( /// Set qlog file. /// Note: The API is not applicable for Windows. #[no_mangle] +#[cfg(feature = "qlog")] #[cfg(unix)] pub extern "C" fn quic_conn_set_qlog_fd( conn: &mut Connection, diff --git a/src/frame.rs b/src/frame.rs index 52814dc7..a5ec9657 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -19,11 +19,14 @@ use crate::codec::Decoder; use crate::codec::Encoder; use crate::error::Error; use crate::packet::PacketType; +#[cfg(feature = "qlog")] use crate::qlog; +#[cfg(feature = "qlog")] use crate::qlog::events::ErrorSpace; +#[cfg(feature = "qlog")] use crate::qlog::events::QuicFrame; +#[cfg(feature = "qlog")] use crate::qlog::events::StreamType; -use crate::qlog::events::TokenType; use crate::ranges::RangeSet; use crate::token::ResetToken; use crate::ConnectionId; @@ -767,6 +770,7 @@ impl Frame { } } + #[cfg(feature = "qlog")] pub fn to_qlog(&self) -> QuicFrame { match self { Frame::Paddings { .. } => QuicFrame::Padding, diff --git a/src/lib.rs b/src/lib.rs index 8f483b9c..a533ed70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ //! and dependencies: //! //! * `ffi`: Build and expose the FFI API. +//! * `qlog`: Enable support for the qlog. #![allow(unused_imports)] #![allow(dead_code)] @@ -1238,6 +1239,7 @@ mod tls; #[path = "h3/h3.rs"] pub mod h3; +#[cfg(feature = "qlog")] #[path = "qlog/qlog.rs"] mod qlog; diff --git a/src/packet.rs b/src/packet.rs index 22297fc4..bb09395d 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -26,6 +26,7 @@ use self::PacketType::*; use crate::codec::Decoder; use crate::codec::Encoder; use crate::connection::space::SpaceId; +#[cfg(feature = "qlog")] use crate::qlog; use crate::ranges; use crate::tls; @@ -132,6 +133,7 @@ impl PacketType { } /// Get the packet type for Qlog. + #[cfg(feature = "qlog")] pub fn to_qlog(self) -> qlog::events::PacketType { match self { VersionNegotiation => qlog::events::PacketType::VersionNegotiation, diff --git a/src/trans_param.rs b/src/trans_param.rs index 3ad26988..b2db8262 100644 --- a/src/trans_param.rs +++ b/src/trans_param.rs @@ -22,7 +22,9 @@ use crate::codec; use crate::codec::Decoder; use crate::codec::Encoder; use crate::error::Error; +#[cfg(feature = "qlog")] use crate::qlog; +#[cfg(feature = "qlog")] use crate::qlog::events::EventData; use crate::tls; use crate::token::ResetToken; @@ -406,6 +408,7 @@ impl TransportParams { } /// Create TransportParametersSet event data for Qlog. + #[cfg(feature = "qlog")] pub fn to_qlog(&self, owner: qlog::events::Owner, cipher: Option) -> EventData { let original_destination_connection_id = Some(format!( "{:?}",