rama_http_headers/common/
cookie.rs

1use crate::util::{FlatCsv, SemiColon};
2
3/// `Cookie` header, defined in [RFC6265](https://datatracker.ietf.org/doc/html/rfc6265#section-5.4)
4///
5/// If the user agent does attach a Cookie header field to an HTTP
6/// request, the user agent must send the cookie-string
7/// as the value of the header field.
8///
9/// When the user agent generates an HTTP request, the user agent MUST NOT
10/// attach more than one Cookie header field.
11///
12/// # Example values
13/// * `SID=31d4d96e407aad42`
14/// * `SID=31d4d96e407aad42; lang=en-US`
15///
16#[derive(Clone, Debug)]
17pub struct Cookie(FlatCsv<SemiColon>);
18
19derive_header! {
20    Cookie(_),
21    name: COOKIE
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    pub fn get(&self, name: &str) -> Option<&str> {
46        self.iter()
47            .find(|&(key, _)| key == name)
48            .map(|(_, val)| val)
49    }
50
51    /// Get the number of key-value pairs this `Cookie` contains.
52    pub fn len(&self) -> usize {
53        self.iter().count()
54    }
55
56    /// Returns true if this doesn't contain any cookies
57    pub fn is_empty(&self) -> bool {
58        self.len() == 0
59    }
60
61    /// Iterator the key-value pairs of this `Cookie` header.
62    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
63        self.0.iter().filter_map(|kv| {
64            let mut iter = kv.splitn(2, '=');
65            let key = iter.next()?.trim();
66            let val = iter.next()?.trim();
67            Some((key, val))
68        })
69    }
70}
71
72/*
73impl PartialEq for Cookie {
74    fn eq(&self, other: &Cookie) -> bool {
75        if self.0.len() == other.0.len() {
76            for &(ref k, ref v) in self.0.iter() {
77                if other.get(k) != Some(v) {
78                    return false;
79                }
80            }
81            true
82        } else {
83            false
84        }
85    }
86}
87*/
88
89#[cfg(test)]
90mod tests {
91    use super::super::test_decode;
92    use super::Cookie;
93
94    #[test]
95    fn test_parse() {
96        let cookie = test_decode::<Cookie>(&["foo=bar"]).unwrap();
97
98        assert_eq!(cookie.get("foo"), Some("bar"));
99        assert_eq!(cookie.get("bar"), None);
100    }
101
102    #[test]
103    fn test_multipe_same_name() {
104        let cookie = test_decode::<Cookie>(&["foo=bar; foo=baz"]).unwrap();
105
106        assert_eq!(cookie.get("foo"), Some("bar"));
107    }
108
109    #[test]
110    fn test_multipe_lines() {
111        let cookie = test_decode::<Cookie>(&["foo=bar", "lol = cat"]).unwrap();
112
113        assert_eq!(cookie.get("foo"), Some("bar"));
114        assert_eq!(cookie.get("lol"), Some("cat"));
115    }
116
117    /*
118    #[test]
119    fn test_set_and_get() {
120        let mut cookie = Cookie::new();
121        cookie.append("foo", "bar");
122        cookie.append(String::from("dyn"), String::from("amic"));
123
124        assert_eq!(cookie.get("foo"), Some("bar"));
125        assert_eq!(cookie.get("dyn"), Some("amic"));
126        assert!(cookie.get("nope").is_none());
127
128        cookie.append("foo", "notbar");
129        assert_eq!(cookie.get("foo"), Some("bar"));
130
131        cookie.set("foo", "hi");
132        assert_eq!(cookie.get("foo"), Some("hi"));
133        assert_eq!(cookie.get("dyn"), Some("amic"));
134    }
135
136    #[test]
137    fn test_eq() {
138        let mut cookie = Cookie::new();
139        let mut cookie2 = Cookie::new();
140
141        // empty is equal
142        assert_eq!(cookie, cookie2);
143
144        // left has more params
145        cookie.append("foo", "bar");
146        assert_ne!(cookie, cookie2);
147
148        // same len, different params
149        cookie2.append("bar", "foo");
150        assert_ne!(cookie, cookie2);
151
152
153        // right has more params, and matching KV
154        cookie2.append("foo", "bar");
155        assert_ne!(cookie, cookie2);
156
157        // same params, different order
158        cookie.append("bar", "foo");
159        assert_eq!(cookie, cookie2);
160    }
161
162    #[test]
163    fn test_parse() {
164        let mut cookie = Cookie::new();
165
166        let parsed = Cookie::parse_header(&b"foo=bar".to_vec().into()).unwrap();
167        cookie.append("foo", "bar");
168        assert_eq!(cookie, parsed);
169
170        let parsed = Cookie::parse_header(&b"foo=bar;".to_vec().into()).unwrap();
171        assert_eq!(cookie, parsed);
172
173        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux".to_vec().into()).unwrap();
174        cookie.append("baz", "quux");
175        assert_eq!(cookie, parsed);
176
177        let parsed = Cookie::parse_header(&b"foo=bar;; baz=quux".to_vec().into()).unwrap();
178        assert_eq!(cookie, parsed);
179
180        let parsed = Cookie::parse_header(&b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into())
181            .unwrap();
182        assert_eq!(cookie, parsed);
183
184        let parsed = Cookie::parse_header(&b" foo  =    bar;baz= quux  ".to_vec().into()).unwrap();
185        assert_eq!(cookie, parsed);
186
187        let parsed =
188            Cookie::parse_header(&vec![b"foo  =    bar".to_vec(), b"baz= quux  ".to_vec()].into())
189                .unwrap();
190        assert_eq!(cookie, parsed);
191
192        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux ; empty=".to_vec().into()).unwrap();
193        cookie.append("empty", "");
194        assert_eq!(cookie, parsed);
195
196
197        let mut cookie = Cookie::new();
198
199        let parsed = Cookie::parse_header(&b"middle=equals=in=the=middle".to_vec().into()).unwrap();
200        cookie.append("middle", "equals=in=the=middle");
201        assert_eq!(cookie, parsed);
202
203        let parsed =
204            Cookie::parse_header(&b"middle=equals=in=the=middle; double==2".to_vec().into())
205                .unwrap();
206        cookie.append("double", "=2");
207        assert_eq!(cookie, parsed);
208    }
209    */
210}