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 payload = Encoder::encode(&self.0);
30        Frame::new_headers(Cow::Owned(payload.to_vec()))
31    }
32
33    /// Returns a reference to the value associated with the key.
34    #[inline(always)]
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    #[inline(always)]
46    pub fn insert<K, V>(&mut self, key: K, value: V)
47    where
48        K: ToString,
49        V: ToString,
50    {
51        self.0.insert(key.to_string(), value.to_string());
52    }
53}
54
55impl<K, V> FromIterator<(K, V)> for Headers
56where
57    K: ToString,
58    V: ToString,
59{
60    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
61        Self(
62            iter.into_iter()
63                .map(|(k, v)| (k.to_string(), v.to_string()))
64                .collect(),
65        )
66    }
67}
68
69impl AsRef<HashMap<String, String>> for Headers {
70    fn as_ref(&self) -> &HashMap<String, String> {
71        &self.0
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn generate_frame_kind() {
81        let headers = [("key1", "value1"), ("key2", "value2")]
82            .into_iter()
83            .collect::<Headers>();
84
85        let frame = headers.generate_frame();
86        assert!(matches!(frame.kind(), FrameKind::Headers));
87    }
88
89    #[test]
90    fn get() {
91        let headers = [("key1", "value1"), ("key2", "value2")]
92            .into_iter()
93            .collect::<Headers>();
94
95        assert_eq!(headers.get("key1"), Some("value1"));
96        assert_eq!(headers.get("key2"), Some("value2"));
97        assert_eq!(headers.get("key3"), None);
98    }
99
100    #[test]
101    fn insert() {
102        let mut headers = [("key1", "value1"), ("key2", "value2")]
103            .into_iter()
104            .collect::<Headers>();
105
106        assert_eq!(headers.get("key1"), Some("value1"));
107        headers.insert("key1", "value1bis");
108        assert_eq!(headers.get("key1"), Some("value1bis"));
109
110        assert_eq!(headers.get("key3"), None);
111        headers.insert("key3", "value3");
112        assert_eq!(headers.get("key3"), Some("value3"));
113    }
114
115    #[test]
116    fn idempotence() {
117        let headers = [("key1", "value1"), ("key2", "value2")]
118            .into_iter()
119            .collect::<Headers>();
120
121        let frame = headers.generate_frame();
122
123        assert_eq!(
124            headers.as_ref(),
125            Headers::with_frame(&frame).unwrap().as_ref()
126        );
127    }
128}