1use std::collections::BTreeMap;
28
29use qlog::events::h3::H3FrameCreated;
30use qlog::events::h3::H3Owner;
31use qlog::events::h3::H3StreamTypeSet;
32use qlog::events::h3::Http3Frame;
33use qlog::events::h3::HttpHeader;
34use qlog::events::quic::ErrorSpace;
35use qlog::events::quic::PacketSent;
36use qlog::events::quic::QuicFrame;
37use qlog::events::Event;
38use qlog::events::EventData;
39use qlog::events::ExData;
40use qlog::events::JsonEvent;
41use qlog::events::RawInfo;
42use quiche;
43use quiche::h3::frame::Frame;
44use quiche::h3::NameValue;
45
46use serde_json::json;
47
48use smallvec::smallvec;
49
50use crate::actions::h3::Action;
51use crate::actions::h3::WaitType;
52use crate::encode_header_block;
53use crate::encode_header_block_literal;
54use crate::fake_packet_sent;
55use crate::HTTP3_CONTROL_STREAM_TYPE_ID;
56use crate::HTTP3_PUSH_STREAM_TYPE_ID;
57use crate::QPACK_DECODER_STREAM_TYPE_ID;
58use crate::QPACK_ENCODER_STREAM_TYPE_ID;
59
60pub enum QlogEvent {
63 Event {
64 data: Box<EventData>,
65 ex_data: ExData,
66 },
67 JsonEvent(JsonEvent),
68}
69
70pub type QlogEvents = Vec<QlogEvent>;
72
73pub struct H3Actions(pub Vec<Action>);
75
76pub struct H3FrameCreatedEx {
78 frame_created: H3FrameCreated,
79 ex_data: ExData,
80}
81
82impl From<&Action> for QlogEvents {
83 fn from(action: &Action) -> Self {
84 match action {
85 Action::SendFrame {
86 stream_id,
87 fin_stream,
88 frame,
89 } => {
90 let frame_ev = EventData::H3FrameCreated(H3FrameCreated {
91 stream_id: *stream_id,
92 frame: frame.to_qlog(),
93 ..Default::default()
94 });
95
96 let mut ex = BTreeMap::new();
97
98 if *fin_stream {
99 ex.insert("fin_stream".to_string(), json!(true));
100 }
101
102 vec![QlogEvent::Event {
103 data: Box::new(frame_ev),
104 ex_data: ex,
105 }]
106 },
107
108 Action::SendHeadersFrame {
109 stream_id,
110 fin_stream,
111 headers,
112 literal_headers,
113 ..
114 } => {
115 let qlog_headers = headers
116 .iter()
117 .map(|h| qlog::events::h3::HttpHeader {
118 name: String::from_utf8_lossy(h.name()).into_owned(),
119 value: String::from_utf8_lossy(h.value()).into_owned(),
120 })
121 .collect();
122
123 let frame = Http3Frame::Headers {
124 headers: qlog_headers,
125 };
126
127 let frame_ev = EventData::H3FrameCreated(H3FrameCreated {
128 stream_id: *stream_id,
129 frame,
130 ..Default::default()
131 });
132
133 let mut ex = BTreeMap::new();
134
135 if *fin_stream {
136 ex.insert("fin_stream".to_string(), json!(true));
137 }
138
139 if *literal_headers {
140 ex.insert("literal_headers".to_string(), json!(true));
141 }
142
143 vec![QlogEvent::Event {
144 data: Box::new(frame_ev),
145 ex_data: ex,
146 }]
147 },
148
149 Action::OpenUniStream {
150 stream_id,
151 fin_stream,
152 stream_type,
153 } => {
154 let ty = match *stream_type {
155 HTTP3_CONTROL_STREAM_TYPE_ID =>
156 qlog::events::h3::H3StreamType::Control,
157 HTTP3_PUSH_STREAM_TYPE_ID =>
158 qlog::events::h3::H3StreamType::Push,
159 QPACK_ENCODER_STREAM_TYPE_ID =>
160 qlog::events::h3::H3StreamType::QpackEncode,
161 QPACK_DECODER_STREAM_TYPE_ID =>
162 qlog::events::h3::H3StreamType::QpackDecode,
163
164 _ => qlog::events::h3::H3StreamType::Unknown,
165 };
166 let ty_val =
167 if matches!(ty, qlog::events::h3::H3StreamType::Unknown) {
168 Some(*stream_type)
169 } else {
170 None
171 };
172
173 let stream_ev = EventData::H3StreamTypeSet(H3StreamTypeSet {
174 owner: Some(H3Owner::Local),
175 stream_id: *stream_id,
176 stream_type: ty,
177 stream_type_value: ty_val,
178 ..Default::default()
179 });
180 let mut ex = BTreeMap::new();
181
182 if *fin_stream {
183 ex.insert("fin_stream".to_string(), json!(true));
184 }
185
186 vec![QlogEvent::Event {
187 data: Box::new(stream_ev),
188 ex_data: ex,
189 }]
190 },
191
192 Action::StreamBytes {
193 stream_id,
194 fin_stream,
195 bytes,
196 } => {
197 let len = bytes.len() as u64;
198 let ev = fake_packet_sent(Some(smallvec![QuicFrame::Stream {
199 stream_id: *stream_id,
200 fin: Some(*fin_stream),
201 offset: 0,
203 length: len,
204 raw: Some(RawInfo {
205 length: Some(len),
206 payload_length: Some(len),
207 data: String::from_utf8(bytes.clone()).ok()
208 })
209 }]));
210
211 vec![QlogEvent::Event {
212 data: Box::new(ev),
213 ex_data: BTreeMap::new(),
214 }]
215 },
216
217 Action::ResetStream {
218 stream_id,
219 error_code,
220 } => {
221 let ev =
222 fake_packet_sent(Some(smallvec![QuicFrame::ResetStream {
223 stream_id: *stream_id,
224 error_code: *error_code,
225 final_size: 0,
226 length: None,
227 payload_length: None
228 }]));
229 vec![QlogEvent::Event {
230 data: Box::new(ev),
231 ex_data: BTreeMap::new(),
232 }]
233 },
234
235 Action::StopSending {
236 stream_id,
237 error_code,
238 } => {
239 let ev =
240 fake_packet_sent(Some(smallvec![QuicFrame::StopSending {
241 stream_id: *stream_id,
242 error_code: *error_code,
243 length: None,
244 payload_length: None
245 }]));
246 vec![QlogEvent::Event {
247 data: Box::new(ev),
248 ex_data: BTreeMap::new(),
249 }]
250 },
251
252 Action::Wait { wait_type } => {
253 let name = "h3i:wait".into();
254
255 let data = match wait_type {
256 d @ WaitType::WaitDuration(_) =>
257 serde_json::to_value(d).unwrap(),
258 WaitType::StreamEvent(event) =>
259 serde_json::to_value(event).unwrap(),
260 };
261
262 vec![QlogEvent::JsonEvent(qlog::events::JsonEvent {
263 time: 0.0,
264 importance: qlog::events::EventImportance::Core,
265 name,
266 data,
267 })]
268 },
269
270 Action::ConnectionClose { error } => {
271 let error_space = if error.is_app {
272 ErrorSpace::ApplicationError
273 } else {
274 ErrorSpace::TransportError
275 };
276
277 let reason = if error.reason.is_empty() {
278 None
279 } else {
280 Some(String::from_utf8(error.reason.clone()).unwrap())
281 };
282
283 let ev = fake_packet_sent(Some(smallvec![
284 QuicFrame::ConnectionClose {
285 error_space: Some(error_space),
286 error_code: Some(error.error_code),
287 error_code_value: None,
289 reason,
290 trigger_frame_type: None
291 }
292 ]));
293
294 vec![QlogEvent::Event {
295 data: Box::new(ev),
296 ex_data: BTreeMap::new(),
297 }]
298 },
299
300 Action::FlushPackets => {
301 vec![]
302 },
303 }
304 }
305}
306
307pub fn actions_from_qlog(event: Event, host_override: Option<&str>) -> H3Actions {
308 let mut actions = vec![];
309 match &event.data {
310 EventData::PacketSent(ps) => {
311 let packet_actions: H3Actions = ps.into();
312 actions.extend(packet_actions.0);
313 },
314
315 EventData::H3FrameCreated(fc) => {
316 let mut frame_created = H3FrameCreatedEx {
317 frame_created: fc.clone(),
318 ex_data: event.ex_data.clone(),
319 };
320
321 if let Some(host) = host_override {
324 frame_created
325 .ex_data
326 .insert("host_override".into(), host.into());
327 }
328
329 actions.push(frame_created.into());
330 },
331
332 EventData::H3StreamTypeSet(st) => {
333 let stream_actions = from_qlog_stream_type_set(st, &event.ex_data);
334 actions.extend(stream_actions);
335 },
336
337 _ => (),
338 }
339
340 H3Actions(actions)
341}
342
343impl From<JsonEvent> for H3Actions {
344 fn from(event: JsonEvent) -> Self {
345 let mut actions = vec![];
346 match event.name.as_ref() {
347 "h3i:wait" => {
348 let wait_type =
349 serde_json::from_value::<WaitType>(event.clone().data);
350
351 if let Ok(wt) = wait_type {
352 actions.push(Action::Wait { wait_type: wt });
353 } else {
354 log::debug!("couldn't create action from event: {:?}", event);
355 }
356 },
357 _ => unimplemented!(),
358 }
359
360 Self(actions)
361 }
362}
363
364impl From<&PacketSent> for H3Actions {
365 fn from(ps: &PacketSent) -> Self {
366 let mut actions = vec![];
367 if let Some(frames) = &ps.frames {
368 for frame in frames {
369 match &frame {
370 QuicFrame::ResetStream {
372 stream_id,
373 error_code,
374 ..
375 } => actions.push(Action::ResetStream {
376 stream_id: *stream_id,
377 error_code: *error_code,
378 }),
379
380 QuicFrame::StopSending {
381 stream_id,
382 error_code,
383 ..
384 } => actions.push(Action::StopSending {
385 stream_id: *stream_id,
386 error_code: *error_code,
387 }),
388
389 QuicFrame::ConnectionClose {
390 error_space,
391 error_code,
392 reason,
393 ..
394 } => {
395 let is_app = matches!(
396 error_space.as_ref().expect(
397 "invalid CC frame in qlog input, no error space"
398 ),
399 ErrorSpace::ApplicationError
400 );
401
402 actions.push(Action::ConnectionClose {
403 error: quiche::ConnectionError {
404 is_app,
405 error_code: error_code.expect("invalid CC frame in qlog input, no error code"),
408 reason: reason
409 .as_ref()
410 .map(|s| s.as_bytes().to_vec())
411 .unwrap_or_default(),
412 },
413 })
414 },
415
416 QuicFrame::Stream { stream_id, fin, .. } => {
417 let fin = fin.unwrap_or_default();
418
419 if fin {
420 actions.push(Action::StreamBytes {
421 stream_id: *stream_id,
422 fin_stream: true,
423 bytes: vec![],
424 });
425 }
426 },
427
428 _ => (),
429 }
430 }
431 }
432
433 Self(actions)
434 }
435}
436
437fn map_header(
438 hdr: &HttpHeader, host_override: Option<&str>,
439) -> quiche::h3::Header {
440 if hdr.name.eq_ignore_ascii_case(":authority") ||
441 hdr.name.eq_ignore_ascii_case("host")
442 {
443 if let Some(host) = host_override {
444 return quiche::h3::Header::new(hdr.name.as_bytes(), host.as_bytes());
445 }
446 }
447
448 quiche::h3::Header::new(hdr.name.as_bytes(), hdr.value.as_bytes())
449}
450
451impl From<H3FrameCreatedEx> for Action {
452 fn from(value: H3FrameCreatedEx) -> Self {
453 let stream_id = value.frame_created.stream_id;
454 let fin_stream = value
455 .ex_data
456 .get("fin_stream")
457 .unwrap_or(&serde_json::Value::Null)
458 .as_bool()
459 .unwrap_or_default();
460 let host_override = value
461 .ex_data
462 .get("host_override")
463 .unwrap_or(&serde_json::Value::Null)
464 .as_str();
465
466 let ret = match &value.frame_created.frame {
467 Http3Frame::Settings { settings } => {
468 let mut raw_settings = vec![];
469 let mut additional_settings = vec![];
470 for s in settings {
473 match s.name.as_str() {
474 "MAX_FIELD_SECTION_SIZE" =>
475 raw_settings.push((0x6, s.value)),
476 "QPACK_MAX_TABLE_CAPACITY" =>
477 raw_settings.push((0x1, s.value)),
478 "QPACK_BLOCKED_STREAMS" =>
479 raw_settings.push((0x7, s.value)),
480 "SETTINGS_ENABLE_CONNECT_PROTOCOL" =>
481 raw_settings.push((0x8, s.value)),
482 "H3_DATAGRAM" => raw_settings.push((0x33, s.value)),
483
484 _ =>
485 if let Ok(ty) = s.name.parse::<u64>() {
486 raw_settings.push((ty, s.value));
487 additional_settings.push((ty, s.value));
488 },
489 }
490 }
491
492 Action::SendFrame {
493 stream_id,
494 fin_stream,
495 frame: Frame::Settings {
496 max_field_section_size: None,
497 qpack_max_table_capacity: None,
498 qpack_blocked_streams: None,
499 connect_protocol_enabled: None,
500 h3_datagram: None,
501 grease: None,
502 raw: Some(raw_settings),
503 additional_settings: Some(additional_settings),
504 },
505 }
506 },
507
508 Http3Frame::Headers { headers } => {
509 let hdrs: Vec<quiche::h3::Header> = headers
510 .iter()
511 .map(|h| map_header(h, host_override))
512 .collect();
513
514 let literal_headers = value
515 .ex_data
516 .get("literal_headers")
517 .unwrap_or(&serde_json::Value::Null)
518 .as_bool()
519 .unwrap_or_default();
520
521 let header_block = if literal_headers {
522 encode_header_block_literal(&hdrs).unwrap()
523 } else {
524 encode_header_block(&hdrs).unwrap()
525 };
526
527 Action::SendHeadersFrame {
528 stream_id,
529 fin_stream,
530 literal_headers,
531 headers: hdrs,
532 frame: Frame::Headers { header_block },
533 }
534 },
535
536 Http3Frame::Data { raw } => {
537 let mut payload = vec![];
538 if let Some(r) = raw {
539 payload = r
540 .data
541 .clone()
542 .unwrap_or("".to_string())
543 .as_bytes()
544 .to_vec();
545 }
546
547 Action::SendFrame {
548 stream_id,
549 fin_stream,
550 frame: Frame::Data { payload },
551 }
552 },
553
554 Http3Frame::Goaway { id } => Action::SendFrame {
555 stream_id,
556 fin_stream,
557 frame: Frame::GoAway { id: *id },
558 },
559
560 _ => unimplemented!(),
561 };
562
563 ret
564 }
565}
566
567fn from_qlog_stream_type_set(
568 st: &H3StreamTypeSet, ex_data: &ExData,
569) -> Vec<Action> {
570 let mut actions = vec![];
571 let fin_stream = parse_ex_data(ex_data);
572 let stream_type = match st.stream_type {
573 qlog::events::h3::H3StreamType::Control => Some(0x0),
574 qlog::events::h3::H3StreamType::Push => Some(0x1),
575 qlog::events::h3::H3StreamType::QpackEncode => Some(0x2),
576 qlog::events::h3::H3StreamType::QpackDecode => Some(0x3),
577 qlog::events::h3::H3StreamType::Reserved |
578 qlog::events::h3::H3StreamType::Unknown => st.stream_type_value,
579 _ => None,
580 };
581
582 if let Some(ty) = stream_type {
583 actions.push(Action::OpenUniStream {
584 stream_id: st.stream_id,
585 fin_stream,
586 stream_type: ty,
587 })
588 }
589
590 actions
591}
592
593fn parse_ex_data(ex_data: &ExData) -> bool {
594 ex_data
595 .get("fin_stream")
596 .unwrap_or(&serde_json::Value::Null)
597 .as_bool()
598 .unwrap_or_default()
599}
600
601#[cfg(test)]
602mod tests {
603 use crate::actions::h3::StreamEvent;
604 use crate::actions::h3::StreamEventType;
605 use crate::encode_header_block_literal;
606 use std::time::Duration;
607
608 use super::*;
609 use quiche::h3::Header;
610 use serde_json;
611
612 const NOW: f32 = 123.0;
613 const H3I_WAIT: &str = "h3i:wait";
614
615 #[test]
616 fn ser_duration_wait() {
617 let ev = JsonEvent {
618 time: NOW,
619 importance: qlog::events::EventImportance::Core,
620 name: H3I_WAIT.to_string(),
621 data: serde_json::to_value(WaitType::WaitDuration(
622 Duration::from_millis(12345),
623 ))
624 .unwrap(),
625 };
626 let serialized = serde_json::to_string(&ev);
627
628 let expected =
629 r#"{"time":123.0,"name":"h3i:wait","data":{"duration":12345.0}}"#;
630 assert_eq!(&serialized.unwrap(), expected);
631 }
632
633 #[test]
634 fn deser_duration_wait() {
635 let ev = JsonEvent {
636 time: NOW,
637 importance: qlog::events::EventImportance::Core,
638 name: H3I_WAIT.to_string(),
639 data: serde_json::to_value(WaitType::WaitDuration(
640 Duration::from_millis(12345),
641 ))
642 .unwrap(),
643 };
644
645 let expected =
646 r#"{"time":123.0,"name":"h3i:wait","data":{"duration":12345.0}}"#;
647 let deser = serde_json::from_str::<JsonEvent>(expected).unwrap();
648 assert_eq!(deser.data, ev.data);
649 }
650
651 #[test]
652 fn ser_stream_wait() {
653 let expected = r#"{"time":123.0,"name":"h3i:wait","data":{"stream_id":0,"type":"data"}}"#;
654 let ev = JsonEvent {
655 time: NOW,
656 importance: qlog::events::EventImportance::Core,
657 name: H3I_WAIT.to_string(),
658 data: serde_json::to_value(StreamEvent {
659 stream_id: 0,
660 event_type: StreamEventType::Data,
661 })
662 .unwrap(),
663 };
664
665 let serialized = serde_json::to_string(&ev);
666 assert_eq!(&serialized.unwrap(), expected);
667 }
668
669 #[test]
670 fn deser_stream_wait() {
671 let ev = JsonEvent {
672 time: NOW,
673 importance: qlog::events::EventImportance::Core,
674 name: H3I_WAIT.to_string(),
675 data: serde_json::to_value(StreamEvent {
676 stream_id: 0,
677 event_type: StreamEventType::Data,
678 })
679 .unwrap(),
680 };
681
682 let expected = r#"{"time":123.0,"name":"h3i:wait","data":{"stream_id":0,"type":"data"}}"#;
683 let deser = serde_json::from_str::<JsonEvent>(expected).unwrap();
684 assert_eq!(deser.data, ev.data);
685 }
686
687 #[test]
688 fn deser_http_headers_to_action() {
689 let serialized = r#"{"time":0.074725,"name":"http:frame_created","data":{"stream_id":0,"frame":{"frame_type":"headers","headers":[{"name":":method","value":"GET"},{"name":":authority","value":"example.net"},{"name":":path","value":"/"},{"name":":scheme","value":"https"}]}},"fin_stream":true}"#;
690 let deserialized = serde_json::from_str::<Event>(serialized).unwrap();
691 let actions = actions_from_qlog(deserialized, None);
692 assert!(actions.0.len() == 1);
693
694 let headers = vec![
695 Header::new(b":method", b"GET"),
696 Header::new(b":authority", b"example.net"),
697 Header::new(b":path", b"/"),
698 Header::new(b":scheme", b"https"),
699 ];
700 let header_block = encode_header_block(&headers).unwrap();
701 let frame = Frame::Headers { header_block };
702 let expected = Action::SendHeadersFrame {
703 stream_id: 0,
704 fin_stream: true,
705 literal_headers: false,
706 headers,
707 frame,
708 };
709
710 assert_eq!(actions.0[0], expected);
711 }
712
713 #[test]
714 fn deser_http_headers_host_overrid_to_action() {
715 let serialized = r#"{"time":0.074725,"name":"http:frame_created","data":{"stream_id":0,"frame":{"frame_type":"headers","headers":[{"name":":method","value":"GET"},{"name":":authority","value":"bla.com"},{"name":":path","value":"/"},{"name":":scheme","value":"https"}]}},"fin_stream":true}"#;
716 let deserialized = serde_json::from_str::<Event>(serialized).unwrap();
717 let actions = actions_from_qlog(deserialized, Some("example.org"));
718 assert!(actions.0.len() == 1);
719
720 let headers = vec![
721 Header::new(b":method", b"GET"),
722 Header::new(b":authority", b"example.org"),
723 Header::new(b":path", b"/"),
724 Header::new(b":scheme", b"https"),
725 ];
726 let header_block = encode_header_block(&headers).unwrap();
727 let frame = Frame::Headers { header_block };
728 let expected = Action::SendHeadersFrame {
729 stream_id: 0,
730 fin_stream: true,
731 literal_headers: false,
732 headers,
733 frame,
734 };
735
736 assert_eq!(actions.0[0], expected);
737 }
738
739 #[test]
740 fn deser_http_headers_literal_to_action() {
741 let serialized = r#"{"time":0.074725,"name":"http:frame_created","data":{"stream_id":0,"frame":{"frame_type":"headers","headers":[{"name":":method","value":"GET"},{"name":":authority","value":"bla.com"},{"name":":path","value":"/"},{"name":":scheme","value":"https"},{"name":"Foo","value":"bar"}]}},"fin_stream":true,"literal_headers":true}"#;
742 let deserialized = serde_json::from_str::<Event>(serialized).unwrap();
743 let actions = actions_from_qlog(deserialized, None);
744 assert!(actions.0.len() == 1);
745
746 let headers = vec![
747 Header::new(b":method", b"GET"),
748 Header::new(b":authority", b"bla.com"),
749 Header::new(b":path", b"/"),
750 Header::new(b":scheme", b"https"),
751 Header::new(b"Foo", b"bar"),
752 ];
753 let header_block = encode_header_block_literal(&headers).unwrap();
754 let frame = Frame::Headers { header_block };
755 let expected = Action::SendHeadersFrame {
756 stream_id: 0,
757 fin_stream: true,
758 literal_headers: true,
759 headers,
760 frame,
761 };
762
763 assert_eq!(actions.0[0], expected);
764 }
765}