summaryrefslogtreecommitdiffstats
path: root/third_party/rust/qlog/src/lib.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/qlog/src/lib.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/qlog/src/lib.rs')
-rw-r--r--third_party/rust/qlog/src/lib.rs971
1 files changed, 971 insertions, 0 deletions
diff --git a/third_party/rust/qlog/src/lib.rs b/third_party/rust/qlog/src/lib.rs
new file mode 100644
index 0000000000..68ff278fcd
--- /dev/null
+++ b/third_party/rust/qlog/src/lib.rs
@@ -0,0 +1,971 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! The qlog crate is an implementation of the qlog [main logging schema],
+//! [QUIC event definitions], and [HTTP/3 and QPACK event definitions].
+//! The crate provides a qlog data model that can be used for traces with
+//! events. It supports serialization and deserialization but defers logging IO
+//! choices to applications.
+//!
+//! Serialization operates in either a [buffered mode] or a [streaming mode].
+//!
+//! The crate uses Serde for conversion between Rust and JSON.
+//!
+//! [main logging schema]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema
+//! [QUIC event definitions]:
+//! https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html
+//! [HTTP/3 and QPACK event definitions]:
+//! https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-h3-events.html
+//! [buffered mode]: #buffered-traces-with-standard-json
+//! [streaming mode]: #streaming-traces-with-json-seq
+//!
+//! Overview
+//! ---------------
+//! qlog is a hierarchical logging format, with a rough structure of:
+//!
+//! * Log
+//! * Trace(s)
+//! * Event(s)
+//!
+//! In practice, a single QUIC connection maps to a single Trace file with one
+//! or more Events. Applications can decide whether to combine Traces from
+//! different connections into the same Log.
+//!
+//! ## Buffered Traces with standard JSON
+//!
+//! A [`Trace`] is a single JSON object. It contains metadata such as the
+//! [`VantagePoint`] of capture and the [`Configuration`], and protocol event
+//! data in the [`Event`] array.
+//!
+//! JSON Traces allow applications to appends events to them before eventually
+//! being serialized as a complete JSON object.
+//!
+//! ### Creating a Trace
+//!
+//! ```
+//! let mut trace = qlog::Trace::new(
+//! qlog::VantagePoint {
+//! name: Some("Example client".to_string()),
+//! ty: qlog::VantagePointType::Client,
+//! flow: None,
+//! },
+//! Some("Example qlog trace".to_string()),
+//! Some("Example qlog trace description".to_string()),
+//! Some(qlog::Configuration {
+//! time_offset: Some(0.0),
+//! original_uris: None,
+//! }),
+//! None,
+//! );
+//! ```
+//!
+//! ### Adding events to a Trace
+//!
+//! Qlog [`Event`] objects are added to [`qlog::Trace.events`].
+//!
+//! The following example demonstrates how to log a qlog QUIC `packet_sent`
+//! event containing a single Crypto frame. It constructs the necessary elements
+//! of the [`Event`], then appends it to the trace with [`push_event()`].
+//!
+//! ```
+//! # let mut trace = qlog::Trace::new (
+//! # qlog::VantagePoint {
+//! # name: Some("Example client".to_string()),
+//! # ty: qlog::VantagePointType::Client,
+//! # flow: None,
+//! # },
+//! # Some("Example qlog trace".to_string()),
+//! # Some("Example qlog trace description".to_string()),
+//! # Some(qlog::Configuration {
+//! # time_offset: Some(0.0),
+//! # original_uris: None,
+//! # }),
+//! # None
+//! # );
+//!
+//! let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8];
+//! let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c];
+//!
+//! let pkt_hdr = qlog::events::quic::PacketHeader::new(
+//! qlog::events::quic::PacketType::Initial,
+//! Some(0), // packet_number
+//! None, // flags
+//! None, // token
+//! None, // length
+//! Some(0x00000001), // version
+//! Some(&scid),
+//! Some(&dcid),
+//! );
+//!
+//! let frames = vec![qlog::events::quic::QuicFrame::Crypto {
+//! offset: 0,
+//! length: 0,
+//! }];
+//!
+//! let raw = qlog::events::RawInfo {
+//! length: Some(1251),
+//! payload_length: Some(1224),
+//! data: None,
+//! };
+//!
+//! let event_data =
+//! qlog::events::EventData::PacketSent(qlog::events::quic::PacketSent {
+//! header: pkt_hdr,
+//! frames: Some(frames.into()),
+//! is_coalesced: None,
+//! retry_token: None,
+//! stateless_reset_token: None,
+//! supported_versions: None,
+//! raw: Some(raw),
+//! datagram_id: None,
+//! send_at_time: None,
+//! trigger: None,
+//! });
+//!
+//! trace.push_event(qlog::events::Event::with_time(0.0, event_data));
+//! ```
+//!
+//! ### Serializing
+//!
+//! The qlog crate has only been tested with `serde_json`, however
+//! other serializer targets might work.
+//!
+//! For example, serializing the trace created above:
+//!
+//! ```
+//! # let mut trace = qlog::Trace::new (
+//! # qlog::VantagePoint {
+//! # name: Some("Example client".to_string()),
+//! # ty: qlog::VantagePointType::Client,
+//! # flow: None,
+//! # },
+//! # Some("Example qlog trace".to_string()),
+//! # Some("Example qlog trace description".to_string()),
+//! # Some(qlog::Configuration {
+//! # time_offset: Some(0.0),
+//! # original_uris: None,
+//! # }),
+//! # None
+//! # );
+//! serde_json::to_string_pretty(&trace).unwrap();
+//! ```
+//!
+//! which would generate the following:
+//!
+//! ```ignore
+//! {
+//! "vantage_point": {
+//! "name": "Example client",
+//! "type": "client"
+//! },
+//! "title": "Example qlog trace",
+//! "description": "Example qlog trace description",
+//! "configuration": {
+//! "time_offset": 0.0
+//! },
+//! "events": [
+//! {
+//! "time": 0.0,
+//! "name": "transport:packet_sent",
+//! "data": {
+//! "header": {
+//! "packet_type": "initial",
+//! "packet_number": 0,
+//! "version": "1",
+//! "scil": 8,
+//! "dcil": 8,
+//! "scid": "7e37e4dcc6682da8",
+//! "dcid": "36ce104eee50101c"
+//! },
+//! "raw": {
+//! "length": 1251,
+//! "payload_length": 1224
+//! },
+//! "frames": [
+//! {
+//! "frame_type": "crypto",
+//! "offset": 0,
+//! "length": 0
+//! }
+//! ]
+//! }
+//! }
+//! ]
+//! }
+//! ```
+//!
+//! ## Streaming Traces with JSON-SEQ
+//!
+//! To help support streaming serialization of qlogs,
+//! draft-ietf-quic-qlog-main-schema-01 introduced support for RFC 7464 JSON
+//! Text Sequences (JSON-SEQ). The qlog crate supports this format and provides
+//! utilities that aid streaming.
+//!
+//! A [`TraceSeq`] contains metadata such as the [`VantagePoint`] of capture and
+//! the [`Configuration`]. However, protocol event data is handled as separate
+//! lines containing a record separator character, a serialized [`Event`], and a
+//! newline.
+//!
+//! ### Creating a TraceSeq
+//!
+//! ```
+//! let mut trace = qlog::TraceSeq::new(
+//! qlog::VantagePoint {
+//! name: Some("Example client".to_string()),
+//! ty: qlog::VantagePointType::Client,
+//! flow: None,
+//! },
+//! Some("Example qlog trace".to_string()),
+//! Some("Example qlog trace description".to_string()),
+//! Some(qlog::Configuration {
+//! time_offset: Some(0.0),
+//! original_uris: None,
+//! }),
+//! None,
+//! );
+//! ```
+//!
+//! Create an object with the [`Write`] trait:
+//!
+//! ```
+//! let mut file = std::fs::File::create("foo.sqlog").unwrap();
+//! ```
+//!
+//! Create a [`QlogStreamer`] and start serialization to foo.sqlog
+//! using [`start_log()`]:
+//!
+//! ```
+//! # let mut trace = qlog::TraceSeq::new(
+//! # qlog::VantagePoint {
+//! # name: Some("Example client".to_string()),
+//! # ty: qlog::VantagePointType::Client,
+//! # flow: None,
+//! # },
+//! # Some("Example qlog trace".to_string()),
+//! # Some("Example qlog trace description".to_string()),
+//! # Some(qlog::Configuration {
+//! # time_offset: Some(0.0),
+//! # original_uris: None,
+//! # }),
+//! # None,
+//! # );
+//! # let mut file = std::fs::File::create("foo.sqlog").unwrap();
+//! let mut streamer = qlog::streamer::QlogStreamer::new(
+//! qlog::QLOG_VERSION.to_string(),
+//! Some("Example qlog".to_string()),
+//! Some("Example qlog description".to_string()),
+//! None,
+//! std::time::Instant::now(),
+//! trace,
+//! qlog::events::EventImportance::Base,
+//! Box::new(file),
+//! );
+//!
+//! streamer.start_log().ok();
+//! ```
+//!
+//! ### Adding events
+//!
+//! Once logging has started you can stream events. Events
+//! are written in one step using one of [`add_event()`],
+//! [`add_event_with_instant()`], [`add_event_now()`],
+//! [`add_event_data_with_instant()`], or [`add_event_data_now()`] :
+//!
+//! ```
+//! # let mut trace = qlog::TraceSeq::new(
+//! # qlog::VantagePoint {
+//! # name: Some("Example client".to_string()),
+//! # ty: qlog::VantagePointType::Client,
+//! # flow: None,
+//! # },
+//! # Some("Example qlog trace".to_string()),
+//! # Some("Example qlog trace description".to_string()),
+//! # Some(qlog::Configuration {
+//! # time_offset: Some(0.0),
+//! # original_uris: None,
+//! # }),
+//! # None,
+//! # );
+//! # let mut file = std::fs::File::create("foo.qlog").unwrap();
+//! # let mut streamer = qlog::streamer::QlogStreamer::new(
+//! # qlog::QLOG_VERSION.to_string(),
+//! # Some("Example qlog".to_string()),
+//! # Some("Example qlog description".to_string()),
+//! # None,
+//! # std::time::Instant::now(),
+//! # trace,
+//! # qlog::events::EventImportance::Base,
+//! # Box::new(file),
+//! # );
+//!
+//! let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8];
+//! let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c];
+//!
+//! let pkt_hdr = qlog::events::quic::PacketHeader::with_type(
+//! qlog::events::quic::PacketType::OneRtt,
+//! Some(0),
+//! Some(0x00000001),
+//! Some(&scid),
+//! Some(&dcid),
+//! );
+//!
+//! let ping = qlog::events::quic::QuicFrame::Ping;
+//! let padding = qlog::events::quic::QuicFrame::Padding;
+//!
+//! let event_data =
+//! qlog::events::EventData::PacketSent(qlog::events::quic::PacketSent {
+//! header: pkt_hdr,
+//! frames: Some(vec![ping, padding].into()),
+//! is_coalesced: None,
+//! retry_token: None,
+//! stateless_reset_token: None,
+//! supported_versions: None,
+//! raw: None,
+//! datagram_id: None,
+//! send_at_time: None,
+//! trigger: None,
+//! });
+//!
+//! let event = qlog::events::Event::with_time(0.0, event_data);
+//!
+//! streamer.add_event(event).ok();
+//! ```
+//!
+//! Once all events have been written, the log
+//! can be finalized with [`finish_log()`]:
+//!
+//! ```
+//! # let mut trace = qlog::TraceSeq::new(
+//! # qlog::VantagePoint {
+//! # name: Some("Example client".to_string()),
+//! # ty: qlog::VantagePointType::Client,
+//! # flow: None,
+//! # },
+//! # Some("Example qlog trace".to_string()),
+//! # Some("Example qlog trace description".to_string()),
+//! # Some(qlog::Configuration {
+//! # time_offset: Some(0.0),
+//! # original_uris: None,
+//! # }),
+//! # None,
+//! # );
+//! # let mut file = std::fs::File::create("foo.qlog").unwrap();
+//! # let mut streamer = qlog::streamer::QlogStreamer::new(
+//! # qlog::QLOG_VERSION.to_string(),
+//! # Some("Example qlog".to_string()),
+//! # Some("Example qlog description".to_string()),
+//! # None,
+//! # std::time::Instant::now(),
+//! # trace,
+//! # qlog::events::EventImportance::Base,
+//! # Box::new(file),
+//! # );
+//! streamer.finish_log().ok();
+//! ```
+//!
+//! ### Serializing
+//!
+//! Serialization to JSON occurs as methods on the [`QlogStreamer`]
+//! are called. No additional steps are required.
+//!
+//! [`Trace`]: struct.Trace.html
+//! [`TraceSeq`]: struct.TraceSeq.html
+//! [`VantagePoint`]: struct.VantagePoint.html
+//! [`Configuration`]: struct.Configuration.html
+//! [`qlog::Trace.events`]: struct.Trace.html#structfield.events
+//! [`push_event()`]: struct.Trace.html#method.push_event
+//! [`QlogStreamer`]: struct.QlogStreamer.html
+//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
+//! [`start_log()`]: streamer/struct.QlogStreamer.html#method.start_log
+//! [`add_event()`]: streamer/struct.QlogStreamer.html#method.add_event
+//! [`add_event_with_instant()`]: streamer/struct.QlogStreamer.html#method.add_event_with_instant
+//! [`add_event_now()`]: streamer/struct.QlogStreamer.html#method.add_event_now
+//! [`add_event_data_with_instant()`]: streamer/struct.QlogStreamer.html#method.add_event_data_with_instant
+//! [`add_event_data_now()`]: streamer/struct.QlogStreamer.html#method.add_event_data_now
+//! [`finish_log()`]: streamer/struct.QlogStreamer.html#method.finish_log
+
+use crate::events::quic::PacketHeader;
+use crate::events::Event;
+
+use serde::Deserialize;
+use serde::Serialize;
+
+/// A quiche qlog error.
+#[derive(Debug)]
+pub enum Error {
+ /// There is no more work to do.
+ Done,
+
+ /// The operation cannot be completed because it was attempted
+ /// in an invalid state.
+ InvalidState,
+
+ // Invalid Qlog format
+ InvalidFormat,
+
+ /// I/O error.
+ IoError(std::io::Error),
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{self:?}")
+ }
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+
+impl std::convert::From<std::io::Error> for Error {
+ fn from(err: std::io::Error) -> Self {
+ Error::IoError(err)
+ }
+}
+
+pub const QLOG_VERSION: &str = "0.3";
+
+pub type Bytes = String;
+pub type StatelessResetToken = Bytes;
+
+/// A specialized [`Result`] type for quiche qlog operations.
+///
+/// This type is used throughout the public API for any operation that
+/// can produce an error.
+///
+/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Qlog {
+ pub qlog_version: String,
+ pub qlog_format: String,
+ pub title: Option<String>,
+ pub description: Option<String>,
+ pub summary: Option<String>,
+
+ pub traces: Vec<Trace>,
+}
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct QlogSeq {
+ pub qlog_version: String,
+ pub qlog_format: String,
+ pub title: Option<String>,
+ pub description: Option<String>,
+ pub summary: Option<String>,
+
+ pub trace: TraceSeq,
+}
+
+#[derive(Clone, Copy)]
+pub enum ImportanceLogLevel {
+ Core = 0,
+ Base = 1,
+ Extra = 2,
+}
+
+// We now commence data definitions heavily styled on the QLOG
+// schema definition. Data is serialized using serde.
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
+pub struct Trace {
+ pub vantage_point: VantagePoint,
+ pub title: Option<String>,
+ pub description: Option<String>,
+
+ pub configuration: Option<Configuration>,
+
+ pub common_fields: Option<CommonFields>,
+
+ pub events: Vec<Event>,
+}
+
+/// Helper functions for using a qlog [Trace].
+impl Trace {
+ /// Creates a new qlog [Trace]
+ pub fn new(
+ vantage_point: VantagePoint, title: Option<String>,
+ description: Option<String>, configuration: Option<Configuration>,
+ common_fields: Option<CommonFields>,
+ ) -> Self {
+ Trace {
+ vantage_point,
+ title,
+ description,
+ configuration,
+ common_fields,
+ events: Vec::new(),
+ }
+ }
+
+ /// Append an [Event] to a [Trace]
+ pub fn push_event(&mut self, event: Event) {
+ self.events.push(event);
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
+pub struct TraceSeq {
+ pub vantage_point: VantagePoint,
+ pub title: Option<String>,
+ pub description: Option<String>,
+
+ pub configuration: Option<Configuration>,
+
+ pub common_fields: Option<CommonFields>,
+}
+
+/// Helper functions for using a qlog [TraceSeq].
+impl TraceSeq {
+ /// Creates a new qlog [TraceSeq]
+ pub fn new(
+ vantage_point: VantagePoint, title: Option<String>,
+ description: Option<String>, configuration: Option<Configuration>,
+ common_fields: Option<CommonFields>,
+ ) -> Self {
+ TraceSeq {
+ vantage_point,
+ title,
+ description,
+ configuration,
+ common_fields,
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
+pub struct VantagePoint {
+ pub name: Option<String>,
+
+ #[serde(rename = "type")]
+ pub ty: VantagePointType,
+
+ pub flow: Option<VantagePointType>,
+}
+
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
+#[serde(rename_all = "snake_case")]
+pub enum VantagePointType {
+ Client,
+ Server,
+ Network,
+ Unknown,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
+pub struct Configuration {
+ pub time_offset: Option<f64>,
+
+ pub original_uris: Option<Vec<String>>,
+ // TODO: additionalUserSpecifiedProperty
+}
+
+impl Default for Configuration {
+ fn default() -> Self {
+ Configuration {
+ time_offset: Some(0.0),
+ original_uris: None,
+ }
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Debug)]
+pub struct CommonFields {
+ pub group_id: Option<String>,
+ pub protocol_type: Option<Vec<String>>,
+
+ pub reference_time: Option<f64>,
+ pub time_format: Option<String>,
+ // TODO: additionalUserSpecifiedProperty
+}
+
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
+#[serde(rename_all = "snake_case")]
+pub enum TokenType {
+ Retry,
+ Resumption,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
+pub struct Token {
+ #[serde(rename(serialize = "type"))]
+ pub ty: Option<TokenType>,
+
+ pub details: Option<String>,
+
+ pub raw: Option<events::RawInfo>,
+}
+
+pub struct HexSlice<'a>(&'a [u8]);
+
+impl<'a> HexSlice<'a> {
+ pub fn new<T>(data: &'a T) -> HexSlice<'a>
+ where
+ T: ?Sized + AsRef<[u8]> + 'a,
+ {
+ HexSlice(data.as_ref())
+ }
+
+ pub fn maybe_string<T>(data: Option<&'a T>) -> Option<String>
+ where
+ T: ?Sized + AsRef<[u8]> + 'a,
+ {
+ data.map(|d| format!("{}", HexSlice::new(d)))
+ }
+}
+
+impl<'a> std::fmt::Display for HexSlice<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ for byte in self.0 {
+ write!(f, "{byte:02x}")?;
+ }
+ Ok(())
+ }
+}
+
+#[doc(hidden)]
+pub mod testing {
+ use super::*;
+ use crate::events::quic::PacketType;
+
+ pub fn make_pkt_hdr(packet_type: PacketType) -> PacketHeader {
+ let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8];
+ let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c];
+
+ // Some(1251),
+ // Some(1224),
+
+ PacketHeader::new(
+ packet_type,
+ Some(0),
+ None,
+ None,
+ None,
+ Some(0x0000_0001),
+ Some(&scid),
+ Some(&dcid),
+ )
+ }
+
+ pub fn make_trace() -> Trace {
+ Trace::new(
+ VantagePoint {
+ name: None,
+ ty: VantagePointType::Server,
+ flow: None,
+ },
+ Some("Quiche qlog trace".to_string()),
+ Some("Quiche qlog trace description".to_string()),
+ Some(Configuration {
+ time_offset: Some(0.0),
+ original_uris: None,
+ }),
+ None,
+ )
+ }
+
+ pub fn make_trace_seq() -> TraceSeq {
+ TraceSeq::new(
+ VantagePoint {
+ name: None,
+ ty: VantagePointType::Server,
+ flow: None,
+ },
+ Some("Quiche qlog trace".to_string()),
+ Some("Quiche qlog trace description".to_string()),
+ Some(Configuration {
+ time_offset: Some(0.0),
+ original_uris: None,
+ }),
+ None,
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::events::quic::PacketSent;
+ use crate::events::quic::PacketType;
+ use crate::events::quic::QuicFrame;
+ use crate::events::EventData;
+ use crate::events::RawInfo;
+ use testing::*;
+
+ #[test]
+ fn packet_sent_event_no_frames() {
+ let log_string = r#"{
+ "time": 0.0,
+ "name": "transport:packet_sent",
+ "data": {
+ "header": {
+ "packet_type": "initial",
+ "packet_number": 0,
+ "version": "1",
+ "scil": 8,
+ "dcil": 8,
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "raw": {
+ "length": 1251,
+ "payload_length": 1224
+ }
+ }
+}"#;
+
+ let pkt_hdr = make_pkt_hdr(PacketType::Initial);
+ let ev_data = EventData::PacketSent(PacketSent {
+ header: pkt_hdr,
+ frames: None,
+ is_coalesced: None,
+ retry_token: None,
+ stateless_reset_token: None,
+ supported_versions: None,
+ raw: Some(RawInfo {
+ length: Some(1251),
+ payload_length: Some(1224),
+ data: None,
+ }),
+ datagram_id: None,
+ send_at_time: None,
+ trigger: None,
+ });
+
+ let ev = Event::with_time(0.0, ev_data);
+
+ assert_eq!(serde_json::to_string_pretty(&ev).unwrap(), log_string);
+ }
+
+ #[test]
+ fn packet_sent_event_some_frames() {
+ let log_string = r#"{
+ "time": 0.0,
+ "name": "transport:packet_sent",
+ "data": {
+ "header": {
+ "packet_type": "initial",
+ "packet_number": 0,
+ "version": "1",
+ "scil": 8,
+ "dcil": 8,
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "raw": {
+ "length": 1251,
+ "payload_length": 1224
+ },
+ "frames": [
+ {
+ "frame_type": "padding"
+ },
+ {
+ "frame_type": "ping"
+ },
+ {
+ "frame_type": "stream",
+ "stream_id": 0,
+ "offset": 0,
+ "length": 100,
+ "fin": true
+ }
+ ]
+ }
+}"#;
+
+ let pkt_hdr = make_pkt_hdr(PacketType::Initial);
+
+ let frames =
+ vec![QuicFrame::Padding, QuicFrame::Ping, QuicFrame::Stream {
+ stream_id: 0,
+ offset: 0,
+ length: 100,
+ fin: Some(true),
+ raw: None,
+ }];
+
+ let ev_data = EventData::PacketSent(PacketSent {
+ header: pkt_hdr,
+ frames: Some(frames.into()),
+ is_coalesced: None,
+ retry_token: None,
+ stateless_reset_token: None,
+ supported_versions: None,
+ raw: Some(RawInfo {
+ length: Some(1251),
+ payload_length: Some(1224),
+ data: None,
+ }),
+ datagram_id: None,
+ send_at_time: None,
+ trigger: None,
+ });
+
+ let ev = Event::with_time(0.0, ev_data);
+ assert_eq!(serde_json::to_string_pretty(&ev).unwrap(), log_string);
+ }
+
+ #[test]
+ fn trace_no_events() {
+ let log_string = r#"{
+ "vantage_point": {
+ "type": "server"
+ },
+ "title": "Quiche qlog trace",
+ "description": "Quiche qlog trace description",
+ "configuration": {
+ "time_offset": 0.0
+ },
+ "events": []
+}"#;
+
+ let trace = make_trace();
+
+ let serialized = serde_json::to_string_pretty(&trace).unwrap();
+ assert_eq!(serialized, log_string);
+
+ let deserialized: Trace = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(deserialized, trace);
+ }
+
+ #[test]
+ fn trace_seq_no_events() {
+ let log_string = r#"{
+ "vantage_point": {
+ "type": "server"
+ },
+ "title": "Quiche qlog trace",
+ "description": "Quiche qlog trace description",
+ "configuration": {
+ "time_offset": 0.0
+ }
+}"#;
+
+ let trace = make_trace_seq();
+
+ let serialized = serde_json::to_string_pretty(&trace).unwrap();
+ assert_eq!(serialized, log_string);
+
+ let deserialized: TraceSeq = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(deserialized, trace);
+ }
+
+ #[test]
+ fn trace_single_transport_event() {
+ let log_string = r#"{
+ "vantage_point": {
+ "type": "server"
+ },
+ "title": "Quiche qlog trace",
+ "description": "Quiche qlog trace description",
+ "configuration": {
+ "time_offset": 0.0
+ },
+ "events": [
+ {
+ "time": 0.0,
+ "name": "transport:packet_sent",
+ "data": {
+ "header": {
+ "packet_type": "initial",
+ "packet_number": 0,
+ "version": "1",
+ "scil": 8,
+ "dcil": 8,
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "raw": {
+ "length": 1251,
+ "payload_length": 1224
+ },
+ "frames": [
+ {
+ "frame_type": "stream",
+ "stream_id": 0,
+ "offset": 0,
+ "length": 100,
+ "fin": true
+ }
+ ]
+ }
+ }
+ ]
+}"#;
+
+ let mut trace = make_trace();
+
+ let pkt_hdr = make_pkt_hdr(PacketType::Initial);
+
+ let frames = vec![QuicFrame::Stream {
+ stream_id: 0,
+ offset: 0,
+ length: 100,
+ fin: Some(true),
+ raw: None,
+ }];
+ let event_data = EventData::PacketSent(PacketSent {
+ header: pkt_hdr,
+ frames: Some(frames.into()),
+ is_coalesced: None,
+ retry_token: None,
+ stateless_reset_token: None,
+ supported_versions: None,
+ raw: Some(RawInfo {
+ length: Some(1251),
+ payload_length: Some(1224),
+ data: None,
+ }),
+ datagram_id: None,
+ send_at_time: None,
+ trigger: None,
+ });
+
+ let ev = Event::with_time(0.0, event_data);
+
+ trace.push_event(ev);
+
+ let serialized = serde_json::to_string_pretty(&trace).unwrap();
+ assert_eq!(serialized, log_string);
+
+ let deserialized: Trace = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(deserialized, trace);
+ }
+}
+
+pub mod events;
+pub mod reader;
+pub mod streamer;