headers/common/cookie.rs
1use util::{FlatCsv, SemiColon};
2
3/// `Cookie` header, defined in [RFC6265](http://tools.ietf.org/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 /// # extern crate headers;
31 /// use headers::{Cookie, HeaderMap, HeaderMapExt, 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 /// Iterator the key-value pairs of this `Cookie` header.
57 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
58 self.0.iter().filter_map(|kv| {
59 let mut iter = kv.splitn(2, '=');
60 let key = iter.next()?.trim();
61 let val = iter.next()?.trim();
62 Some((key, val))
63 })
64 }
65}
66
67/*
68impl PartialEq for Cookie {
69 fn eq(&self, other: &Cookie) -> bool {
70 if self.0.len() == other.0.len() {
71 for &(ref k, ref v) in self.0.iter() {
72 if other.get(k) != Some(v) {
73 return false;
74 }
75 }
76 true
77 } else {
78 false
79 }
80 }
81}
82*/
83
84#[cfg(test)]
85mod tests {
86 use super::super::test_decode;
87 use super::Cookie;
88
89 #[test]
90 fn test_parse() {
91 let cookie = test_decode::<Cookie>(&["foo=bar"]).unwrap();
92
93 assert_eq!(cookie.get("foo"), Some("bar"));
94 assert_eq!(cookie.get("bar"), None);
95 }
96
97 #[test]
98 fn test_multipe_same_name() {
99 let cookie = test_decode::<Cookie>(&["foo=bar; foo=baz"]).unwrap();
100
101 assert_eq!(cookie.get("foo"), Some("bar"));
102 }
103
104 #[test]
105 fn test_multipe_lines() {
106 let cookie = test_decode::<Cookie>(&["foo=bar", "lol = cat"]).unwrap();
107
108 assert_eq!(cookie.get("foo"), Some("bar"));
109 assert_eq!(cookie.get("lol"), Some("cat"));
110 }
111
112 /*
113 #[test]
114 fn test_set_and_get() {
115 let mut cookie = Cookie::new();
116 cookie.append("foo", "bar");
117 cookie.append(String::from("dyn"), String::from("amic"));
118
119 assert_eq!(cookie.get("foo"), Some("bar"));
120 assert_eq!(cookie.get("dyn"), Some("amic"));
121 assert!(cookie.get("nope").is_none());
122
123 cookie.append("foo", "notbar");
124 assert_eq!(cookie.get("foo"), Some("bar"));
125
126 cookie.set("foo", "hi");
127 assert_eq!(cookie.get("foo"), Some("hi"));
128 assert_eq!(cookie.get("dyn"), Some("amic"));
129 }
130
131 #[test]
132 fn test_eq() {
133 let mut cookie = Cookie::new();
134 let mut cookie2 = Cookie::new();
135
136 // empty is equal
137 assert_eq!(cookie, cookie2);
138
139 // left has more params
140 cookie.append("foo", "bar");
141 assert_ne!(cookie, cookie2);
142
143 // same len, different params
144 cookie2.append("bar", "foo");
145 assert_ne!(cookie, cookie2);
146
147
148 // right has more params, and matching KV
149 cookie2.append("foo", "bar");
150 assert_ne!(cookie, cookie2);
151
152 // same params, different order
153 cookie.append("bar", "foo");
154 assert_eq!(cookie, cookie2);
155 }
156
157 #[test]
158 fn test_parse() {
159 let mut cookie = Cookie::new();
160
161 let parsed = Cookie::parse_header(&b"foo=bar".to_vec().into()).unwrap();
162 cookie.append("foo", "bar");
163 assert_eq!(cookie, parsed);
164
165 let parsed = Cookie::parse_header(&b"foo=bar;".to_vec().into()).unwrap();
166 assert_eq!(cookie, parsed);
167
168 let parsed = Cookie::parse_header(&b"foo=bar; baz=quux".to_vec().into()).unwrap();
169 cookie.append("baz", "quux");
170 assert_eq!(cookie, parsed);
171
172 let parsed = Cookie::parse_header(&b"foo=bar;; baz=quux".to_vec().into()).unwrap();
173 assert_eq!(cookie, parsed);
174
175 let parsed = Cookie::parse_header(&b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into())
176 .unwrap();
177 assert_eq!(cookie, parsed);
178
179 let parsed = Cookie::parse_header(&b" foo = bar;baz= quux ".to_vec().into()).unwrap();
180 assert_eq!(cookie, parsed);
181
182 let parsed =
183 Cookie::parse_header(&vec![b"foo = bar".to_vec(), b"baz= quux ".to_vec()].into())
184 .unwrap();
185 assert_eq!(cookie, parsed);
186
187 let parsed = Cookie::parse_header(&b"foo=bar; baz=quux ; empty=".to_vec().into()).unwrap();
188 cookie.append("empty", "");
189 assert_eq!(cookie, parsed);
190
191
192 let mut cookie = Cookie::new();
193
194 let parsed = Cookie::parse_header(&b"middle=equals=in=the=middle".to_vec().into()).unwrap();
195 cookie.append("middle", "equals=in=the=middle");
196 assert_eq!(cookie, parsed);
197
198 let parsed =
199 Cookie::parse_header(&b"middle=equals=in=the=middle; double==2".to_vec().into())
200 .unwrap();
201 cookie.append("double", "=2");
202 assert_eq!(cookie, parsed);
203 }
204 */
205}