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}