use crate::events::quic::PacketHeader;
use crate::events::Event;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug)]
pub enum Error {
Done,
InvalidState,
InvalidFormat,
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;
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,
}
#[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>,
}
impl 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(),
}
}
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>,
}
impl 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, Debug)]
pub struct VantagePoint {
pub name: Option<String>,
#[serde(rename = "type")]
pub ty: VantagePointType,
pub flow: Option<VantagePointType>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, 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>>,
}
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<f32>,
pub time_format: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum TokenType {
Retry,
Resumption,
}
#[serde_with::skip_serializing_none]
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct Token {
#[serde(rename(serialize = "type"))]
pub ty: Option<TokenType>,
pub length: Option<u32>,
pub data: Option<Bytes>,
pub details: Option<String>,
}
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, "{:02x}", byte)?;
}
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];
PacketHeader::new(
packet_type,
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.clone(),
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,
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 mut frames = Vec::new();
frames.push(QuicFrame::Padding);
frames.push(QuicFrame::Ping);
frames.push(QuicFrame::Stream {
stream_id: 0,
offset: 0,
length: 100,
fin: Some(true),
raw: None,
});
let ev_data = EventData::PacketSent(PacketSent {
header: pkt_hdr.clone(),
frames: Some(frames),
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,
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),
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,
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 streamer;