Skip to main content

rama_http_headers/common/
cookie.rs

1// TOOD: adapt together <https://github.com/plabayo/rama/issues/44>
2
3use rama_utils::str::arcstr::ArcStr;
4
5derive_non_empty_flat_csv_header! {
6    #[header(name = COOKIE, sep = SemiColon)]
7    #[derive(Clone, Debug, PartialEq)]
8    /// `Cookie` header, defined in [RFC6265](https://datatracker.ietf.org/doc/html/rfc6265#section-5.4)
9    ///
10    /// If the user agent does attach a Cookie header field to an HTTP
11    /// request, the user agent must send the cookie-string
12    /// as the value of the header field.
13    ///
14    /// When the user agent generates an HTTP request, the user agent MUST NOT
15    /// attach more than one Cookie header field.
16    ///
17    /// # Example values
18    /// * `SID=31d4d96e407aad42`
19    /// * `SID=31d4d96e407aad42; lang=en-US`
20    ///
21    pub struct Cookie(pub NonEmptyVec<ArcStr>);
22}
23
24impl Cookie {
25    /// Lookup a value for a cookie name.
26    ///
27    /// # Example
28    ///
29    /// ```
30    /// use rama_http_headers::{HeaderMapExt, Cookie};
31    /// use rama_http_types::{HeaderMap, HeaderValue};
32    ///
33    /// // Setup the header map with strings...
34    /// let mut headers = HeaderMap::new();
35    /// headers.insert("cookie", HeaderValue::from_static("lang=en-US"));
36    ///
37    /// // Parse a `Cookie` so we can play with it...
38    /// let cookie = headers
39    ///     .typed_get::<Cookie>()
40    ///     .expect("we just inserted a valid Cookie");
41    ///
42    /// assert_eq!(cookie.get("lang"), Some("en-US"));
43    /// assert_eq!(cookie.get("SID"), None);
44    /// ```
45    #[must_use]
46    pub fn get(&self, name: &str) -> Option<&str> {
47        self.iter()
48            .find(|&(key, _)| key == name)
49            .map(|(_, val)| val)
50    }
51
52    /// Iterator the key-value pairs of this `Cookie` header.
53    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
54        self.0.iter().filter_map(|kv| {
55            let mut iter = kv.splitn(2, '=');
56            let key = iter.next()?.trim();
57            let val = iter.next()?.trim();
58            Some((key, val))
59        })
60    }
61}
62
63/*
64impl PartialEq for Cookie {
65    fn eq(&self, other: &Cookie) -> bool {
66        if self.0.len() == other.0.len() {
67            for &(ref k, ref v) in self.0.iter() {
68                if other.get(k) != Some(v) {
69                    return false;
70                }
71            }
72            true
73        } else {
74            false
75        }
76    }
77}
78*/
79
80#[cfg(test)]
81mod tests {
82    use super::super::test_decode;
83    use super::Cookie;
84
85    #[test]
86    fn test_parse() {
87        let cookie = test_decode::<Cookie>(&["foo=bar"]).unwrap();
88
89        assert_eq!(cookie.get("foo"), Some("bar"));
90        assert_eq!(cookie.get("bar"), None);
91    }
92
93    #[test]
94    fn test_multipe_same_name() {
95        let cookie = test_decode::<Cookie>(&["foo=bar; foo=baz"]).unwrap();
96
97        assert_eq!(cookie.get("foo"), Some("bar"));
98    }
99
100    #[test]
101    fn test_multipe_lines() {
102        let cookie = test_decode::<Cookie>(&["foo=bar", "lol = cat"]).unwrap();
103
104        assert_eq!(cookie.get("foo"), Some("bar"));
105        assert_eq!(cookie.get("lol"), Some("cat"));
106    }
107
108    /*
109    #[test]
110    fn test_set_and_get() {
111        let mut cookie = Cookie::new();
112        cookie.append("foo", "bar");
113        cookie.append(String::from("dyn"), String::from("amic"));
114
115        assert_eq!(cookie.get("foo"), Some("bar"));
116        assert_eq!(cookie.get("dyn"), Some("amic"));
117        assert!(cookie.get("nope").is_none());
118
119        cookie.append("foo", "notbar");
120        assert_eq!(cookie.get("foo"), Some("bar"));
121
122        cookie.set("foo", "hi");
123        assert_eq!(cookie.get("foo"), Some("hi"));
124        assert_eq!(cookie.get("dyn"), Some("amic"));
125    }
126
127    #[test]
128    fn test_eq() {
129        let mut cookie = Cookie::new();
130        let mut cookie2 = Cookie::new();
131
132        // empty is equal
133        assert_eq!(cookie, cookie2);
134
135        // left has more params
136        cookie.append("foo", "bar");
137        assert_ne!(cookie, cookie2);
138
139        // same len, different params
140        cookie2.append("bar", "foo");
141        assert_ne!(cookie, cookie2);
142
143
144        // right has more params, and matching KV
145        cookie2.append("foo", "bar");
146        assert_ne!(cookie, cookie2);
147
148        // same params, different order
149        cookie.append("bar", "foo");
150        assert_eq!(cookie, cookie2);
151    }
152
153    #[test]
154    fn test_parse() {
155        let mut cookie = Cookie::new();
156
157        let parsed = Cookie::parse_header(&b"foo=bar".to_vec().into()).unwrap();
158        cookie.append("foo", "bar");
159        assert_eq!(cookie, parsed);
160
161        let parsed = Cookie::parse_header(&b"foo=bar;".to_vec().into()).unwrap();
162        assert_eq!(cookie, parsed);
163
164        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux".to_vec().into()).unwrap();
165        cookie.append("baz", "quux");
166        assert_eq!(cookie, parsed);
167
168        let parsed = Cookie::parse_header(&b"foo=bar;; baz=quux".to_vec().into()).unwrap();
169        assert_eq!(cookie, parsed);
170
171        let parsed = Cookie::parse_header(&b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into())
172            .unwrap();
173        assert_eq!(cookie, parsed);
174
175        let parsed = Cookie::parse_header(&b" foo  =    bar;baz= quux  ".to_vec().into()).unwrap();
176        assert_eq!(cookie, parsed);
177
178        let parsed =
179            Cookie::parse_header(&vec![b"foo  =    bar".to_vec(), b"baz= quux  ".to_vec()].into())
180                .unwrap();
181        assert_eq!(cookie, parsed);
182
183        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux ; empty=".to_vec().into()).unwrap();
184        cookie.append("empty", "");
185        assert_eq!(cookie, parsed);
186
187
188        let mut cookie = Cookie::new();
189
190        let parsed = Cookie::parse_header(&b"middle=equals=in=the=middle".to_vec().into()).unwrap();
191        cookie.append("middle", "equals=in=the=middle");
192        assert_eq!(cookie, parsed);
193
194        let parsed =
195            Cookie::parse_header(&b"middle=equals=in=the=middle; double==2".to_vec().into())
196                .unwrap();
197        cookie.append("double", "=2");
198        assert_eq!(cookie, parsed);
199    }
200    */
201}