1 // Copyright (C) 2019, Cloudflare, Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright notice, 9 // this list of conditions and the following disclaimer. 10 // 11 // * Redistributions in binary form must reproduce the above copyright 12 // notice, this list of conditions and the following disclaimer in the 13 // documentation and/or other materials provided with the distribution. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 17 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 19 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 //! The qlog crate is an implementation of the [qlog main schema] and [qlog QUIC 28 //! and HTTP/3 events] that attempts to closely follow the format of the qlog 29 //! [TypeScript schema]. This is just a data model and no support is provided 30 //! for logging IO, applications can decide themselves the most appropriate 31 //! method. 32 //! 33 //! The crate uses Serde for conversion between Rust and JSON. 34 //! 35 //! [qlog main schema]: https://tools.ietf.org/html/draft-marx-qlog-main-schema 36 //! [qlog QUIC and HTTP/3 events]: 37 //! https://quiclog.github.io/internet-drafts/draft-marx-qlog-event-definitions-quic-h3 38 //! [TypeScript schema]: 39 //! https://github.com/quiclog/qlog/blob/master/TypeScript/draft-01/QLog.ts 40 //! 41 //! Overview 42 //! --------------- 43 //! qlog is a hierarchical logging format, with a rough structure of: 44 //! 45 //! * Log 46 //! * Trace(s) 47 //! * Event(s) 48 //! 49 //! In practice, a single QUIC connection maps to a single Trace file with one 50 //! or more Events. Applications can decide whether to combine Traces from 51 //! different connections into the same Log. 52 //! 53 //! ## Traces 54 //! 55 //! A [`Trace`] contains metadata such as the [`VantagePoint`] of capture and 56 //! the [`Configuration`] of the `Trace`. 57 //! 58 //! A very important part of the `Trace` is the definition of `event_fields`. A 59 //! qlog Event is a vector of [`EventField`]; this provides great flexibility to 60 //! log events with any number of `EventFields` in any order. The `event_fields` 61 //! property describes the format of event logging and it is important that 62 //! events comply with that format. Failing to do so it going to cause problems 63 //! for qlog analysis tools. For information is available at 64 //! https://tools.ietf.org/html/draft-marx-qlog-main-schema-01#section-3.3.4 65 //! 66 //! In order to make using qlog a bit easier, this crate expects a qlog Event to 67 //! consist of the following EventFields in the following order: 68 //! [`EventField::RelativeTime`], [`EventField::Category`], 69 //! [`EventField::Event`] and [`EventField::Data`]. A set of methods are 70 //! provided to assist in creating a Trace and appending events to it in this 71 //! format. 72 //! 73 //! ## Writing out logs 74 //! As events occur during the connection, the application appends them to the 75 //! trace. The qlog crate supports two modes of writing logs: the buffered mode 76 //! stores everything in memory and requires the application to serialize and 77 //! write the output, the streaming mode progressively writes serialized JSON 78 //! output to a writer designated by the application. 79 //! 80 //! ### Creating a Trace 81 //! 82 //! A typical application needs a single qlog [`Trace`] that it appends QUIC 83 //! and/or HTTP/3 events to: 84 //! 85 //! ``` 86 //! let mut trace = qlog::Trace::new( 87 //! qlog::VantagePoint { 88 //! name: Some("Example client".to_string()), 89 //! ty: qlog::VantagePointType::Client, 90 //! flow: None, 91 //! }, 92 //! Some("Example qlog trace".to_string()), 93 //! Some("Example qlog trace description".to_string()), 94 //! Some(qlog::Configuration { 95 //! time_offset: Some("0".to_string()), 96 //! time_units: Some(qlog::TimeUnits::Ms), 97 //! original_uris: None, 98 //! }), 99 //! None, 100 //! ); 101 //! ``` 102 //! 103 //! ## Adding events 104 //! 105 //! Qlog Events are added to [`qlog::Trace.events`]. 106 //! 107 //! It is recommended to use the provided utility methods to append semantically 108 //! valid events to a trace. However, there is nothing preventing you from 109 //! creating the events manually. 110 //! 111 //! The following example demonstrates how to log a QUIC packet 112 //! containing a single Crypto frame. It uses the [`QuicFrame::crypto()`], 113 //! [`packet_sent_min()`] and [`push_event()`] methods to create and log a 114 //! PacketSent event and its EventData. 115 //! 116 //! ``` 117 //! # let mut trace = qlog::Trace::new ( 118 //! # qlog::VantagePoint { 119 //! # name: Some("Example client".to_string()), 120 //! # ty: qlog::VantagePointType::Client, 121 //! # flow: None, 122 //! # }, 123 //! # Some("Example qlog trace".to_string()), 124 //! # Some("Example qlog trace description".to_string()), 125 //! # Some(qlog::Configuration { 126 //! # time_offset: Some("0".to_string()), 127 //! # time_units: Some(qlog::TimeUnits::Ms), 128 //! # original_uris: None, 129 //! # }), 130 //! # None 131 //! # ); 132 //! 133 //! let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8]; 134 //! let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c]; 135 //! 136 //! let pkt_hdr = qlog::PacketHeader::new( 137 //! 0, 138 //! Some(1251), 139 //! Some(1224), 140 //! Some(0xff00001b), 141 //! Some(b"7e37e4dcc6682da8"), 142 //! Some(&dcid), 143 //! ); 144 //! 145 //! let frames = 146 //! vec![qlog::QuicFrame::crypto("0".to_string(), "1000".to_string())]; 147 //! 148 //! let event = qlog::event::Event::packet_sent_min( 149 //! qlog::PacketType::Initial, 150 //! pkt_hdr, 151 //! Some(frames), 152 //! ); 153 //! 154 //! trace.push_event(std::time::Duration::new(0, 0), event); 155 //! ``` 156 //! 157 //! ### Serializing 158 //! 159 //! The qlog crate has only been tested with `serde_json`, however 160 //! other serializer targets might work. 161 //! 162 //! For example, serializing the trace created above: 163 //! 164 //! ``` 165 //! # let mut trace = qlog::Trace::new ( 166 //! # qlog::VantagePoint { 167 //! # name: Some("Example client".to_string()), 168 //! # ty: qlog::VantagePointType::Client, 169 //! # flow: None, 170 //! # }, 171 //! # Some("Example qlog trace".to_string()), 172 //! # Some("Example qlog trace description".to_string()), 173 //! # Some(qlog::Configuration { 174 //! # time_offset: Some("0".to_string()), 175 //! # time_units: Some(qlog::TimeUnits::Ms), 176 //! # original_uris: None, 177 //! # }), 178 //! # None 179 //! # ); 180 //! serde_json::to_string_pretty(&trace).unwrap(); 181 //! ``` 182 //! 183 //! which would generate the following: 184 //! 185 //! ```ignore 186 //! { 187 //! "vantage_point": { 188 //! "name": "Example client", 189 //! "type": "client" 190 //! }, 191 //! "title": "Example qlog trace", 192 //! "description": "Example qlog trace description", 193 //! "configuration": { 194 //! "time_units": "ms", 195 //! "time_offset": "0" 196 //! }, 197 //! "event_fields": [ 198 //! "relative_time", 199 //! "category", 200 //! "event", 201 //! "data" 202 //! ], 203 //! "events": [ 204 //! [ 205 //! "0", 206 //! "transport", 207 //! "packet_sent", 208 //! { 209 //! "packet_type": "initial", 210 //! "header": { 211 //! "packet_number": "0", 212 //! "packet_size": 1251, 213 //! "payload_length": 1224, 214 //! "version": "ff00001b", 215 //! "scil": "8", 216 //! "dcil": "8", 217 //! "scid": "7e37e4dcc6682da8", 218 //! "dcid": "36ce104eee50101c" 219 //! }, 220 //! "frames": [ 221 //! { 222 //! "frame_type": "crypto", 223 //! "offset": "0", 224 //! "length": "100", 225 //! } 226 //! ] 227 //! } 228 //! ] 229 //! ] 230 //! } 231 //! ``` 232 //! 233 //! Streaming Mode 234 //! -------------- 235 //! 236 //! Create the trace: 237 //! 238 //! ``` 239 //! let mut trace = qlog::Trace::new( 240 //! qlog::VantagePoint { 241 //! name: Some("Example client".to_string()), 242 //! ty: qlog::VantagePointType::Client, 243 //! flow: None, 244 //! }, 245 //! Some("Example qlog trace".to_string()), 246 //! Some("Example qlog trace description".to_string()), 247 //! Some(qlog::Configuration { 248 //! time_offset: Some("0".to_string()), 249 //! time_units: Some(qlog::TimeUnits::Ms), 250 //! original_uris: None, 251 //! }), 252 //! None, 253 //! ); 254 //! ``` 255 //! Create an object with the [`Write`] trait: 256 //! 257 //! ``` 258 //! let mut file = std::fs::File::create("foo.qlog").unwrap(); 259 //! ``` 260 //! 261 //! Create a [`QlogStreamer`] and start serialization to foo.qlog 262 //! using [`start_log()`]: 263 //! 264 //! ``` 265 //! # let mut trace = qlog::Trace::new( 266 //! # qlog::VantagePoint { 267 //! # name: Some("Example client".to_string()), 268 //! # ty: qlog::VantagePointType::Client, 269 //! # flow: None, 270 //! # }, 271 //! # Some("Example qlog trace".to_string()), 272 //! # Some("Example qlog trace description".to_string()), 273 //! # Some(qlog::Configuration { 274 //! # time_offset: Some("0".to_string()), 275 //! # time_units: Some(qlog::TimeUnits::Ms), 276 //! # original_uris: None, 277 //! # }), 278 //! # None, 279 //! # ); 280 //! # let mut file = std::fs::File::create("foo.qlog").unwrap(); 281 //! let mut streamer = qlog::QlogStreamer::new( 282 //! qlog::QLOG_VERSION.to_string(), 283 //! Some("Example qlog".to_string()), 284 //! Some("Example qlog description".to_string()), 285 //! None, 286 //! std::time::Instant::now(), 287 //! trace, 288 //! Box::new(file), 289 //! ); 290 //! 291 //! streamer.start_log().ok(); 292 //! ``` 293 //! 294 //! ### Adding simple events 295 //! 296 //! Once logging has started you can stream events. Simple events 297 //! can be written in one step using [`add_event()`]: 298 //! 299 //! ``` 300 //! # let mut trace = qlog::Trace::new( 301 //! # qlog::VantagePoint { 302 //! # name: Some("Example client".to_string()), 303 //! # ty: qlog::VantagePointType::Client, 304 //! # flow: None, 305 //! # }, 306 //! # Some("Example qlog trace".to_string()), 307 //! # Some("Example qlog trace description".to_string()), 308 //! # Some(qlog::Configuration { 309 //! # time_offset: Some("0".to_string()), 310 //! # time_units: Some(qlog::TimeUnits::Ms), 311 //! # original_uris: None, 312 //! # }), 313 //! # None, 314 //! # ); 315 //! # let mut file = std::fs::File::create("foo.qlog").unwrap(); 316 //! # let mut streamer = qlog::QlogStreamer::new( 317 //! # qlog::QLOG_VERSION.to_string(), 318 //! # Some("Example qlog".to_string()), 319 //! # Some("Example qlog description".to_string()), 320 //! # None, 321 //! # std::time::Instant::now(), 322 //! # trace, 323 //! # Box::new(file), 324 //! # ); 325 //! let event = qlog::event::Event::metrics_updated_min(); 326 //! streamer.add_event(event).ok(); 327 //! ``` 328 //! 329 //! ### Adding events with frames 330 //! Some events contain optional arrays of QUIC frames. If the 331 //! event has `Some(Vec<QuicFrame>)`, even if it is empty, the 332 //! streamer enters a frame serializing mode that must be 333 //! finalized before other events can be logged. 334 //! 335 //! In this example, a `PacketSent` event is created with an 336 //! empty frame array and frames are written out later: 337 //! 338 //! ``` 339 //! # let mut trace = qlog::Trace::new( 340 //! # qlog::VantagePoint { 341 //! # name: Some("Example client".to_string()), 342 //! # ty: qlog::VantagePointType::Client, 343 //! # flow: None, 344 //! # }, 345 //! # Some("Example qlog trace".to_string()), 346 //! # Some("Example qlog trace description".to_string()), 347 //! # Some(qlog::Configuration { 348 //! # time_offset: Some("0".to_string()), 349 //! # time_units: Some(qlog::TimeUnits::Ms), 350 //! # original_uris: None, 351 //! # }), 352 //! # None, 353 //! # ); 354 //! # let mut file = std::fs::File::create("foo.qlog").unwrap(); 355 //! # let mut streamer = qlog::QlogStreamer::new( 356 //! # qlog::QLOG_VERSION.to_string(), 357 //! # Some("Example qlog".to_string()), 358 //! # Some("Example qlog description".to_string()), 359 //! # None, 360 //! # std::time::Instant::now(), 361 //! # trace, 362 //! # Box::new(file), 363 //! # ); 364 //! let qlog_pkt_hdr = qlog::PacketHeader::with_type( 365 //! qlog::PacketType::OneRtt, 366 //! 0, 367 //! Some(1251), 368 //! Some(1224), 369 //! Some(0xff00001b), 370 //! Some(b"7e37e4dcc6682da8"), 371 //! Some(b"36ce104eee50101c"), 372 //! ); 373 //! 374 //! let event = qlog::event::Event::packet_sent_min( 375 //! qlog::PacketType::OneRtt, 376 //! qlog_pkt_hdr, 377 //! Some(Vec::new()), 378 //! ); 379 //! 380 //! streamer.add_event(event).ok(); 381 //! ``` 382 //! 383 //! In this example, the frames contained in the QUIC packet 384 //! are PING and PADDING. Each frame is written using the 385 //! [`add_frame()`] method. Frame writing is concluded with 386 //! [`finish_frames()`]. 387 //! 388 //! ``` 389 //! # let mut trace = qlog::Trace::new( 390 //! # qlog::VantagePoint { 391 //! # name: Some("Example client".to_string()), 392 //! # ty: qlog::VantagePointType::Client, 393 //! # flow: None, 394 //! # }, 395 //! # Some("Example qlog trace".to_string()), 396 //! # Some("Example qlog trace description".to_string()), 397 //! # Some(qlog::Configuration { 398 //! # time_offset: Some("0".to_string()), 399 //! # time_units: Some(qlog::TimeUnits::Ms), 400 //! # original_uris: None, 401 //! # }), 402 //! # None, 403 //! # ); 404 //! # let mut file = std::fs::File::create("foo.qlog").unwrap(); 405 //! # let mut streamer = qlog::QlogStreamer::new( 406 //! # qlog::QLOG_VERSION.to_string(), 407 //! # Some("Example qlog".to_string()), 408 //! # Some("Example qlog description".to_string()), 409 //! # None, 410 //! # std::time::Instant::now(), 411 //! # trace, 412 //! # Box::new(file), 413 //! # ); 414 //! 415 //! let ping = qlog::QuicFrame::ping(); 416 //! let padding = qlog::QuicFrame::padding(); 417 //! 418 //! streamer.add_frame(ping, false).ok(); 419 //! streamer.add_frame(padding, false).ok(); 420 //! 421 //! streamer.finish_frames().ok(); 422 //! ``` 423 //! 424 //! Once all events have have been written, the log 425 //! can be finalized with [`finish_log()`]: 426 //! 427 //! ``` 428 //! # let mut trace = qlog::Trace::new( 429 //! # qlog::VantagePoint { 430 //! # name: Some("Example client".to_string()), 431 //! # ty: qlog::VantagePointType::Client, 432 //! # flow: None, 433 //! # }, 434 //! # Some("Example qlog trace".to_string()), 435 //! # Some("Example qlog trace description".to_string()), 436 //! # Some(qlog::Configuration { 437 //! # time_offset: Some("0".to_string()), 438 //! # time_units: Some(qlog::TimeUnits::Ms), 439 //! # original_uris: None, 440 //! # }), 441 //! # None, 442 //! # ); 443 //! # let mut file = std::fs::File::create("foo.qlog").unwrap(); 444 //! # let mut streamer = qlog::QlogStreamer::new( 445 //! # qlog::QLOG_VERSION.to_string(), 446 //! # Some("Example qlog".to_string()), 447 //! # Some("Example qlog description".to_string()), 448 //! # None, 449 //! # std::time::Instant::now(), 450 //! # trace, 451 //! # Box::new(file), 452 //! # ); 453 //! streamer.finish_log().ok(); 454 //! ``` 455 //! 456 //! ### Serializing 457 //! 458 //! Serialization to JSON occurs as methods on the [`QlogStreamer`] 459 //! are called. No additional steps are required. 460 //! 461 //! [`Trace`]: struct.Trace.html 462 //! [`VantagePoint`]: struct.VantagePoint.html 463 //! [`Configuration`]: struct.Configuration.html 464 //! [`EventField`]: enum.EventField.html 465 //! [`EventField::RelativeTime`]: enum.EventField.html#variant.RelativeTime 466 //! [`EventField::Category`]: enum.EventField.html#variant.Category 467 //! [`EventField::Type`]: enum.EventField.html#variant.Type 468 //! [`EventField::Data`]: enum.EventField.html#variant.Data 469 //! [`qlog::Trace.events`]: struct.Trace.html#structfield.events 470 //! [`push_event()`]: struct.Trace.html#method.push_event 471 //! [`packet_sent_min()`]: event/struct.Event.html#method.packet_sent_min 472 //! [`QuicFrame::crypto()`]: enum.QuicFrame.html#variant.Crypto 473 //! [`QlogStreamer`]: struct.QlogStreamer.html 474 //! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html 475 //! [`start_log()`]: struct.QlogStreamer.html#method.start_log 476 //! [`add_event()`]: struct.QlogStreamer.html#method.add_event 477 //! [`add_frame()`]: struct.QlogStreamer.html#method.add_frame 478 //! [`finish_frames()`]: struct.QlogStreamer.html#method.finish_frames 479 //! [`finish_log()`]: struct.QlogStreamer.html#method.finish_log 480 481 use serde::Serialize; 482 483 /// A quiche qlog error. 484 #[derive(Debug)] 485 pub enum Error { 486 /// There is no more work to do. 487 Done, 488 489 /// The operation cannot be completed because it was attempted 490 /// in an invalid state. 491 InvalidState, 492 493 /// I/O error. 494 IoError(std::io::Error), 495 } 496 497 impl std::fmt::Display for Error { fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result498 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 499 write!(f, "{:?}", self) 500 } 501 } 502 503 impl std::error::Error for Error { source(&self) -> Option<&(dyn std::error::Error + 'static)>504 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 505 None 506 } 507 } 508 509 impl std::convert::From<std::io::Error> for Error { from(err: std::io::Error) -> Self510 fn from(err: std::io::Error) -> Self { 511 Error::IoError(err) 512 } 513 } 514 515 pub const QLOG_VERSION: &str = "draft-02-wip"; 516 517 /// A specialized [`Result`] type for quiche qlog operations. 518 /// 519 /// This type is used throughout the public API for any operation that 520 /// can produce an error. 521 /// 522 /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html 523 pub type Result<T> = std::result::Result<T, Error>; 524 525 #[serde_with::skip_serializing_none] 526 #[derive(Serialize, Clone)] 527 pub struct Qlog { 528 pub qlog_version: String, 529 pub title: Option<String>, 530 pub description: Option<String>, 531 pub summary: Option<String>, 532 533 pub traces: Vec<Trace>, 534 } 535 536 impl Default for Qlog { default() -> Self537 fn default() -> Self { 538 Qlog { 539 qlog_version: QLOG_VERSION.to_string(), 540 title: Some("Default qlog title".to_string()), 541 description: Some("Default qlog description".to_string()), 542 summary: Some("Default qlog title".to_string()), 543 traces: Vec::new(), 544 } 545 } 546 } 547 548 #[derive(PartialEq)] 549 pub enum StreamerState { 550 Initial, 551 Ready, 552 WritingFrames, 553 Finished, 554 } 555 556 /// A helper object specialized for streaming JSON-serialized qlog to a 557 /// [`Write`] trait. 558 /// 559 /// The object is responsible for the `Qlog` object that contains the provided 560 /// `Trace`. 561 /// 562 /// Serialization is progressively driven by method calls; once log streaming is 563 /// started, `event::Events` can be written using `add_event()`. Some events 564 /// can contain an array of `QuicFrame`s, when writing such an event, the 565 /// streamer enters a frame-serialization mode where frames are be progressively 566 /// written using `add_frame()`. This mode is concluded using 567 /// `finished_frames()`. While serializing frames, any attempts to log 568 /// additional events are ignored. 569 /// 570 /// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html 571 pub struct QlogStreamer { 572 start_time: std::time::Instant, 573 writer: Box<dyn std::io::Write + Send + Sync>, 574 qlog: Qlog, 575 state: StreamerState, 576 first_event: bool, 577 first_frame: bool, 578 } 579 580 impl QlogStreamer { 581 /// Creates a QlogStreamer object. 582 /// 583 /// It owns a `Qlog` object that contains the provided `Trace`, which must 584 /// have the following ordered-set of names EventFields: 585 /// 586 /// ["relative_time", "category", "event".to_string(), "data"] 587 /// 588 /// All serialization will be written to the provided `Write`. new( qlog_version: String, title: Option<String>, description: Option<String>, summary: Option<String>, start_time: std::time::Instant, trace: Trace, writer: Box<dyn std::io::Write + Send + Sync>, ) -> Self589 pub fn new( 590 qlog_version: String, title: Option<String>, description: Option<String>, 591 summary: Option<String>, start_time: std::time::Instant, trace: Trace, 592 writer: Box<dyn std::io::Write + Send + Sync>, 593 ) -> Self { 594 let qlog = Qlog { 595 qlog_version, 596 title, 597 description, 598 summary, 599 traces: vec![trace], 600 }; 601 602 QlogStreamer { 603 start_time, 604 writer, 605 qlog, 606 state: StreamerState::Initial, 607 first_event: true, 608 first_frame: false, 609 } 610 } 611 612 /// Starts qlog streaming serialization. 613 /// 614 /// This writes out the JSON-serialized form of all information up to qlog 615 /// `Trace`'s array of `EventField`s. EventFields are separately appended 616 /// using functions that accept and `event::Event`. start_log(&mut self) -> Result<()>617 pub fn start_log(&mut self) -> Result<()> { 618 if self.state != StreamerState::Initial { 619 return Err(Error::Done); 620 } 621 622 // A qlog contains a trace holding a vector of events that we want to 623 // serialize in a streaming manner. So at the start of serialization, 624 // take off all closing delimiters, and leave us in a state to accept 625 // new events. 626 match serde_json::to_string(&self.qlog) { 627 Ok(mut out) => { 628 out.truncate(out.len() - 4); 629 630 self.writer.as_mut().write_all(out.as_bytes())?; 631 632 self.state = StreamerState::Ready; 633 634 self.first_event = self.qlog.traces[0].events.is_empty(); 635 }, 636 637 _ => return Err(Error::Done), 638 } 639 640 Ok(()) 641 } 642 643 /// Finishes qlog streaming serialization. 644 /// 645 /// The JSON-serialized output has remaining close delimiters added. 646 /// After this is called, no more serialization will occur. finish_log(&mut self) -> Result<()>647 pub fn finish_log(&mut self) -> Result<()> { 648 if self.state == StreamerState::Initial || 649 self.state == StreamerState::Finished 650 { 651 return Err(Error::InvalidState); 652 } 653 654 self.writer.as_mut().write_all(b"]}]}")?; 655 656 self.state = StreamerState::Finished; 657 658 self.writer.as_mut().flush()?; 659 660 Ok(()) 661 } 662 663 /// Writes a JSON-serialized `EventField`s. 664 /// 665 /// Some qlog events can contain `QuicFrames`. If this is detected `true` is 666 /// returned and the streamer enters a frame-serialization mode that is only 667 /// concluded by `finish_frames()`. In this mode, attempts to log additional 668 /// events are ignored. 669 /// 670 /// If the event contains no array of `QuicFrames` return `false`. add_event(&mut self, event: event::Event) -> Result<bool>671 pub fn add_event(&mut self, event: event::Event) -> Result<bool> { 672 if self.state != StreamerState::Ready { 673 return Err(Error::InvalidState); 674 } 675 676 let event_time = if cfg!(test) { 677 std::time::Duration::from_secs(0) 678 } else { 679 self.start_time.elapsed() 680 }; 681 682 let rel = match &self.qlog.traces[0].configuration { 683 Some(conf) => match conf.time_units { 684 Some(TimeUnits::Ms) => event_time.as_millis().to_string(), 685 686 Some(TimeUnits::Us) => event_time.as_micros().to_string(), 687 688 None => String::from(""), 689 }, 690 691 None => String::from(""), 692 }; 693 694 let (ev_data, contains_frames) = match serde_json::to_string(&event.data) 695 { 696 Ok(mut ev_data_out) => 697 if let Some(f) = event.data.contains_quic_frames() { 698 ev_data_out.truncate(ev_data_out.len() - 2); 699 700 if f == 0 { 701 self.first_frame = true; 702 } 703 704 (ev_data_out, true) 705 } else { 706 (ev_data_out, false) 707 }, 708 709 _ => return Err(Error::Done), 710 }; 711 712 let maybe_comma = if self.first_event { 713 self.first_event = false; 714 "" 715 } else { 716 "," 717 }; 718 719 let maybe_terminate = if contains_frames { "" } else { "]" }; 720 721 let ev_time = serde_json::to_string(&EventField::RelativeTime(rel)).ok(); 722 let ev_cat = 723 serde_json::to_string(&EventField::Category(event.category)).ok(); 724 let ev_ty = serde_json::to_string(&EventField::Event(event.ty)).ok(); 725 726 if let (Some(ev_time), Some(ev_cat), Some(ev_ty)) = 727 (ev_time, ev_cat, ev_ty) 728 { 729 let out = format!( 730 "{}[{},{},{},{}{}", 731 maybe_comma, ev_time, ev_cat, ev_ty, ev_data, maybe_terminate 732 ); 733 734 self.writer.as_mut().write_all(out.as_bytes())?; 735 736 if contains_frames { 737 self.state = StreamerState::WritingFrames 738 } else { 739 self.state = StreamerState::Ready 740 }; 741 742 return Ok(contains_frames); 743 } 744 745 Err(Error::Done) 746 } 747 748 /// Writes a JSON-serialized `QuicFrame`. 749 /// 750 /// Only valid while in the frame-serialization mode. add_frame(&mut self, frame: QuicFrame, last: bool) -> Result<()>751 pub fn add_frame(&mut self, frame: QuicFrame, last: bool) -> Result<()> { 752 if self.state != StreamerState::WritingFrames { 753 return Err(Error::InvalidState); 754 } 755 756 match serde_json::to_string(&frame) { 757 Ok(mut out) => { 758 if !self.first_frame { 759 out.insert(0, ','); 760 } else { 761 self.first_frame = false; 762 } 763 764 self.writer.as_mut().write_all(out.as_bytes())?; 765 766 if last { 767 self.finish_frames()?; 768 } 769 }, 770 771 _ => return Err(Error::Done), 772 } 773 774 Ok(()) 775 } 776 777 /// Concludes `QuicFrame` streaming serialization. 778 /// 779 /// Only valid while in the frame-serialization mode. finish_frames(&mut self) -> Result<()>780 pub fn finish_frames(&mut self) -> Result<()> { 781 if self.state != StreamerState::WritingFrames { 782 return Err(Error::InvalidState); 783 } 784 785 self.writer.as_mut().write_all(b"]}]")?; 786 self.state = StreamerState::Ready; 787 788 Ok(()) 789 } 790 791 /// Returns the writer. 792 #[allow(clippy::borrowed_box)] writer(&self) -> &Box<dyn std::io::Write + Send + Sync>793 pub fn writer(&self) -> &Box<dyn std::io::Write + Send + Sync> { 794 &self.writer 795 } 796 } 797 798 #[serde_with::skip_serializing_none] 799 #[derive(Serialize, Clone)] 800 pub struct Trace { 801 pub vantage_point: VantagePoint, 802 pub title: Option<String>, 803 pub description: Option<String>, 804 805 pub configuration: Option<Configuration>, 806 807 pub common_fields: Option<CommonFields>, 808 pub event_fields: Vec<String>, 809 810 pub events: Vec<Vec<EventField>>, 811 } 812 813 /// Helper functions for using a qlog trace. 814 impl Trace { 815 /// Creates a new qlog trace with the hard-coded event_fields 816 /// ["relative_time", "category", "event", "data"] new( vantage_point: VantagePoint, title: Option<String>, description: Option<String>, configuration: Option<Configuration>, common_fields: Option<CommonFields>, ) -> Self817 pub fn new( 818 vantage_point: VantagePoint, title: Option<String>, 819 description: Option<String>, configuration: Option<Configuration>, 820 common_fields: Option<CommonFields>, 821 ) -> Self { 822 Trace { 823 vantage_point, 824 title, 825 description, 826 configuration, 827 common_fields, 828 event_fields: vec![ 829 "relative_time".to_string(), 830 "category".to_string(), 831 "event".to_string(), 832 "data".to_string(), 833 ], 834 events: Vec::new(), 835 } 836 } 837 push_event( &mut self, relative_time: std::time::Duration, event: crate::event::Event, )838 pub fn push_event( 839 &mut self, relative_time: std::time::Duration, event: crate::event::Event, 840 ) { 841 let rel = match &self.configuration { 842 Some(conf) => match conf.time_units { 843 Some(TimeUnits::Ms) => relative_time.as_millis().to_string(), 844 845 Some(TimeUnits::Us) => relative_time.as_micros().to_string(), 846 847 None => String::from(""), 848 }, 849 850 None => String::from(""), 851 }; 852 853 self.events.push(vec![ 854 EventField::RelativeTime(rel), 855 EventField::Category(event.category), 856 EventField::Event(event.ty), 857 EventField::Data(event.data), 858 ]); 859 } 860 } 861 862 #[serde_with::skip_serializing_none] 863 #[derive(Serialize, Clone)] 864 pub struct VantagePoint { 865 pub name: Option<String>, 866 867 #[serde(rename = "type")] 868 pub ty: VantagePointType, 869 870 pub flow: Option<VantagePointType>, 871 } 872 873 #[derive(Serialize, Clone)] 874 #[serde(rename_all = "snake_case")] 875 pub enum VantagePointType { 876 Client, 877 Server, 878 Network, 879 Unknown, 880 } 881 882 #[derive(Serialize, Clone)] 883 #[serde(rename_all = "snake_case")] 884 pub enum TimeUnits { 885 Ms, 886 Us, 887 } 888 889 #[serde_with::skip_serializing_none] 890 #[derive(Serialize, Clone)] 891 pub struct Configuration { 892 pub time_units: Option<TimeUnits>, 893 pub time_offset: Option<String>, 894 895 pub original_uris: Option<Vec<String>>, 896 /* TODO 897 * additionalUserSpecifiedProperty */ 898 } 899 900 impl Default for Configuration { default() -> Self901 fn default() -> Self { 902 Configuration { 903 time_units: Some(TimeUnits::Ms), 904 time_offset: Some("0".to_string()), 905 original_uris: None, 906 } 907 } 908 } 909 910 #[serde_with::skip_serializing_none] 911 #[derive(Serialize, Clone, Default)] 912 pub struct CommonFields { 913 pub group_id: Option<String>, 914 pub protocol_type: Option<String>, 915 916 pub reference_time: Option<String>, 917 /* TODO 918 * additionalUserSpecifiedProperty */ 919 } 920 921 #[derive(Serialize, Clone)] 922 #[serde(untagged)] 923 pub enum EventType { 924 ConnectivityEventType(ConnectivityEventType), 925 926 TransportEventType(TransportEventType), 927 928 SecurityEventType(SecurityEventType), 929 930 RecoveryEventType(RecoveryEventType), 931 932 Http3EventType(Http3EventType), 933 934 QpackEventType(QpackEventType), 935 936 GenericEventType(GenericEventType), 937 } 938 939 #[derive(Serialize, Clone)] 940 #[serde(untagged)] 941 #[allow(clippy::large_enum_variant)] 942 pub enum EventField { 943 RelativeTime(String), 944 945 Category(EventCategory), 946 947 Event(EventType), 948 949 Data(EventData), 950 } 951 952 #[derive(Serialize, Clone)] 953 #[serde(rename_all = "snake_case")] 954 pub enum EventCategory { 955 Connectivity, 956 Security, 957 Transport, 958 Recovery, 959 Http, 960 Qpack, 961 962 Error, 963 Warning, 964 Info, 965 Debug, 966 Verbose, 967 Simulation, 968 } 969 970 #[derive(Serialize, Clone)] 971 #[serde(rename_all = "snake_case")] 972 pub enum ConnectivityEventType { 973 ServerListening, 974 ConnectionStarted, 975 ConnectionIdUpdated, 976 SpinBitUpdated, 977 ConnectionStateUpdated, 978 } 979 980 #[derive(Serialize, Clone)] 981 #[serde(rename_all = "snake_case")] 982 pub enum TransportEventType { 983 ParametersSet, 984 985 DatagramsSent, 986 DatagramsReceived, 987 DatagramDropped, 988 989 PacketSent, 990 PacketReceived, 991 PacketDropped, 992 PacketBuffered, 993 994 FramesProcessed, 995 996 StreamStateUpdated, 997 } 998 999 #[derive(Serialize, Clone)] 1000 #[serde(rename_all = "snake_case")] 1001 pub enum TransportEventTrigger { 1002 Line, 1003 Retransmit, 1004 KeysUnavailable, 1005 } 1006 1007 #[derive(Serialize, Clone)] 1008 #[serde(rename_all = "snake_case")] 1009 pub enum SecurityEventType { 1010 KeyUpdated, 1011 KeyRetired, 1012 } 1013 1014 #[derive(Serialize, Clone)] 1015 #[serde(rename_all = "snake_case")] 1016 pub enum SecurityEventTrigger { 1017 Tls, 1018 Implicit, 1019 RemoteUpdate, 1020 LocalUpdate, 1021 } 1022 1023 #[derive(Serialize, Clone)] 1024 #[serde(rename_all = "snake_case")] 1025 pub enum RecoveryEventType { 1026 ParametersSet, 1027 MetricsUpdated, 1028 CongestionStateUpdated, 1029 LossTimerSet, 1030 LossTimerTriggered, 1031 PacketLost, 1032 MarkedForRetransmit, 1033 } 1034 1035 #[derive(Serialize, Clone)] 1036 #[serde(rename_all = "snake_case")] 1037 pub enum RecoveryEventTrigger { 1038 AckReceived, 1039 PacketSent, 1040 Alarm, 1041 Unknown, 1042 } 1043 1044 // ================================================================== // 1045 1046 #[derive(Serialize, Clone)] 1047 #[serde(rename_all = "snake_case")] 1048 pub enum KeyType { 1049 ServerInitialSecret, 1050 ClientInitialSecret, 1051 1052 ServerHandshakeSecret, 1053 ClientHandshakeSecret, 1054 1055 Server0RttSecret, 1056 Client0RttSecret, 1057 1058 Server1RttSecret, 1059 Client1RttSecret, 1060 } 1061 1062 #[derive(Serialize, Clone)] 1063 #[serde(rename_all = "snake_case")] 1064 pub enum ConnectionState { 1065 Attempted, 1066 Reset, 1067 Handshake, 1068 Active, 1069 Keepalive, 1070 Draining, 1071 Closed, 1072 } 1073 1074 #[derive(Serialize, Clone)] 1075 #[serde(rename_all = "snake_case")] 1076 pub enum TransportOwner { 1077 Local, 1078 Remote, 1079 } 1080 1081 #[derive(Serialize, Clone)] 1082 pub struct PreferredAddress { 1083 pub ip_v4: String, 1084 pub ip_v6: String, 1085 1086 pub port_v4: u64, 1087 pub port_v6: u64, 1088 1089 pub connection_id: String, 1090 pub stateless_reset_token: String, 1091 } 1092 1093 #[derive(Serialize, Clone)] 1094 #[serde(rename_all = "snake_case")] 1095 pub enum StreamSide { 1096 Sending, 1097 Receiving, 1098 } 1099 1100 #[derive(Serialize, Clone)] 1101 #[serde(rename_all = "snake_case")] 1102 pub enum StreamState { 1103 // bidirectional stream states, draft-23 3.4. 1104 Idle, 1105 Open, 1106 HalfClosedLocal, 1107 HalfClosedRemote, 1108 Closed, 1109 1110 // sending-side stream states, draft-23 3.1. 1111 Ready, 1112 Send, 1113 DataSent, 1114 ResetSent, 1115 ResetReceived, 1116 1117 // receive-side stream states, draft-23 3.2. 1118 Receive, 1119 SizeKnown, 1120 DataRead, 1121 ResetRead, 1122 1123 // both-side states 1124 DataReceived, 1125 1126 // qlog-defined 1127 Destroyed, 1128 } 1129 1130 #[derive(Serialize, Clone)] 1131 #[serde(rename_all = "snake_case")] 1132 pub enum TimerType { 1133 Ack, 1134 Pto, 1135 } 1136 1137 #[derive(Serialize, Clone)] 1138 #[serde(rename_all = "snake_case")] 1139 pub enum H3Owner { 1140 Local, 1141 Remote, 1142 } 1143 1144 #[derive(Serialize, Clone)] 1145 #[serde(rename_all = "snake_case")] 1146 pub enum H3StreamType { 1147 Data, 1148 Control, 1149 Push, 1150 Reserved, 1151 QpackEncode, 1152 QpackDecode, 1153 } 1154 1155 #[derive(Serialize, Clone)] 1156 #[serde(rename_all = "snake_case")] 1157 pub enum H3DataRecipient { 1158 Application, 1159 Transport, 1160 } 1161 1162 #[derive(Serialize, Clone)] 1163 #[serde(rename_all = "snake_case")] 1164 pub enum H3PushDecision { 1165 Claimed, 1166 Abandoned, 1167 } 1168 1169 #[derive(Serialize, Clone)] 1170 #[serde(rename_all = "snake_case")] 1171 pub enum QpackOwner { 1172 Local, 1173 Remote, 1174 } 1175 1176 #[derive(Serialize, Clone)] 1177 #[serde(rename_all = "snake_case")] 1178 pub enum QpackStreamState { 1179 Blocked, 1180 Unblocked, 1181 } 1182 1183 #[derive(Serialize, Clone)] 1184 #[serde(rename_all = "snake_case")] 1185 pub enum QpackUpdateType { 1186 Added, 1187 Evicted, 1188 } 1189 1190 #[derive(Serialize, Clone)] 1191 pub struct QpackDynamicTableEntry { 1192 pub index: u64, 1193 pub name: Option<String>, 1194 pub value: Option<String>, 1195 } 1196 1197 #[derive(Serialize, Clone)] 1198 pub struct QpackHeaderBlockPrefix { 1199 pub required_insert_count: u64, 1200 pub sign_bit: bool, 1201 pub delta_base: u64, 1202 } 1203 1204 #[serde_with::skip_serializing_none] 1205 #[derive(Serialize, Clone)] 1206 #[serde(untagged)] 1207 #[allow(clippy::large_enum_variant)] 1208 pub enum EventData { 1209 // ================================================================== // 1210 // CONNECTIVITY 1211 ServerListening { 1212 ip_v4: Option<String>, 1213 ip_v6: Option<String>, 1214 port_v4: u64, 1215 port_v6: u64, 1216 1217 quic_versions: Option<Vec<String>>, 1218 alpn_values: Option<Vec<String>>, 1219 1220 stateless_reset_required: Option<bool>, 1221 }, 1222 1223 ConnectionStarted { 1224 ip_version: String, 1225 src_ip: String, 1226 dst_ip: String, 1227 1228 protocol: Option<String>, 1229 src_port: u64, 1230 dst_port: u64, 1231 1232 quic_version: Option<String>, 1233 src_cid: Option<String>, 1234 dst_cid: Option<String>, 1235 }, 1236 1237 ConnectionIdUpdated { 1238 src_old: Option<String>, 1239 src_new: Option<String>, 1240 1241 dst_old: Option<String>, 1242 dst_new: Option<String>, 1243 }, 1244 1245 SpinBitUpdated { 1246 state: bool, 1247 }, 1248 1249 ConnectionStateUpdated { 1250 old: Option<ConnectionState>, 1251 new: ConnectionState, 1252 }, 1253 1254 // ================================================================== // 1255 // SECURITY 1256 KeyUpdated { 1257 key_type: KeyType, 1258 old: Option<String>, 1259 new: String, 1260 generation: Option<u64>, 1261 }, 1262 1263 KeyRetired { 1264 key_type: KeyType, 1265 key: Option<String>, 1266 generation: Option<u64>, 1267 }, 1268 1269 // ================================================================== // 1270 // TRANSPORT 1271 TransportParametersSet { 1272 owner: Option<TransportOwner>, 1273 1274 resumption_allowed: Option<bool>, 1275 early_data_enabled: Option<bool>, 1276 alpn: Option<String>, 1277 version: Option<String>, 1278 tls_cipher: Option<String>, 1279 1280 original_connection_id: Option<String>, 1281 stateless_reset_token: Option<String>, 1282 disable_active_migration: Option<bool>, 1283 1284 idle_timeout: Option<u64>, 1285 max_packet_size: Option<u64>, 1286 ack_delay_exponent: Option<u64>, 1287 max_ack_delay: Option<u64>, 1288 active_connection_id_limit: Option<u64>, 1289 1290 initial_max_data: Option<String>, 1291 initial_max_stream_data_bidi_local: Option<String>, 1292 initial_max_stream_data_bidi_remote: Option<String>, 1293 initial_max_stream_data_uni: Option<String>, 1294 initial_max_streams_bidi: Option<String>, 1295 initial_max_streams_uni: Option<String>, 1296 1297 preferred_address: Option<PreferredAddress>, 1298 }, 1299 1300 DatagramsReceived { 1301 count: Option<u64>, 1302 byte_length: Option<u64>, 1303 }, 1304 1305 DatagramsSent { 1306 count: Option<u64>, 1307 byte_length: Option<u64>, 1308 }, 1309 1310 DatagramDropped { 1311 byte_length: Option<u64>, 1312 }, 1313 1314 PacketReceived { 1315 packet_type: PacketType, 1316 header: PacketHeader, 1317 // `frames` is defined here in the QLog schema specification. However, 1318 // our streaming serializer requires serde to put the object at the end, 1319 // so we define it there and depend on serde's preserve_order feature. 1320 is_coalesced: Option<bool>, 1321 1322 raw_encrypted: Option<String>, 1323 raw_decrypted: Option<String>, 1324 frames: Option<Vec<QuicFrame>>, 1325 }, 1326 1327 PacketSent { 1328 packet_type: PacketType, 1329 header: PacketHeader, 1330 // `frames` is defined here in the QLog schema specification. However, 1331 // our streaming serializer requires serde to put the object at the end, 1332 // so we define it there and depend on serde's preserve_order feature. 1333 is_coalesced: Option<bool>, 1334 1335 raw_encrypted: Option<String>, 1336 raw_decrypted: Option<String>, 1337 frames: Option<Vec<QuicFrame>>, 1338 }, 1339 1340 PacketDropped { 1341 packet_type: Option<PacketType>, 1342 packet_size: Option<u64>, 1343 1344 raw: Option<String>, 1345 }, 1346 1347 PacketBuffered { 1348 packet_type: PacketType, 1349 packet_number: String, 1350 }, 1351 1352 StreamStateUpdated { 1353 stream_id: String, 1354 stream_type: Option<StreamType>, 1355 1356 old: Option<StreamState>, 1357 new: StreamState, 1358 1359 stream_side: Option<StreamSide>, 1360 }, 1361 1362 FramesProcessed { 1363 frames: Vec<QuicFrame>, 1364 }, 1365 1366 // ================================================================== // 1367 // RECOVERY 1368 RecoveryParametersSet { 1369 reordering_threshold: Option<u64>, 1370 time_threshold: Option<u64>, 1371 timer_granularity: Option<u64>, 1372 initial_rtt: Option<u64>, 1373 1374 max_datagram_size: Option<u64>, 1375 initial_congestion_window: Option<u64>, 1376 minimum_congestion_window: Option<u64>, 1377 loss_reduction_factor: Option<u64>, 1378 persistent_congestion_threshold: Option<u64>, 1379 }, 1380 1381 MetricsUpdated { 1382 min_rtt: Option<u64>, 1383 smoothed_rtt: Option<u64>, 1384 latest_rtt: Option<u64>, 1385 rtt_variance: Option<u64>, 1386 1387 max_ack_delay: Option<u64>, 1388 pto_count: Option<u64>, 1389 1390 congestion_window: Option<u64>, 1391 bytes_in_flight: Option<u64>, 1392 1393 ssthresh: Option<u64>, 1394 1395 // qlog defined 1396 packets_in_flight: Option<u64>, 1397 in_recovery: Option<bool>, 1398 1399 pacing_rate: Option<u64>, 1400 }, 1401 1402 CongestionStateUpdated { 1403 old: Option<String>, 1404 new: String, 1405 }, 1406 1407 LossTimerSet { 1408 timer_type: Option<TimerType>, 1409 timeout: Option<String>, 1410 }, 1411 1412 PacketLost { 1413 packet_type: PacketType, 1414 packet_number: String, 1415 1416 header: Option<PacketHeader>, 1417 frames: Vec<QuicFrame>, 1418 }, 1419 1420 MarkedForRetransmit { 1421 frames: Vec<QuicFrame>, 1422 }, 1423 1424 // ================================================================== // 1425 // HTTP/3 1426 H3ParametersSet { 1427 owner: Option<H3Owner>, 1428 1429 max_header_list_size: Option<u64>, 1430 max_table_capacity: Option<u64>, 1431 blocked_streams_count: Option<u64>, 1432 1433 push_allowed: Option<bool>, 1434 1435 waits_for_settings: Option<bool>, 1436 }, 1437 1438 H3StreamTypeSet { 1439 stream_id: String, 1440 owner: Option<H3Owner>, 1441 1442 old: Option<H3StreamType>, 1443 new: H3StreamType, 1444 }, 1445 1446 H3FrameCreated { 1447 stream_id: String, 1448 frame: Http3Frame, 1449 byte_length: Option<String>, 1450 1451 raw: Option<String>, 1452 }, 1453 1454 H3FrameParsed { 1455 stream_id: String, 1456 frame: Http3Frame, 1457 byte_length: Option<String>, 1458 1459 raw: Option<String>, 1460 }, 1461 1462 H3DataMoved { 1463 stream_id: String, 1464 offset: Option<String>, 1465 length: Option<u64>, 1466 1467 from: Option<H3DataRecipient>, 1468 to: Option<H3DataRecipient>, 1469 1470 raw: Option<String>, 1471 }, 1472 1473 H3PushResolved { 1474 push_id: Option<String>, 1475 stream_id: Option<String>, 1476 1477 decision: Option<H3PushDecision>, 1478 }, 1479 1480 // ================================================================== // 1481 // QPACK 1482 QpackStateUpdated { 1483 owner: Option<QpackOwner>, 1484 1485 dynamic_table_capacity: Option<u64>, 1486 dynamic_table_size: Option<u64>, 1487 1488 known_received_count: Option<u64>, 1489 current_insert_count: Option<u64>, 1490 }, 1491 1492 QpackStreamStateUpdated { 1493 stream_id: String, 1494 1495 state: QpackStreamState, 1496 }, 1497 1498 QpackDynamicTableUpdated { 1499 update_type: QpackUpdateType, 1500 1501 entries: Vec<QpackDynamicTableEntry>, 1502 }, 1503 1504 QpackHeadersEncoded { 1505 stream_id: Option<String>, 1506 1507 headers: Option<HttpHeader>, 1508 1509 block_prefix: QpackHeaderBlockPrefix, 1510 header_block: Vec<QpackHeaderBlockRepresentation>, 1511 1512 raw: Option<String>, 1513 }, 1514 1515 QpackHeadersDecoded { 1516 stream_id: Option<String>, 1517 1518 headers: Option<HttpHeader>, 1519 1520 block_prefix: QpackHeaderBlockPrefix, 1521 header_block: Vec<QpackHeaderBlockRepresentation>, 1522 1523 raw: Option<String>, 1524 }, 1525 1526 QpackInstructionSent { 1527 instruction: QPackInstruction, 1528 byte_length: Option<String>, 1529 1530 raw: Option<String>, 1531 }, 1532 1533 QpackInstructionReceived { 1534 instruction: QPackInstruction, 1535 byte_length: Option<String>, 1536 1537 raw: Option<String>, 1538 }, 1539 1540 // ================================================================== // 1541 // Generic 1542 ConnectionError { 1543 code: Option<ConnectionErrorCode>, 1544 description: Option<String>, 1545 }, 1546 1547 ApplicationError { 1548 code: Option<ApplicationErrorCode>, 1549 description: Option<String>, 1550 }, 1551 1552 InternalError { 1553 code: Option<u64>, 1554 description: Option<String>, 1555 }, 1556 1557 InternalWarning { 1558 code: Option<u64>, 1559 description: Option<String>, 1560 }, 1561 1562 Message { 1563 message: String, 1564 }, 1565 1566 Marker { 1567 marker_type: String, 1568 message: Option<String>, 1569 }, 1570 } 1571 1572 impl EventData { 1573 /// Returns size of `EventData` array of `QuicFrame`s if it exists. contains_quic_frames(&self) -> Option<usize>1574 pub fn contains_quic_frames(&self) -> Option<usize> { 1575 // For some EventData variants, the frame array is optional 1576 // but for others it is mandatory. 1577 match self { 1578 EventData::PacketSent { frames, .. } | 1579 EventData::PacketReceived { frames, .. } => 1580 if let Some(f) = frames { 1581 Some(f.len()) 1582 } else { 1583 None 1584 }, 1585 1586 EventData::PacketLost { frames, .. } | 1587 EventData::MarkedForRetransmit { frames } | 1588 EventData::FramesProcessed { frames } => Some(frames.len()), 1589 1590 _ => None, 1591 } 1592 } 1593 } 1594 1595 #[derive(Serialize, Clone)] 1596 #[serde(rename_all = "snake_case")] 1597 pub enum PacketType { 1598 Initial, 1599 Handshake, 1600 1601 #[serde(rename = "0RTT")] 1602 ZeroRtt, 1603 1604 #[serde(rename = "1RTT")] 1605 OneRtt, 1606 1607 Retry, 1608 VersionNegotiation, 1609 Unknown, 1610 } 1611 1612 #[derive(Serialize, Clone)] 1613 #[serde(rename_all = "snake_case")] 1614 pub enum Http3EventType { 1615 ParametersSet, 1616 StreamTypeSet, 1617 FrameCreated, 1618 FrameParsed, 1619 DataMoved, 1620 PushResolved, 1621 } 1622 1623 #[derive(Serialize, Clone)] 1624 #[serde(rename_all = "snake_case")] 1625 pub enum QpackEventType { 1626 StateUpdated, 1627 StreamStateUpdated, 1628 DynamicTableUpdated, 1629 HeadersEncoded, 1630 HeadersDecoded, 1631 InstructionSent, 1632 InstructionReceived, 1633 } 1634 1635 #[derive(Serialize, Clone)] 1636 #[serde(rename_all = "snake_case")] 1637 pub enum QuicFrameTypeName { 1638 Padding, 1639 Ping, 1640 Ack, 1641 ResetStream, 1642 StopSending, 1643 Crypto, 1644 NewToken, 1645 Stream, 1646 MaxData, 1647 MaxStreamData, 1648 MaxStreams, 1649 DataBlocked, 1650 StreamDataBlocked, 1651 StreamsBlocked, 1652 NewConnectionId, 1653 RetireConnectionId, 1654 PathChallenge, 1655 PathResponse, 1656 ConnectionClose, 1657 ApplicationClose, 1658 HandshakeDone, 1659 Datagram, 1660 Unknown, 1661 } 1662 1663 // TODO: search for pub enum Error { to see how best to encode errors in qlog. 1664 #[serde_with::skip_serializing_none] 1665 #[derive(Clone, Serialize)] 1666 pub struct PacketHeader { 1667 pub packet_number: String, 1668 pub packet_size: Option<u64>, 1669 pub payload_length: Option<u64>, 1670 pub version: Option<String>, 1671 pub scil: Option<String>, 1672 pub dcil: Option<String>, 1673 pub scid: Option<String>, 1674 pub dcid: Option<String>, 1675 } 1676 1677 impl PacketHeader { 1678 /// Creates a new PacketHeader. new( packet_number: u64, packet_size: Option<u64>, payload_length: Option<u64>, version: Option<u32>, scid: Option<&[u8]>, dcid: Option<&[u8]>, ) -> Self1679 pub fn new( 1680 packet_number: u64, packet_size: Option<u64>, 1681 payload_length: Option<u64>, version: Option<u32>, scid: Option<&[u8]>, 1682 dcid: Option<&[u8]>, 1683 ) -> Self { 1684 let (scil, scid) = match scid { 1685 Some(cid) => ( 1686 Some(cid.len().to_string()), 1687 Some(format!("{}", HexSlice::new(&cid))), 1688 ), 1689 1690 None => (None, None), 1691 }; 1692 1693 let (dcil, dcid) = match dcid { 1694 Some(cid) => ( 1695 Some(cid.len().to_string()), 1696 Some(format!("{}", HexSlice::new(&cid))), 1697 ), 1698 1699 None => (None, None), 1700 }; 1701 1702 let version = match version { 1703 Some(v) => Some(format!("{:x?}", v)), 1704 1705 None => None, 1706 }; 1707 1708 PacketHeader { 1709 packet_number: packet_number.to_string(), 1710 packet_size, 1711 payload_length, 1712 version, 1713 scil, 1714 dcil, 1715 scid, 1716 dcid, 1717 } 1718 } 1719 1720 /// Creates a new PacketHeader. 1721 /// 1722 /// Once a QUIC connection has formed, version, dcid and scid are stable, so 1723 /// there are space benefits to not logging them in every packet, especially 1724 /// PacketType::OneRtt. with_type( ty: PacketType, packet_number: u64, packet_size: Option<u64>, payload_length: Option<u64>, version: Option<u32>, scid: Option<&[u8]>, dcid: Option<&[u8]>, ) -> Self1725 pub fn with_type( 1726 ty: PacketType, packet_number: u64, packet_size: Option<u64>, 1727 payload_length: Option<u64>, version: Option<u32>, scid: Option<&[u8]>, 1728 dcid: Option<&[u8]>, 1729 ) -> Self { 1730 match ty { 1731 PacketType::OneRtt => PacketHeader::new( 1732 packet_number, 1733 packet_size, 1734 payload_length, 1735 None, 1736 None, 1737 None, 1738 ), 1739 1740 _ => PacketHeader::new( 1741 packet_number, 1742 packet_size, 1743 payload_length, 1744 version, 1745 scid, 1746 dcid, 1747 ), 1748 } 1749 } 1750 } 1751 1752 #[derive(Serialize, Clone)] 1753 #[serde(rename_all = "snake_case")] 1754 pub enum StreamType { 1755 Bidirectional, 1756 Unidirectional, 1757 } 1758 1759 #[derive(Serialize, Clone)] 1760 #[serde(rename_all = "snake_case")] 1761 pub enum ErrorSpace { 1762 TransportError, 1763 ApplicationError, 1764 } 1765 1766 #[derive(Serialize, Clone)] 1767 #[serde(rename_all = "snake_case")] 1768 pub enum GenericEventType { 1769 ConnectionError, 1770 ApplicationError, 1771 InternalError, 1772 InternalWarning, 1773 1774 Message, 1775 Marker, 1776 } 1777 1778 #[derive(Serialize, Clone)] 1779 #[serde(untagged)] 1780 pub enum ConnectionErrorCode { 1781 TransportError(TransportError), 1782 CryptoError(CryptoError), 1783 Value(u64), 1784 } 1785 1786 #[derive(Serialize, Clone)] 1787 #[serde(untagged)] 1788 pub enum ApplicationErrorCode { 1789 ApplicationError(ApplicationError), 1790 Value(u64), 1791 } 1792 1793 #[derive(Serialize, Clone)] 1794 #[serde(rename_all = "snake_case")] 1795 pub enum TransportError { 1796 NoError, 1797 InternalError, 1798 ServerBusy, 1799 FlowControlError, 1800 StreamLimitError, 1801 StreamStateError, 1802 FinalSizeError, 1803 FrameEncodingError, 1804 TransportParameterError, 1805 ProtocolViolation, 1806 InvalidMigration, 1807 CryptoBufferExceeded, 1808 Unknown, 1809 } 1810 1811 // TODO 1812 #[derive(Serialize, Clone)] 1813 #[serde(rename_all = "snake_case")] 1814 pub enum CryptoError { 1815 Prefix, 1816 } 1817 1818 #[derive(Serialize, Clone)] 1819 #[serde(rename_all = "snake_case")] 1820 pub enum ApplicationError { 1821 HttpNoError, 1822 HttpGeneralProtocolError, 1823 HttpInternalError, 1824 HttpRequestCancelled, 1825 HttpIncompleteRequest, 1826 HttpConnectError, 1827 HttpFrameError, 1828 HttpExcessiveLoad, 1829 HttpVersionFallback, 1830 HttpIdError, 1831 HttpStreamCreationError, 1832 HttpClosedCriticalStream, 1833 HttpEarlyResponse, 1834 HttpMissingSettings, 1835 HttpUnexpectedFrame, 1836 HttpRequestRejection, 1837 HttpSettingsError, 1838 Unknown, 1839 } 1840 1841 #[serde_with::skip_serializing_none] 1842 #[derive(Serialize, Clone)] 1843 #[serde(untagged)] 1844 pub enum QuicFrame { 1845 Padding { 1846 frame_type: QuicFrameTypeName, 1847 }, 1848 1849 Ping { 1850 frame_type: QuicFrameTypeName, 1851 }, 1852 1853 Ack { 1854 frame_type: QuicFrameTypeName, 1855 ack_delay: Option<String>, 1856 acked_ranges: Option<Vec<(u64, u64)>>, 1857 1858 ect1: Option<String>, 1859 1860 ect0: Option<String>, 1861 1862 ce: Option<String>, 1863 }, 1864 1865 ResetStream { 1866 frame_type: QuicFrameTypeName, 1867 stream_id: String, 1868 error_code: u64, 1869 final_size: String, 1870 }, 1871 1872 StopSending { 1873 frame_type: QuicFrameTypeName, 1874 stream_id: String, 1875 error_code: u64, 1876 }, 1877 1878 Crypto { 1879 frame_type: QuicFrameTypeName, 1880 offset: String, 1881 length: String, 1882 }, 1883 1884 NewToken { 1885 frame_type: QuicFrameTypeName, 1886 length: String, 1887 token: String, 1888 }, 1889 1890 Stream { 1891 frame_type: QuicFrameTypeName, 1892 stream_id: String, 1893 offset: String, 1894 length: String, 1895 fin: bool, 1896 1897 raw: Option<String>, 1898 }, 1899 1900 MaxData { 1901 frame_type: QuicFrameTypeName, 1902 maximum: String, 1903 }, 1904 1905 MaxStreamData { 1906 frame_type: QuicFrameTypeName, 1907 stream_id: String, 1908 maximum: String, 1909 }, 1910 1911 MaxStreams { 1912 frame_type: QuicFrameTypeName, 1913 stream_type: StreamType, 1914 maximum: String, 1915 }, 1916 1917 DataBlocked { 1918 frame_type: QuicFrameTypeName, 1919 limit: String, 1920 }, 1921 1922 StreamDataBlocked { 1923 frame_type: QuicFrameTypeName, 1924 stream_id: String, 1925 limit: String, 1926 }, 1927 1928 StreamsBlocked { 1929 frame_type: QuicFrameTypeName, 1930 stream_type: StreamType, 1931 limit: String, 1932 }, 1933 1934 NewConnectionId { 1935 frame_type: QuicFrameTypeName, 1936 sequence_number: String, 1937 retire_prior_to: String, 1938 length: u64, 1939 connection_id: String, 1940 reset_token: String, 1941 }, 1942 1943 RetireConnectionId { 1944 frame_type: QuicFrameTypeName, 1945 sequence_number: String, 1946 }, 1947 1948 PathChallenge { 1949 frame_type: QuicFrameTypeName, 1950 1951 data: Option<String>, 1952 }, 1953 1954 PathResponse { 1955 frame_type: QuicFrameTypeName, 1956 1957 data: Option<String>, 1958 }, 1959 1960 ConnectionClose { 1961 frame_type: QuicFrameTypeName, 1962 error_space: ErrorSpace, 1963 error_code: u64, 1964 raw_error_code: u64, 1965 reason: String, 1966 1967 trigger_frame_type: Option<String>, 1968 }, 1969 1970 HandshakeDone { 1971 frame_type: QuicFrameTypeName, 1972 }, 1973 1974 Datagram { 1975 frame_type: QuicFrameTypeName, 1976 length: String, 1977 1978 raw: Option<String>, 1979 }, 1980 1981 Unknown { 1982 frame_type: QuicFrameTypeName, 1983 raw_frame_type: u64, 1984 }, 1985 } 1986 1987 impl QuicFrame { padding() -> Self1988 pub fn padding() -> Self { 1989 QuicFrame::Padding { 1990 frame_type: QuicFrameTypeName::Padding, 1991 } 1992 } 1993 ping() -> Self1994 pub fn ping() -> Self { 1995 QuicFrame::Ping { 1996 frame_type: QuicFrameTypeName::Ping, 1997 } 1998 } 1999 ack( ack_delay: Option<String>, acked_ranges: Option<Vec<(u64, u64)>>, ect1: Option<String>, ect0: Option<String>, ce: Option<String>, ) -> Self2000 pub fn ack( 2001 ack_delay: Option<String>, acked_ranges: Option<Vec<(u64, u64)>>, 2002 ect1: Option<String>, ect0: Option<String>, ce: Option<String>, 2003 ) -> Self { 2004 QuicFrame::Ack { 2005 frame_type: QuicFrameTypeName::Ack, 2006 ack_delay, 2007 acked_ranges, 2008 ect1, 2009 ect0, 2010 ce, 2011 } 2012 } 2013 reset_stream( stream_id: String, error_code: u64, final_size: String, ) -> Self2014 pub fn reset_stream( 2015 stream_id: String, error_code: u64, final_size: String, 2016 ) -> Self { 2017 QuicFrame::ResetStream { 2018 frame_type: QuicFrameTypeName::ResetStream, 2019 stream_id, 2020 error_code, 2021 final_size, 2022 } 2023 } 2024 stop_sending(stream_id: String, error_code: u64) -> Self2025 pub fn stop_sending(stream_id: String, error_code: u64) -> Self { 2026 QuicFrame::StopSending { 2027 frame_type: QuicFrameTypeName::StopSending, 2028 stream_id, 2029 error_code, 2030 } 2031 } 2032 crypto(offset: String, length: String) -> Self2033 pub fn crypto(offset: String, length: String) -> Self { 2034 QuicFrame::Crypto { 2035 frame_type: QuicFrameTypeName::Crypto, 2036 offset, 2037 length, 2038 } 2039 } 2040 new_token(length: String, token: String) -> Self2041 pub fn new_token(length: String, token: String) -> Self { 2042 QuicFrame::NewToken { 2043 frame_type: QuicFrameTypeName::NewToken, 2044 length, 2045 token, 2046 } 2047 } 2048 stream( stream_id: String, offset: String, length: String, fin: bool, raw: Option<String>, ) -> Self2049 pub fn stream( 2050 stream_id: String, offset: String, length: String, fin: bool, 2051 raw: Option<String>, 2052 ) -> Self { 2053 QuicFrame::Stream { 2054 frame_type: QuicFrameTypeName::Stream, 2055 stream_id, 2056 offset, 2057 length, 2058 fin, 2059 raw, 2060 } 2061 } 2062 max_data(maximum: String) -> Self2063 pub fn max_data(maximum: String) -> Self { 2064 QuicFrame::MaxData { 2065 frame_type: QuicFrameTypeName::MaxData, 2066 maximum, 2067 } 2068 } 2069 max_stream_data(stream_id: String, maximum: String) -> Self2070 pub fn max_stream_data(stream_id: String, maximum: String) -> Self { 2071 QuicFrame::MaxStreamData { 2072 frame_type: QuicFrameTypeName::MaxStreamData, 2073 stream_id, 2074 maximum, 2075 } 2076 } 2077 max_streams(stream_type: StreamType, maximum: String) -> Self2078 pub fn max_streams(stream_type: StreamType, maximum: String) -> Self { 2079 QuicFrame::MaxStreams { 2080 frame_type: QuicFrameTypeName::MaxStreams, 2081 stream_type, 2082 maximum, 2083 } 2084 } 2085 data_blocked(limit: String) -> Self2086 pub fn data_blocked(limit: String) -> Self { 2087 QuicFrame::DataBlocked { 2088 frame_type: QuicFrameTypeName::DataBlocked, 2089 limit, 2090 } 2091 } 2092 stream_data_blocked(stream_id: String, limit: String) -> Self2093 pub fn stream_data_blocked(stream_id: String, limit: String) -> Self { 2094 QuicFrame::StreamDataBlocked { 2095 frame_type: QuicFrameTypeName::StreamDataBlocked, 2096 stream_id, 2097 limit, 2098 } 2099 } 2100 streams_blocked(stream_type: StreamType, limit: String) -> Self2101 pub fn streams_blocked(stream_type: StreamType, limit: String) -> Self { 2102 QuicFrame::StreamsBlocked { 2103 frame_type: QuicFrameTypeName::StreamsBlocked, 2104 stream_type, 2105 limit, 2106 } 2107 } 2108 new_connection_id( sequence_number: String, retire_prior_to: String, length: u64, connection_id: String, reset_token: String, ) -> Self2109 pub fn new_connection_id( 2110 sequence_number: String, retire_prior_to: String, length: u64, 2111 connection_id: String, reset_token: String, 2112 ) -> Self { 2113 QuicFrame::NewConnectionId { 2114 frame_type: QuicFrameTypeName::NewConnectionId, 2115 sequence_number, 2116 retire_prior_to, 2117 length, 2118 connection_id, 2119 reset_token, 2120 } 2121 } 2122 retire_connection_id(sequence_number: String) -> Self2123 pub fn retire_connection_id(sequence_number: String) -> Self { 2124 QuicFrame::RetireConnectionId { 2125 frame_type: QuicFrameTypeName::RetireConnectionId, 2126 sequence_number, 2127 } 2128 } 2129 path_challenge(data: Option<String>) -> Self2130 pub fn path_challenge(data: Option<String>) -> Self { 2131 QuicFrame::PathChallenge { 2132 frame_type: QuicFrameTypeName::PathChallenge, 2133 data, 2134 } 2135 } 2136 path_response(data: Option<String>) -> Self2137 pub fn path_response(data: Option<String>) -> Self { 2138 QuicFrame::PathResponse { 2139 frame_type: QuicFrameTypeName::PathResponse, 2140 data, 2141 } 2142 } 2143 connection_close( error_space: ErrorSpace, error_code: u64, raw_error_code: u64, reason: String, trigger_frame_type: Option<String>, ) -> Self2144 pub fn connection_close( 2145 error_space: ErrorSpace, error_code: u64, raw_error_code: u64, 2146 reason: String, trigger_frame_type: Option<String>, 2147 ) -> Self { 2148 QuicFrame::ConnectionClose { 2149 frame_type: QuicFrameTypeName::ConnectionClose, 2150 error_space, 2151 error_code, 2152 raw_error_code, 2153 reason, 2154 trigger_frame_type, 2155 } 2156 } 2157 handshake_done() -> Self2158 pub fn handshake_done() -> Self { 2159 QuicFrame::HandshakeDone { 2160 frame_type: QuicFrameTypeName::HandshakeDone, 2161 } 2162 } 2163 datagram(length: String, raw: Option<String>) -> Self2164 pub fn datagram(length: String, raw: Option<String>) -> Self { 2165 QuicFrame::Datagram { 2166 frame_type: QuicFrameTypeName::Datagram, 2167 length, 2168 raw, 2169 } 2170 } 2171 unknown(raw_frame_type: u64) -> Self2172 pub fn unknown(raw_frame_type: u64) -> Self { 2173 QuicFrame::Unknown { 2174 frame_type: QuicFrameTypeName::Unknown, 2175 raw_frame_type, 2176 } 2177 } 2178 } 2179 2180 // ================================================================== // 2181 #[derive(Serialize, Clone)] 2182 #[serde(rename_all = "snake_case")] 2183 pub enum Http3FrameTypeName { 2184 Data, 2185 Headers, 2186 CancelPush, 2187 Settings, 2188 PushPromise, 2189 Goaway, 2190 MaxPushId, 2191 DuplicatePush, 2192 Reserved, 2193 Unknown, 2194 } 2195 2196 #[derive(Serialize, Clone)] 2197 pub struct HttpHeader { 2198 pub name: String, 2199 pub value: String, 2200 } 2201 2202 #[derive(Serialize, Clone)] 2203 pub struct Setting { 2204 pub name: String, 2205 pub value: String, 2206 } 2207 2208 #[derive(Serialize, Clone)] 2209 pub enum Http3Frame { 2210 Data { 2211 frame_type: Http3FrameTypeName, 2212 2213 raw: Option<String>, 2214 }, 2215 2216 Headers { 2217 frame_type: Http3FrameTypeName, 2218 headers: Vec<HttpHeader>, 2219 }, 2220 2221 CancelPush { 2222 frame_type: Http3FrameTypeName, 2223 push_id: String, 2224 }, 2225 2226 Settings { 2227 frame_type: Http3FrameTypeName, 2228 settings: Vec<Setting>, 2229 }, 2230 2231 PushPromise { 2232 frame_type: Http3FrameTypeName, 2233 push_id: String, 2234 headers: Vec<HttpHeader>, 2235 }, 2236 2237 Goaway { 2238 frame_type: Http3FrameTypeName, 2239 stream_id: String, 2240 }, 2241 2242 MaxPushId { 2243 frame_type: Http3FrameTypeName, 2244 push_id: String, 2245 }, 2246 2247 DuplicatePush { 2248 frame_type: Http3FrameTypeName, 2249 push_id: String, 2250 }, 2251 2252 Reserved { 2253 frame_type: Http3FrameTypeName, 2254 }, 2255 2256 Unknown { 2257 frame_type: Http3FrameTypeName, 2258 }, 2259 } 2260 2261 impl Http3Frame { data(raw: Option<String>) -> Self2262 pub fn data(raw: Option<String>) -> Self { 2263 Http3Frame::Data { 2264 frame_type: Http3FrameTypeName::Data, 2265 raw, 2266 } 2267 } 2268 headers(headers: Vec<HttpHeader>) -> Self2269 pub fn headers(headers: Vec<HttpHeader>) -> Self { 2270 Http3Frame::Headers { 2271 frame_type: Http3FrameTypeName::Headers, 2272 headers, 2273 } 2274 } 2275 cancel_push(push_id: String) -> Self2276 pub fn cancel_push(push_id: String) -> Self { 2277 Http3Frame::CancelPush { 2278 frame_type: Http3FrameTypeName::CancelPush, 2279 push_id, 2280 } 2281 } 2282 settings(settings: Vec<Setting>) -> Self2283 pub fn settings(settings: Vec<Setting>) -> Self { 2284 Http3Frame::Settings { 2285 frame_type: Http3FrameTypeName::Settings, 2286 settings, 2287 } 2288 } 2289 push_promise(push_id: String, headers: Vec<HttpHeader>) -> Self2290 pub fn push_promise(push_id: String, headers: Vec<HttpHeader>) -> Self { 2291 Http3Frame::PushPromise { 2292 frame_type: Http3FrameTypeName::PushPromise, 2293 push_id, 2294 headers, 2295 } 2296 } 2297 goaway(stream_id: String) -> Self2298 pub fn goaway(stream_id: String) -> Self { 2299 Http3Frame::Goaway { 2300 frame_type: Http3FrameTypeName::Goaway, 2301 stream_id, 2302 } 2303 } 2304 max_push_id(push_id: String) -> Self2305 pub fn max_push_id(push_id: String) -> Self { 2306 Http3Frame::MaxPushId { 2307 frame_type: Http3FrameTypeName::MaxPushId, 2308 push_id, 2309 } 2310 } 2311 duplicate_push(push_id: String) -> Self2312 pub fn duplicate_push(push_id: String) -> Self { 2313 Http3Frame::DuplicatePush { 2314 frame_type: Http3FrameTypeName::DuplicatePush, 2315 push_id, 2316 } 2317 } 2318 reserved() -> Self2319 pub fn reserved() -> Self { 2320 Http3Frame::Reserved { 2321 frame_type: Http3FrameTypeName::Reserved, 2322 } 2323 } 2324 unknown() -> Self2325 pub fn unknown() -> Self { 2326 Http3Frame::Unknown { 2327 frame_type: Http3FrameTypeName::Unknown, 2328 } 2329 } 2330 } 2331 2332 #[derive(Serialize, Clone)] 2333 #[serde(rename_all = "snake_case")] 2334 pub enum QpackInstructionTypeName { 2335 SetDynamicTableCapacityInstruction, 2336 InsertWithNameReferenceInstruction, 2337 InsertWithoutNameReferenceInstruction, 2338 DuplicateInstruction, 2339 HeaderAcknowledgementInstruction, 2340 StreamCancellationInstruction, 2341 InsertCountIncrementInstruction, 2342 } 2343 2344 #[derive(Serialize, Clone)] 2345 #[serde(rename_all = "snake_case")] 2346 pub enum QpackTableType { 2347 Static, 2348 Dynamic, 2349 } 2350 2351 #[derive(Serialize, Clone)] 2352 pub enum QPackInstruction { 2353 SetDynamicTableCapacityInstruction { 2354 instruction_type: QpackInstructionTypeName, 2355 2356 capacity: u64, 2357 }, 2358 2359 InsertWithNameReferenceInstruction { 2360 instruction_type: QpackInstructionTypeName, 2361 2362 table_type: QpackTableType, 2363 2364 name_index: u64, 2365 2366 huffman_encoded_value: bool, 2367 value_length: u64, 2368 value: String, 2369 }, 2370 2371 InsertWithoutNameReferenceInstruction { 2372 instruction_type: QpackInstructionTypeName, 2373 2374 huffman_encoded_name: bool, 2375 name_length: u64, 2376 name: String, 2377 2378 huffman_encoded_value: bool, 2379 value_length: u64, 2380 value: String, 2381 }, 2382 2383 DuplicateInstruction { 2384 instruction_type: QpackInstructionTypeName, 2385 2386 index: u64, 2387 }, 2388 2389 HeaderAcknowledgementInstruction { 2390 instruction_type: QpackInstructionTypeName, 2391 2392 stream_id: String, 2393 }, 2394 2395 StreamCancellationInstruction { 2396 instruction_type: QpackInstructionTypeName, 2397 2398 stream_id: String, 2399 }, 2400 2401 InsertCountIncrementInstruction { 2402 instruction_type: QpackInstructionTypeName, 2403 2404 increment: u64, 2405 }, 2406 } 2407 2408 #[derive(Serialize, Clone)] 2409 #[serde(rename_all = "snake_case")] 2410 pub enum QpackHeaderBlockRepresentationTypeName { 2411 IndexedHeaderField, 2412 LiteralHeaderFieldWithName, 2413 LiteralHeaderFieldWithoutName, 2414 } 2415 2416 #[derive(Serialize, Clone)] 2417 pub enum QpackHeaderBlockRepresentation { 2418 IndexedHeaderField { 2419 header_field_type: QpackHeaderBlockRepresentationTypeName, 2420 2421 table_type: QpackTableType, 2422 index: u64, 2423 2424 is_post_base: Option<bool>, 2425 }, 2426 2427 LiteralHeaderFieldWithName { 2428 header_field_type: QpackHeaderBlockRepresentationTypeName, 2429 2430 preserve_literal: bool, 2431 table_type: QpackTableType, 2432 name_index: u64, 2433 2434 huffman_encoded_value: bool, 2435 value_length: u64, 2436 value: String, 2437 2438 is_post_base: Option<bool>, 2439 }, 2440 2441 LiteralHeaderFieldWithoutName { 2442 header_field_type: QpackHeaderBlockRepresentationTypeName, 2443 2444 preserve_literal: bool, 2445 table_type: QpackTableType, 2446 name_index: u64, 2447 2448 huffman_encoded_name: bool, 2449 name_length: u64, 2450 name: String, 2451 2452 huffman_encoded_value: bool, 2453 value_length: u64, 2454 value: String, 2455 2456 is_post_base: Option<bool>, 2457 }, 2458 } 2459 2460 pub struct HexSlice<'a>(&'a [u8]); 2461 2462 impl<'a> HexSlice<'a> { new<T>(data: &'a T) -> HexSlice<'a> where T: ?Sized + AsRef<[u8]> + 'a,2463 pub fn new<T>(data: &'a T) -> HexSlice<'a> 2464 where 2465 T: ?Sized + AsRef<[u8]> + 'a, 2466 { 2467 HexSlice(data.as_ref()) 2468 } 2469 maybe_string<T>(data: Option<&'a T>) -> Option<String> where T: ?Sized + AsRef<[u8]> + 'a,2470 pub fn maybe_string<T>(data: Option<&'a T>) -> Option<String> 2471 where 2472 T: ?Sized + AsRef<[u8]> + 'a, 2473 { 2474 match data { 2475 Some(d) => Some(format!("{}", HexSlice::new(d))), 2476 2477 None => None, 2478 } 2479 } 2480 } 2481 2482 impl<'a> std::fmt::Display for HexSlice<'a> { fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result2483 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 2484 for byte in self.0 { 2485 write!(f, "{:02x}", byte)?; 2486 } 2487 Ok(()) 2488 } 2489 } 2490 2491 #[doc(hidden)] 2492 pub mod testing { 2493 use super::*; 2494 make_pkt_hdr() -> PacketHeader2495 pub fn make_pkt_hdr() -> PacketHeader { 2496 let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8]; 2497 let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c]; 2498 2499 PacketHeader::new( 2500 0, 2501 Some(1251), 2502 Some(1224), 2503 Some(0xff00_0018), 2504 Some(&scid), 2505 Some(&dcid), 2506 ) 2507 } 2508 make_trace() -> Trace2509 pub fn make_trace() -> Trace { 2510 Trace::new( 2511 VantagePoint { 2512 name: None, 2513 ty: VantagePointType::Server, 2514 flow: None, 2515 }, 2516 Some("Quiche qlog trace".to_string()), 2517 Some("Quiche qlog trace description".to_string()), 2518 Some(Configuration { 2519 time_offset: Some("0".to_string()), 2520 time_units: Some(TimeUnits::Ms), 2521 original_uris: None, 2522 }), 2523 None, 2524 ) 2525 } 2526 } 2527 2528 #[cfg(test)] 2529 mod tests { 2530 use super::*; 2531 use testing::*; 2532 2533 #[test] packet_header()2534 fn packet_header() { 2535 let pkt_hdr = make_pkt_hdr(); 2536 2537 let log_string = r#"{ 2538 "packet_number": "0", 2539 "packet_size": 1251, 2540 "payload_length": 1224, 2541 "version": "ff000018", 2542 "scil": "8", 2543 "dcil": "8", 2544 "scid": "7e37e4dcc6682da8", 2545 "dcid": "36ce104eee50101c" 2546 }"#; 2547 2548 assert_eq!(serde_json::to_string_pretty(&pkt_hdr).unwrap(), log_string); 2549 } 2550 2551 #[test] packet_sent_event_no_frames()2552 fn packet_sent_event_no_frames() { 2553 let log_string = r#"{ 2554 "packet_type": "initial", 2555 "header": { 2556 "packet_number": "0", 2557 "packet_size": 1251, 2558 "payload_length": 1224, 2559 "version": "ff00001b", 2560 "scil": "8", 2561 "dcil": "8", 2562 "scid": "7e37e4dcc6682da8", 2563 "dcid": "36ce104eee50101c" 2564 } 2565 }"#; 2566 2567 let scid = [0x7e, 0x37, 0xe4, 0xdc, 0xc6, 0x68, 0x2d, 0xa8]; 2568 let dcid = [0x36, 0xce, 0x10, 0x4e, 0xee, 0x50, 0x10, 0x1c]; 2569 let pkt_hdr = PacketHeader::new( 2570 0, 2571 Some(1251), 2572 Some(1224), 2573 Some(0xff00001b), 2574 Some(&scid), 2575 Some(&dcid), 2576 ); 2577 2578 let pkt_sent_evt = EventData::PacketSent { 2579 raw_encrypted: None, 2580 raw_decrypted: None, 2581 packet_type: PacketType::Initial, 2582 header: pkt_hdr.clone(), 2583 frames: None, 2584 is_coalesced: None, 2585 }; 2586 2587 assert_eq!( 2588 serde_json::to_string_pretty(&pkt_sent_evt).unwrap(), 2589 log_string 2590 ); 2591 } 2592 2593 #[test] packet_sent_event_some_frames()2594 fn packet_sent_event_some_frames() { 2595 let log_string = r#"{ 2596 "packet_type": "initial", 2597 "header": { 2598 "packet_number": "0", 2599 "packet_size": 1251, 2600 "payload_length": 1224, 2601 "version": "ff000018", 2602 "scil": "8", 2603 "dcil": "8", 2604 "scid": "7e37e4dcc6682da8", 2605 "dcid": "36ce104eee50101c" 2606 }, 2607 "frames": [ 2608 { 2609 "frame_type": "padding" 2610 }, 2611 { 2612 "frame_type": "ping" 2613 }, 2614 { 2615 "frame_type": "stream", 2616 "stream_id": "0", 2617 "offset": "0", 2618 "length": "100", 2619 "fin": true 2620 } 2621 ] 2622 }"#; 2623 2624 let pkt_hdr = make_pkt_hdr(); 2625 2626 let mut frames = Vec::new(); 2627 frames.push(QuicFrame::padding()); 2628 2629 frames.push(QuicFrame::ping()); 2630 2631 frames.push(QuicFrame::stream( 2632 "0".to_string(), 2633 "0".to_string(), 2634 "100".to_string(), 2635 true, 2636 None, 2637 )); 2638 2639 let pkt_sent_evt = EventData::PacketSent { 2640 raw_encrypted: None, 2641 raw_decrypted: None, 2642 packet_type: PacketType::Initial, 2643 header: pkt_hdr.clone(), 2644 frames: Some(frames), 2645 is_coalesced: None, 2646 }; 2647 2648 assert_eq!( 2649 serde_json::to_string_pretty(&pkt_sent_evt).unwrap(), 2650 log_string 2651 ); 2652 } 2653 2654 #[test] trace_no_events()2655 fn trace_no_events() { 2656 let log_string = r#"{ 2657 "vantage_point": { 2658 "type": "server" 2659 }, 2660 "title": "Quiche qlog trace", 2661 "description": "Quiche qlog trace description", 2662 "configuration": { 2663 "time_units": "ms", 2664 "time_offset": "0" 2665 }, 2666 "event_fields": [ 2667 "relative_time", 2668 "category", 2669 "event", 2670 "data" 2671 ], 2672 "events": [] 2673 }"#; 2674 2675 let trace = make_trace(); 2676 2677 assert_eq!(serde_json::to_string_pretty(&trace).unwrap(), log_string); 2678 } 2679 2680 #[test] trace_single_transport_event()2681 fn trace_single_transport_event() { 2682 let log_string = r#"{ 2683 "vantage_point": { 2684 "type": "server" 2685 }, 2686 "title": "Quiche qlog trace", 2687 "description": "Quiche qlog trace description", 2688 "configuration": { 2689 "time_units": "ms", 2690 "time_offset": "0" 2691 }, 2692 "event_fields": [ 2693 "relative_time", 2694 "category", 2695 "event", 2696 "data" 2697 ], 2698 "events": [ 2699 [ 2700 "0", 2701 "transport", 2702 "packet_sent", 2703 { 2704 "packet_type": "initial", 2705 "header": { 2706 "packet_number": "0", 2707 "packet_size": 1251, 2708 "payload_length": 1224, 2709 "version": "ff000018", 2710 "scil": "8", 2711 "dcil": "8", 2712 "scid": "7e37e4dcc6682da8", 2713 "dcid": "36ce104eee50101c" 2714 }, 2715 "frames": [ 2716 { 2717 "frame_type": "stream", 2718 "stream_id": "0", 2719 "offset": "0", 2720 "length": "100", 2721 "fin": true 2722 } 2723 ] 2724 } 2725 ] 2726 ] 2727 }"#; 2728 2729 let mut trace = make_trace(); 2730 2731 let pkt_hdr = make_pkt_hdr(); 2732 2733 let frames = vec![QuicFrame::stream( 2734 "0".to_string(), 2735 "0".to_string(), 2736 "100".to_string(), 2737 true, 2738 None, 2739 )]; 2740 let event = event::Event::packet_sent_min( 2741 PacketType::Initial, 2742 pkt_hdr, 2743 Some(frames), 2744 ); 2745 2746 trace.push_event(std::time::Duration::new(0, 0), event); 2747 2748 assert_eq!(serde_json::to_string_pretty(&trace).unwrap(), log_string); 2749 } 2750 2751 #[test] test_event_validity()2752 fn test_event_validity() { 2753 // Test a single event in each category 2754 2755 let ev = event::Event::server_listening_min(443, 443); 2756 assert!(ev.is_valid()); 2757 2758 let ev = event::Event::transport_parameters_set_min(); 2759 assert!(ev.is_valid()); 2760 2761 let ev = event::Event::recovery_parameters_set_min(); 2762 assert!(ev.is_valid()); 2763 2764 let ev = event::Event::h3_parameters_set_min(); 2765 assert!(ev.is_valid()); 2766 2767 let ev = event::Event::qpack_state_updated_min(); 2768 assert!(ev.is_valid()); 2769 2770 let ev = event::Event { 2771 category: EventCategory::Error, 2772 ty: EventType::GenericEventType(GenericEventType::ConnectionError), 2773 data: EventData::ConnectionError { 2774 code: None, 2775 description: None, 2776 }, 2777 }; 2778 2779 assert!(ev.is_valid()); 2780 } 2781 2782 #[test] bogus_event_validity()2783 fn bogus_event_validity() { 2784 // Test a single event in each category 2785 2786 let mut ev = event::Event::server_listening_min(443, 443); 2787 ev.category = EventCategory::Simulation; 2788 assert!(!ev.is_valid()); 2789 2790 let mut ev = event::Event::transport_parameters_set_min(); 2791 ev.category = EventCategory::Simulation; 2792 assert!(!ev.is_valid()); 2793 2794 let mut ev = event::Event::recovery_parameters_set_min(); 2795 ev.category = EventCategory::Simulation; 2796 assert!(!ev.is_valid()); 2797 2798 let mut ev = event::Event::h3_parameters_set_min(); 2799 ev.category = EventCategory::Simulation; 2800 assert!(!ev.is_valid()); 2801 2802 let mut ev = event::Event::qpack_state_updated_min(); 2803 ev.category = EventCategory::Simulation; 2804 assert!(!ev.is_valid()); 2805 2806 let ev = event::Event { 2807 category: EventCategory::Error, 2808 ty: EventType::GenericEventType(GenericEventType::ConnectionError), 2809 data: EventData::FramesProcessed { frames: Vec::new() }, 2810 }; 2811 2812 assert!(!ev.is_valid()); 2813 } 2814 2815 #[test] serialization_states()2816 fn serialization_states() { 2817 let v: Vec<u8> = Vec::new(); 2818 let buff = std::io::Cursor::new(v); 2819 let writer = Box::new(buff); 2820 2821 let mut trace = make_trace(); 2822 let pkt_hdr = make_pkt_hdr(); 2823 2824 let frame1 = QuicFrame::stream( 2825 "40".to_string(), 2826 "40".to_string(), 2827 "400".to_string(), 2828 true, 2829 None, 2830 ); 2831 2832 let event1 = event::Event::packet_sent_min( 2833 PacketType::Handshake, 2834 pkt_hdr.clone(), 2835 Some(vec![frame1]), 2836 ); 2837 2838 trace.push_event(std::time::Duration::new(0, 0), event1); 2839 2840 let frame2 = QuicFrame::stream( 2841 "0".to_string(), 2842 "0".to_string(), 2843 "100".to_string(), 2844 true, 2845 None, 2846 ); 2847 2848 let frame3 = QuicFrame::stream( 2849 "0".to_string(), 2850 "0".to_string(), 2851 "100".to_string(), 2852 true, 2853 None, 2854 ); 2855 2856 let event2 = event::Event::packet_sent_min( 2857 PacketType::Initial, 2858 pkt_hdr.clone(), 2859 Some(Vec::new()), 2860 ); 2861 2862 let event3 = event::Event::packet_sent( 2863 PacketType::Initial, 2864 pkt_hdr, 2865 Some(Vec::new()), 2866 None, 2867 Some("encrypted_foo".to_string()), 2868 Some("decrypted_foo".to_string()), 2869 ); 2870 2871 let mut s = QlogStreamer::new( 2872 "version".to_string(), 2873 Some("title".to_string()), 2874 Some("description".to_string()), 2875 None, 2876 std::time::Instant::now(), 2877 trace, 2878 writer, 2879 ); 2880 2881 // Before the log is started all other operations should fail. 2882 assert!(match s.add_event(event2.clone()) { 2883 Err(Error::InvalidState) => true, 2884 _ => false, 2885 }); 2886 assert!(match s.add_frame(frame2.clone(), false) { 2887 Err(Error::InvalidState) => true, 2888 _ => false, 2889 }); 2890 assert!(match s.finish_frames() { 2891 Err(Error::InvalidState) => true, 2892 _ => false, 2893 }); 2894 assert!(match s.finish_log() { 2895 Err(Error::InvalidState) => true, 2896 _ => false, 2897 }); 2898 2899 // Once a log is started, can't write frames before an event. 2900 assert!(match s.start_log() { 2901 Ok(()) => true, 2902 _ => false, 2903 }); 2904 assert!(match s.add_frame(frame2.clone(), true) { 2905 Err(Error::InvalidState) => true, 2906 _ => false, 2907 }); 2908 assert!(match s.finish_frames() { 2909 Err(Error::InvalidState) => true, 2910 _ => false, 2911 }); 2912 2913 // Some events hold frames; can't write any more events until frame 2914 // writing is concluded. 2915 assert!(match s.add_event(event2.clone()) { 2916 Ok(true) => true, 2917 _ => false, 2918 }); 2919 assert!(match s.add_event(event2.clone()) { 2920 Err(Error::InvalidState) => true, 2921 _ => false, 2922 }); 2923 2924 // While writing frames, can't write events. 2925 assert!(match s.add_frame(frame2.clone(), false) { 2926 Ok(()) => true, 2927 _ => false, 2928 }); 2929 2930 assert!(match s.add_event(event2.clone()) { 2931 Err(Error::InvalidState) => true, 2932 _ => false, 2933 }); 2934 assert!(match s.finish_frames() { 2935 Ok(()) => true, 2936 _ => false, 2937 }); 2938 2939 // Adding an event that includes both frames and raw data should 2940 // be allowed. 2941 assert!(match s.add_event(event3.clone()) { 2942 Ok(true) => true, 2943 _ => false, 2944 }); 2945 assert!(match s.add_frame(frame3.clone(), false) { 2946 Ok(()) => true, 2947 _ => false, 2948 }); 2949 assert!(match s.finish_frames() { 2950 Ok(()) => true, 2951 _ => false, 2952 }); 2953 2954 assert!(match s.finish_log() { 2955 Ok(()) => true, 2956 _ => false, 2957 }); 2958 2959 let r = s.writer(); 2960 let w: &Box<std::io::Cursor<Vec<u8>>> = unsafe { std::mem::transmute(r) }; 2961 2962 let log_string = r#"{"qlog_version":"version","title":"title","description":"description","traces":[{"vantage_point":{"type":"server"},"title":"Quiche qlog trace","description":"Quiche qlog trace description","configuration":{"time_units":"ms","time_offset":"0"},"event_fields":["relative_time","category","event","data"],"events":[["0","transport","packet_sent",{"packet_type":"handshake","header":{"packet_number":"0","packet_size":1251,"payload_length":1224,"version":"ff000018","scil":"8","dcil":"8","scid":"7e37e4dcc6682da8","dcid":"36ce104eee50101c"},"frames":[{"frame_type":"stream","stream_id":"40","offset":"40","length":"400","fin":true}]}],["0","transport","packet_sent",{"packet_type":"initial","header":{"packet_number":"0","packet_size":1251,"payload_length":1224,"version":"ff000018","scil":"8","dcil":"8","scid":"7e37e4dcc6682da8","dcid":"36ce104eee50101c"},"frames":[{"frame_type":"stream","stream_id":"0","offset":"0","length":"100","fin":true}]}],["0","transport","packet_sent",{"packet_type":"initial","header":{"packet_number":"0","packet_size":1251,"payload_length":1224,"version":"ff000018","scil":"8","dcil":"8","scid":"7e37e4dcc6682da8","dcid":"36ce104eee50101c"},"raw_encrypted":"encrypted_foo","raw_decrypted":"decrypted_foo","frames":[{"frame_type":"stream","stream_id":"0","offset":"0","length":"100","fin":true}]}]]}]}"#; 2963 2964 let written_string = std::str::from_utf8(w.as_ref().get_ref()).unwrap(); 2965 2966 assert_eq!(log_string, written_string); 2967 } 2968 } 2969 2970 pub mod event; 2971