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 headers::{Cookie, HeaderMap, HeaderMapExt, HeaderValue};
31    ///
32    /// // Setup the header map with strings...
33    /// let mut headers = HeaderMap::new();
34    /// headers.insert("cookie", HeaderValue::from_static("lang=en-US"));
35    ///
36    /// // Parse a `Cookie` so we can play with it...
37    /// let cookie = headers
38    ///     .typed_get::<Cookie>()
39    ///     .expect("we just inserted a valid Cookie");
40    ///
41    /// assert_eq!(cookie.get("lang"), Some("en-US"));
42    /// assert_eq!(cookie.get("SID"), None);
43    /// ```
44    pub fn get(&self, name: &str) -> Option<&str> {
45        self.iter()
46            .find(|&(key, _)| key == name)
47            .map(|(_, val)| val)
48    }
49
50    /// Get the number of key-value pairs this `Cookie` contains.
51    pub fn len(&self) -> usize {
52        self.iter().count()
53    }
54
55    /// Iterator the key-value pairs of this `Cookie` header.
56    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
57        self.0.iter().filter_map(|kv| {
58            let mut iter = kv.splitn(2, '=');
59            let key = iter.next()?.trim();
60            let val = iter.next()?.trim();
61            Some((key, val))
62        })
63    }
64}
65
66/*
67impl PartialEq for Cookie {
68    fn eq(&self, other: &Cookie) -> bool {
69        if self.0.len() == other.0.len() {
70            for &(ref k, ref v) in self.0.iter() {
71                if other.get(k) != Some(v) {
72                    return false;
73                }
74            }
75            true
76        } else {
77            false
78        }
79    }
80}
81*/
82
83#[cfg(test)]
84mod tests {
85    use super::super::test_decode;
86    use super::Cookie;
87
88    #[test]
89    fn test_parse() {
90        let cookie = test_decode::<Cookie>(&["foo=bar"]).unwrap();
91
92        assert_eq!(cookie.get("foo"), Some("bar"));
93        assert_eq!(cookie.get("bar"), None);
94    }
95
96    #[test]
97    fn test_multipe_same_name() {
98        let cookie = test_decode::<Cookie>(&["foo=bar; foo=baz"]).unwrap();
99
100        assert_eq!(cookie.get("foo"), Some("bar"));
101    }
102
103    #[test]
104    fn test_multipe_lines() {
105        let cookie = test_decode::<Cookie>(&["foo=bar", "lol = cat"]).unwrap();
106
107        assert_eq!(cookie.get("foo"), Some("bar"));
108        assert_eq!(cookie.get("lol"), Some("cat"));
109    }
110
111    /*
112    #[test]
113    fn test_set_and_get() {
114        let mut cookie = Cookie::new();
115        cookie.append("foo", "bar");
116        cookie.append(String::from("dyn"), String::from("amic"));
117
118        assert_eq!(cookie.get("foo"), Some("bar"));
119        assert_eq!(cookie.get("dyn"), Some("amic"));
120        assert!(cookie.get("nope").is_none());
121
122        cookie.append("foo", "notbar");
123        assert_eq!(cookie.get("foo"), Some("bar"));
124
125        cookie.set("foo", "hi");
126        assert_eq!(cookie.get("foo"), Some("hi"));
127        assert_eq!(cookie.get("dyn"), Some("amic"));
128    }
129
130    #[test]
131    fn test_eq() {
132        let mut cookie = Cookie::new();
133        let mut cookie2 = Cookie::new();
134
135        // empty is equal
136        assert_eq!(cookie, cookie2);
137
138        // left has more params
139        cookie.append("foo", "bar");
140        assert_ne!(cookie, cookie2);
141
142        // same len, different params
143        cookie2.append("bar", "foo");
144        assert_ne!(cookie, cookie2);
145
146
147        // right has more params, and matching KV
148        cookie2.append("foo", "bar");
149        assert_ne!(cookie, cookie2);
150
151        // same params, different order
152        cookie.append("bar", "foo");
153        assert_eq!(cookie, cookie2);
154    }
155
156    #[test]
157    fn test_parse() {
158        let mut cookie = Cookie::new();
159
160        let parsed = Cookie::parse_header(&b"foo=bar".to_vec().into()).unwrap();
161        cookie.append("foo", "bar");
162        assert_eq!(cookie, parsed);
163
164        let parsed = Cookie::parse_header(&b"foo=bar;".to_vec().into()).unwrap();
165        assert_eq!(cookie, parsed);
166
167        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux".to_vec().into()).unwrap();
168        cookie.append("baz", "quux");
169        assert_eq!(cookie, parsed);
170
171        let parsed = Cookie::parse_header(&b"foo=bar;; baz=quux".to_vec().into()).unwrap();
172        assert_eq!(cookie, parsed);
173
174        let parsed = Cookie::parse_header(&b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into())
175            .unwrap();
176        assert_eq!(cookie, parsed);
177
178        let parsed = Cookie::parse_header(&b" foo  =    bar;baz= quux  ".to_vec().into()).unwrap();
179        assert_eq!(cookie, parsed);
180
181        let parsed =
182            Cookie::parse_header(&vec![b"foo  =    bar".to_vec(), b"baz= quux  ".to_vec()].into())
183                .unwrap();
184        assert_eq!(cookie, parsed);
185
186        let parsed = Cookie::parse_header(&b"foo=bar; baz=quux ; empty=".to_vec().into()).unwrap();
187        cookie.append("empty", "");
188        assert_eq!(cookie, parsed);
189
190
191        let mut cookie = Cookie::new();
192
193        let parsed = Cookie::parse_header(&b"middle=equals=in=the=middle".to_vec().into()).unwrap();
194        cookie.append("middle", "equals=in=the=middle");
195        assert_eq!(cookie, parsed);
196
197        let parsed =
198            Cookie::parse_header(&b"middle=equals=in=the=middle; double==2".to_vec().into())
199                .unwrap();
200        cookie.append("double", "=2");
201        assert_eq!(cookie, parsed);
202    }
203    */
204}