1use std::cmp;
30use std::convert::TryFrom;
31use std::error::Error;
32use std::fmt::Debug;
33use std::sync::Arc;
34
35use crate::quiche;
36use multimap::MultiMap;
37
38use quiche::h3::frame::Frame as QFrame;
39use quiche::h3::Header;
40use quiche::h3::NameValue;
41use serde::ser::SerializeStruct;
42use serde::ser::Serializer;
43use serde::Serialize;
44
45use crate::client::connection_summary::MAX_SERIALIZED_BUFFER_LEN;
46use crate::encode_header_block;
47
48pub type BoxError = Box<dyn Error + Send + Sync + 'static>;
49
50#[derive(Debug, Eq, PartialEq, Clone)]
53pub enum H3iFrame {
54 QuicheH3(QFrame),
56 Headers(EnrichedHeaders),
58 ResetStream(ResetStream),
60}
61
62impl H3iFrame {
63 pub fn to_enriched_headers(&self) -> Option<EnrichedHeaders> {
67 if let H3iFrame::Headers(header) = self {
68 Some(header.clone())
69 } else {
70 None
71 }
72 }
73}
74
75impl Serialize for H3iFrame {
76 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
77 where
78 S: Serializer,
79 {
80 match self {
81 H3iFrame::QuicheH3(frame) => {
82 let mut state = s.serialize_struct("frame", 1)?;
83 let name = frame_name(frame);
84 state.serialize_field(name, &SerializableQFrame(frame))?;
85 state.end()
86 },
87 H3iFrame::Headers(headers) => {
88 let mut state = s.serialize_struct("enriched_headers", 1)?;
89 state.serialize_field("enriched_headers", headers)?;
90 state.end()
91 },
92 H3iFrame::ResetStream(reset) => {
93 let mut state = s.serialize_struct("reset_stream", 1)?;
94 state.serialize_field("reset_stream", reset)?;
95 state.end()
96 },
97 }
98 }
99}
100
101impl From<QFrame> for H3iFrame {
102 fn from(value: QFrame) -> Self {
103 Self::QuicheH3(value)
104 }
105}
106
107impl From<Vec<Header>> for H3iFrame {
108 fn from(value: Vec<Header>) -> Self {
109 Self::Headers(EnrichedHeaders::from(value))
110 }
111}
112
113pub type HeaderMap = MultiMap<Vec<u8>, Vec<u8>>;
114
115#[derive(Clone, PartialEq, Eq)]
117pub struct EnrichedHeaders {
118 header_block: Vec<u8>,
119 headers: Vec<Header>,
120 header_map: HeaderMap,
122}
123
124pub struct SerializableHeader<'a>(&'a Header);
126
127impl Serialize for SerializableHeader<'_> {
128 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
129 where
130 S: Serializer,
131 {
132 let mut state = s.serialize_struct("header", 2)?;
133 state.serialize_field("name", &String::from_utf8_lossy(self.0.name()))?;
134 state
135 .serialize_field("value", &String::from_utf8_lossy(self.0.value()))?;
136 state.end()
137 }
138}
139
140impl EnrichedHeaders {
141 pub fn headers(&self) -> &[Header] {
156 &self.headers
157 }
158
159 pub fn header_map(&self) -> &HeaderMap {
194 &self.header_map
195 }
196
197 pub fn status_code(&self) -> Option<&Vec<u8>> {
211 self.header_map.get(b":status".as_slice())
212 }
213}
214
215impl Serialize for EnrichedHeaders {
216 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
217 where
218 S: Serializer,
219 {
220 let mut state = s.serialize_struct("enriched_headers", 2)?;
221 state.serialize_field("header_block_len", &self.header_block.len())?;
222 let x: Vec<SerializableHeader> =
223 self.headers.iter().map(SerializableHeader).collect();
224 state.serialize_field("headers", &x)?;
225 state.end()
226 }
227}
228
229impl From<Vec<Header>> for EnrichedHeaders {
230 fn from(headers: Vec<Header>) -> Self {
231 let header_block = encode_header_block(&headers).unwrap();
232
233 let mut header_map: HeaderMap = MultiMap::with_capacity(headers.len());
234 for header in headers.iter() {
235 header_map.insert(header.name().to_vec(), header.value().to_vec());
236 }
237
238 Self {
239 header_block,
240 headers,
241 header_map,
242 }
243 }
244}
245
246impl TryFrom<QFrame> for EnrichedHeaders {
247 type Error = BoxError;
248
249 fn try_from(value: QFrame) -> Result<Self, Self::Error> {
250 match value {
251 QFrame::Headers { header_block } => {
252 let mut qpack_decoder = quiche::h3::qpack::Decoder::new();
253 let headers = qpack_decoder.decode(&header_block, u64::MAX)?;
254
255 Ok(EnrichedHeaders::from(headers))
256 },
257 _ => Err("Cannot convert non-Headers frame into HeadersFrame".into()),
258 }
259 }
260}
261
262impl Debug for EnrichedHeaders {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 write!(f, "{:?}", self.headers)
265 }
266}
267
268#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
273pub struct ResetStream {
274 pub stream_id: u64,
276 pub error_code: u64,
278}
279
280fn frame_name(frame: &QFrame) -> &'static str {
281 match frame {
282 QFrame::Data { .. } => "DATA",
283 QFrame::Headers { .. } => "HEADERS",
284 QFrame::CancelPush { .. } => "CANCEL_PUSH",
285 QFrame::Settings { .. } => "SETTINGS",
286 QFrame::PushPromise { .. } => "PUSH_PROMISE",
287 QFrame::GoAway { .. } => "GO_AWAY",
288 QFrame::MaxPushId { .. } => "MAX_PUSH_ID",
289 QFrame::PriorityUpdateRequest { .. } => "PRIORITY_UPDATE(REQUEST)",
290 QFrame::PriorityUpdatePush { .. } => "PRIORITY_UPDATE(PUSH)",
291 QFrame::Unknown { .. } => "UNKNOWN",
292 }
293}
294
295pub struct SerializableQFrame<'a>(&'a QFrame);
297
298impl Serialize for SerializableQFrame<'_> {
299 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
300 where
301 S: Serializer,
302 {
303 let name = frame_name(self.0);
304 match self.0 {
305 QFrame::Data { payload } => {
306 let mut state = s.serialize_struct(name, 2)?;
307 let max = cmp::min(payload.len(), MAX_SERIALIZED_BUFFER_LEN);
308 state.serialize_field("payload_len", &payload.len())?;
309 state.serialize_field(
310 "payload",
311 &qlog::HexSlice::maybe_string(Some(&payload[..max])),
312 )?;
313 state.end()
314 },
315
316 QFrame::Headers { header_block } => {
317 let mut state = s.serialize_struct(name, 1)?;
318 state.serialize_field("header_block_len", &header_block.len())?;
319 state.end()
320 },
321
322 QFrame::CancelPush { push_id } => {
323 let mut state = s.serialize_struct(name, 1)?;
324 state.serialize_field("push_id", &push_id)?;
325 state.end()
326 },
327
328 QFrame::Settings {
329 max_field_section_size,
330 qpack_max_table_capacity,
331 qpack_blocked_streams,
332 connect_protocol_enabled,
333 h3_datagram,
334 grease: _,
335 additional_settings,
336 raw: _,
337 } => {
338 let mut state = s.serialize_struct(name, 6)?;
339 state.serialize_field(
340 "max_field_section_size",
341 &max_field_section_size,
342 )?;
343 state.serialize_field(
344 "qpack_max_table_capacity",
345 &qpack_max_table_capacity,
346 )?;
347 state.serialize_field(
348 "qpack_blocked_streams",
349 &qpack_blocked_streams,
350 )?;
351 state.serialize_field(
352 "connect_protocol_enabled",
353 &connect_protocol_enabled,
354 )?;
355 state.serialize_field("h3_datagram", &h3_datagram)?;
356 state.serialize_field(
357 "additional_settings",
358 &additional_settings,
359 )?;
360 state.end()
361 },
362
363 QFrame::PushPromise {
364 push_id,
365 header_block,
366 } => {
367 let mut state = s.serialize_struct(name, 2)?;
368 state.serialize_field("push_id", &push_id)?;
369 state.serialize_field("header_block_len", &header_block.len())?;
370 state.end()
371 },
372
373 QFrame::GoAway { id } => {
374 let mut state = s.serialize_struct(name, 1)?;
375 state.serialize_field("id", &id)?;
376 state.end()
377 },
378
379 QFrame::MaxPushId { push_id } => {
380 let mut state = s.serialize_struct(name, 1)?;
381 state.serialize_field("push_id", &push_id)?;
382 state.end()
383 },
384
385 QFrame::PriorityUpdateRequest {
386 prioritized_element_id,
387 priority_field_value,
388 } => {
389 let mut state = s.serialize_struct(name, 3)?;
390 state.serialize_field(
391 "prioritized_element_id",
392 &prioritized_element_id,
393 )?;
394
395 let max = cmp::min(
396 priority_field_value.len(),
397 MAX_SERIALIZED_BUFFER_LEN,
398 );
399 state.serialize_field(
400 "priority_field_value_len",
401 &priority_field_value.len(),
402 )?;
403 state.serialize_field(
404 "priority_field_value",
405 &String::from_utf8_lossy(&priority_field_value[..max]),
406 )?;
407 state.end()
408 },
409
410 QFrame::PriorityUpdatePush {
411 prioritized_element_id,
412 priority_field_value,
413 } => {
414 let mut state = s.serialize_struct(name, 3)?;
415 state.serialize_field(
416 "prioritized_element_id",
417 &prioritized_element_id,
418 )?;
419 let max = cmp::min(
420 priority_field_value.len(),
421 MAX_SERIALIZED_BUFFER_LEN,
422 );
423 state.serialize_field(
424 "priority_field_value_len",
425 &priority_field_value.len(),
426 )?;
427 state.serialize_field(
428 "priority_field_value",
429 &String::from_utf8_lossy(&priority_field_value[..max]),
430 )?;
431 state.end()
432 },
433
434 QFrame::Unknown { raw_type, payload } => {
435 let mut state = s.serialize_struct(name, 3)?;
436 state.serialize_field("raw_type", &raw_type)?;
437 let max = cmp::min(payload.len(), MAX_SERIALIZED_BUFFER_LEN);
438 state.serialize_field("payload_len", &payload.len())?;
439 state.serialize_field(
440 "payload",
441 &qlog::HexSlice::maybe_string(Some(&payload[..max])),
442 )?;
443 state.end()
444 },
445 }
446 }
447}
448
449type CustomEquivalenceHandler =
450 Box<dyn for<'f> Fn(&'f H3iFrame) -> bool + Send + Sync + 'static>;
451
452#[derive(Clone)]
453enum Comparator {
454 Frame(H3iFrame),
455 Fn(Arc<CustomEquivalenceHandler>),
465}
466
467impl Serialize for Comparator {
468 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
469 where
470 S: Serializer,
471 {
472 match self {
473 Self::Fn(_) => serializer.serialize_str("<comparator_fn>"),
474 Self::Frame(f) => {
475 let mut frame_ser = serializer.serialize_struct("frame", 1)?;
476 frame_ser.serialize_field("frame", f)?;
477 frame_ser.end()
478 },
479 }
480 }
481}
482
483#[derive(Serialize, Clone)]
488pub struct CloseTriggerFrame {
489 stream_id: u64,
490 comparator: Comparator,
491}
492
493impl CloseTriggerFrame {
494 pub fn new(stream_id: u64, frame: impl Into<H3iFrame>) -> Self {
513 Self {
514 stream_id,
515 comparator: Comparator::Frame(frame.into()),
516 }
517 }
518
519 pub fn new_with_comparator<F>(stream_id: u64, comparator_fn: F) -> Self
526 where
527 F: Fn(&H3iFrame) -> bool + Send + Sync + 'static,
528 {
529 Self {
530 stream_id,
531 comparator: Comparator::Fn(Arc::new(Box::new(comparator_fn))),
532 }
533 }
534
535 pub(crate) fn stream_id(&self) -> u64 {
536 self.stream_id
537 }
538
539 pub(crate) fn is_equivalent(&self, other: &H3iFrame) -> bool {
540 let frame = match &self.comparator {
541 Comparator::Fn(compare) => return compare(other),
542 Comparator::Frame(frame) => frame,
543 };
544
545 match frame {
546 H3iFrame::Headers(me) => {
547 let H3iFrame::Headers(other) = other else {
548 return false;
549 };
550
551 me.headers().iter().all(|m| other.headers().contains(m))
558 },
559 H3iFrame::QuicheH3(me) => match other {
560 H3iFrame::QuicheH3(other) => me == other,
561 _ => false,
562 },
563 H3iFrame::ResetStream(me) => match other {
564 H3iFrame::ResetStream(rs) => me == rs,
565 _ => false,
566 },
567 }
568 }
569}
570
571impl Debug for CloseTriggerFrame {
572 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
573 let repr = match &self.comparator {
574 Comparator::Frame(frame) => format!("{frame:?}"),
575 Comparator::Fn(_) => "closure".to_string(),
576 };
577
578 write!(
579 f,
580 "CloseTriggerFrame {{ stream_id: {}, comparator: {repr} }}",
581 self.stream_id
582 )
583 }
584}
585
586impl PartialEq for CloseTriggerFrame {
587 fn eq(&self, other: &Self) -> bool {
588 match (&self.comparator, &other.comparator) {
589 (Comparator::Frame(this_frame), Comparator::Frame(other_frame)) =>
590 self.stream_id == other.stream_id && this_frame == other_frame,
591 _ => false,
592 }
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599 use quiche::h3::frame::Frame;
600
601 #[test]
602 fn enriched_headers_decode_error() {
603 let frame = QFrame::Headers {
606 header_block: vec![0, 0, 0x80],
607 };
608
609 assert!(EnrichedHeaders::try_from(frame).is_err());
610 }
611
612 #[test]
613 fn test_header_equivalence() {
614 let this = CloseTriggerFrame::new(0, vec![
615 Header::new(b"hello", b"world"),
616 Header::new(b"go", b"jets"),
617 ]);
618 let other: H3iFrame = vec![
619 Header::new(b"hello", b"world"),
620 Header::new(b"go", b"jets"),
621 Header::new(b"go", b"devils"),
622 ]
623 .into();
624
625 assert!(this.is_equivalent(&other));
626 }
627
628 #[test]
629 fn test_header_non_equivalence() {
630 let this = CloseTriggerFrame::new(0, vec![
631 Header::new(b"hello", b"world"),
632 Header::new(b"go", b"jets"),
633 Header::new(b"go", b"devils"),
634 ]);
635 let other: H3iFrame =
636 vec![Header::new(b"hello", b"world"), Header::new(b"go", b"jets")]
637 .into();
638
639 assert!(!this.is_equivalent(&other));
642 }
643
644 #[test]
645 fn test_rst_stream_equivalence() {
646 let mut rs = ResetStream {
647 stream_id: 0,
648 error_code: 57,
649 };
650
651 let this = CloseTriggerFrame::new(0, H3iFrame::ResetStream(rs.clone()));
652 let incoming = H3iFrame::ResetStream(rs.clone());
653 assert!(this.is_equivalent(&incoming));
654
655 rs.stream_id = 57;
656 let incoming = H3iFrame::ResetStream(rs);
657 assert!(!this.is_equivalent(&incoming));
658 }
659
660 #[test]
661 fn test_frame_equivalence() {
662 let mut d = Frame::Data {
663 payload: b"57".to_vec(),
664 };
665
666 let this = CloseTriggerFrame::new(0, H3iFrame::QuicheH3(d.clone()));
667 let incoming = H3iFrame::QuicheH3(d.clone());
668 assert!(this.is_equivalent(&incoming));
669
670 d = Frame::Data {
671 payload: b"go jets".to_vec(),
672 };
673 let incoming = H3iFrame::QuicheH3(d.clone());
674 assert!(!this.is_equivalent(&incoming));
675 }
676
677 #[test]
678 fn test_comparator() {
679 let this = CloseTriggerFrame::new_with_comparator(0, |frame| {
680 if let H3iFrame::Headers(..) = frame {
681 frame
682 .to_enriched_headers()
683 .unwrap()
684 .header_map()
685 .get(&b"cookie".to_vec())
686 .is_some_and(|v| {
687 std::str::from_utf8(v)
688 .map(|s| s.to_lowercase())
689 .unwrap()
690 .contains("cookie")
691 })
692 } else {
693 false
694 }
695 });
696
697 let incoming: H3iFrame =
698 vec![Header::new(b"cookie", b"SomeRandomCookie1234")].into();
699
700 assert!(this.is_equivalent(&incoming));
701 }
702}