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