1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::error::ErrorCode;
use crate::frame::Frame;
use crate::frame::FrameKind;
use crate::qpack::Decoder;
use crate::qpack::Encoder;
use std::borrow::Cow;
use std::collections::HashMap;

/// HTTP3 headers from the request or response.
#[derive(Debug)]
pub struct Headers(HashMap<String, String>);

impl Headers {
    /// Constructs the headers from a HTTP3 [`Frame`].
    ///
    /// # Panics
    ///
    /// Panics if `frame` is not type [`FrameKind::Headers`].
    pub fn with_frame(frame: &Frame) -> Result<Self, ErrorCode> {
        assert!(matches!(frame.kind(), FrameKind::Headers));

        let headers = Decoder::decode(frame.payload()).map_err(|_| ErrorCode::Decompression)?;

        Ok(Self(headers))
    }

    /// Generates a [`Frame`] with these headers.
    pub fn generate_frame(&self) -> Frame<'static> {
        let payload = Encoder::encode(&self.0);
        Frame::new_headers(Cow::Owned(payload.to_vec()))
    }

    /// Returns a reference to the value associated with the key.
    #[inline(always)]
    pub fn get<K>(&self, key: K) -> Option<&str>
    where
        K: AsRef<str>,
    {
        self.0.get(key.as_ref()).map(|s| s.as_str())
    }

    /// Inserts a field (key, value) in the headers.
    ///
    /// If the headers did have this key present, the value is updated.
    #[inline(always)]
    pub fn insert<K, V>(&mut self, key: K, value: V)
    where
        K: ToString,
        V: ToString,
    {
        self.0.insert(key.to_string(), value.to_string());
    }
}

impl<K, V> FromIterator<(K, V)> for Headers
where
    K: ToString,
    V: ToString,
{
    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
        Self(
            iter.into_iter()
                .map(|(k, v)| (k.to_string(), v.to_string()))
                .collect(),
        )
    }
}

impl AsRef<HashMap<String, String>> for Headers {
    fn as_ref(&self) -> &HashMap<String, String> {
        &self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn generate_frame_kind() {
        let headers = [("key1", "value1"), ("key2", "value2")]
            .into_iter()
            .collect::<Headers>();

        let frame = headers.generate_frame();
        assert!(matches!(frame.kind(), FrameKind::Headers));
    }

    #[test]
    fn get() {
        let headers = [("key1", "value1"), ("key2", "value2")]
            .into_iter()
            .collect::<Headers>();

        assert_eq!(headers.get("key1"), Some("value1"));
        assert_eq!(headers.get("key2"), Some("value2"));
        assert_eq!(headers.get("key3"), None);
    }

    #[test]
    fn insert() {
        let mut headers = [("key1", "value1"), ("key2", "value2")]
            .into_iter()
            .collect::<Headers>();

        assert_eq!(headers.get("key1"), Some("value1"));
        headers.insert("key1", "value1bis");
        assert_eq!(headers.get("key1"), Some("value1bis"));

        assert_eq!(headers.get("key3"), None);
        headers.insert("key3", "value3");
        assert_eq!(headers.get("key3"), Some("value3"));
    }

    #[test]
    fn idempotence() {
        let headers = [("key1", "value1"), ("key2", "value2")]
            .into_iter()
            .collect::<Headers>();

        let frame = headers.generate_frame();

        assert_eq!(
            headers.as_ref(),
            Headers::with_frame(&frame).unwrap().as_ref()
        );
    }
}