Skip to main content

wtransport_proto/
headers.rs

1use crate::error::ErrorCode;
2use crate::frame::Frame;
3use crate::frame::FrameKind;
4use crate::qpack::Decoder;
5use crate::qpack::Encoder;
6use std::borrow::Cow;
7use std::collections::HashMap;
8
9/// HTTP3 headers from the request or response.
10#[derive(Debug)]
11pub struct Headers(HashMap<String, String>);
12
13impl Headers {
14    /// Constructs the headers from a HTTP3 [`Frame`].
15    ///
16    /// # Panics
17    ///
18    /// Panics if `frame` is not type [`FrameKind::Headers`].
19    pub fn with_frame(frame: &Frame) -> Result<Self, ErrorCode> {
20        assert!(matches!(frame.kind(), FrameKind::Headers));
21
22        let headers = Decoder::decode(frame.payload()).map_err(|_| ErrorCode::Decompression)?;
23
24        Ok(Self(headers))
25    }
26
27    /// Generates a [`Frame`] with these headers.
28    pub fn generate_frame(&self) -> Frame<'static> {
29        let headers = self.sorted_headers();
30        let payload = Encoder::encode(headers);
31        Frame::new_headers(Cow::Owned(payload.to_vec()))
32    }
33
34    /// Returns a reference to the value associated with the key.
35    pub fn get<K>(&self, key: K) -> Option<&str>
36    where
37        K: AsRef<str>,
38    {
39        self.0.get(key.as_ref()).map(|s| s.as_str())
40    }
41
42    /// Inserts a field (key, value) in the headers.
43    ///
44    /// If the headers did have this key present, the value is updated.
45    pub fn insert<K, V>(&mut self, key: K, value: V)
46    where
47        K: ToString,
48        V: ToString,
49    {
50        self.0.insert(key.to_string(), value.to_string());
51    }
52
53    fn sorted_headers(&self) -> Vec<(&str, &str)> {
54        let mut headers = self
55            .0
56            .iter()
57            .map(|(k, v)| (k.as_str(), v.as_str()))
58            .collect::<Vec<_>>();
59
60        headers.sort_by_key(|(name, _)| (!name.starts_with(':'), *name));
61
62        headers
63    }
64}
65
66impl<K, V> FromIterator<(K, V)> for Headers
67where
68    K: ToString,
69    V: ToString,
70{
71    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
72        Self(
73            iter.into_iter()
74                .map(|(k, v)| (k.to_string(), v.to_string()))
75                .collect(),
76        )
77    }
78}
79
80impl AsRef<HashMap<String, String>> for Headers {
81    fn as_ref(&self) -> &HashMap<String, String> {
82        &self.0
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn generate_frame_kind() {
92        let headers = [("key1", "value1"), ("key2", "value2")]
93            .into_iter()
94            .collect::<Headers>();
95
96        let frame = headers.generate_frame();
97        assert!(matches!(frame.kind(), FrameKind::Headers));
98    }
99
100    #[test]
101    fn get() {
102        let headers = [("key1", "value1"), ("key2", "value2")]
103            .into_iter()
104            .collect::<Headers>();
105
106        assert_eq!(headers.get("key1"), Some("value1"));
107        assert_eq!(headers.get("key2"), Some("value2"));
108        assert_eq!(headers.get("key3"), None);
109    }
110
111    #[test]
112    fn insert() {
113        let mut headers = [("key1", "value1"), ("key2", "value2")]
114            .into_iter()
115            .collect::<Headers>();
116
117        assert_eq!(headers.get("key1"), Some("value1"));
118        headers.insert("key1", "value1bis");
119        assert_eq!(headers.get("key1"), Some("value1bis"));
120
121        assert_eq!(headers.get("key3"), None);
122        headers.insert("key3", "value3");
123        assert_eq!(headers.get("key3"), Some("value3"));
124    }
125
126    #[test]
127    fn idempotence() {
128        let headers = [("key1", "value1"), ("key2", "value2")]
129            .into_iter()
130            .collect::<Headers>();
131
132        let frame = headers.generate_frame();
133
134        assert_eq!(
135            headers.as_ref(),
136            Headers::with_frame(&frame).unwrap().as_ref()
137        );
138    }
139}