hyperx/header/common/
cookie.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::str::from_utf8;
4
5use header::{Header, RawLike};
6use header::internals::VecMap;
7
8/// `Cookie` header, defined in [RFC6265](http://tools.ietf.org/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/// Consider using the _cookie_ crate for parsing/decoding or encoding
22/// cookie values.
23///
24/// # Example
25/// ```
26/// # extern crate http;
27/// use hyperx::header::{Cookie, TypedHeaders};
28///
29/// let mut headers = http::HeaderMap::new();
30/// let mut cookie = Cookie::new();
31/// cookie.append("foo", "bar");
32///
33/// assert_eq!(cookie.get("foo"), Some("bar"));
34///
35/// headers.encode(&cookie);
36/// ```
37#[derive(Clone)]
38pub struct Cookie(VecMap<Cow<'static, str>, Cow<'static, str>>);
39
40impl Cookie {
41    /// Creates a new `Cookie` header.
42    pub fn new() -> Cookie {
43        Cookie(VecMap::with_capacity(0))
44    }
45
46    /// Sets a name and value for the `Cookie`.
47    ///
48    /// # Note
49    ///
50    /// This will remove all other instances with the same name,
51    /// and insert the new value.
52    pub fn set<K, V>(&mut self, key: K, value: V)
53        where K: Into<Cow<'static, str>>,
54              V: Into<Cow<'static, str>>
55    {
56        let key = key.into();
57        let value = value.into();
58        self.0.remove_all(&key);
59        self.0.append(key, value);
60    }
61
62    /// Append a name and value for the `Cookie`.
63    ///
64    /// # Note
65    ///
66    /// Cookies are allowed to set a name with a
67    /// a value multiple times. For example:
68    ///
69    /// ```
70    /// use hyperx::header::Cookie;
71    /// let mut cookie = Cookie::new();
72    /// cookie.append("foo", "bar");
73    /// cookie.append("foo", "quux");
74    /// assert_eq!(cookie.to_string(), "foo=bar; foo=quux");
75    pub fn append<K, V>(&mut self, key: K, value: V)
76        where K: Into<Cow<'static, str>>,
77              V: Into<Cow<'static, str>>
78    {
79        self.0.append(key.into(), value.into());
80    }
81
82    /// Get a value for the name, if it exists.
83    ///
84    /// # Note
85    ///
86    /// Only returns the first instance found. To access
87    /// any other values associated with the name, parse
88    /// the `str` representation.
89    pub fn get(&self, key: &str) -> Option<&str> {
90        self.0.get(key).map(AsRef::as_ref)
91    }
92
93    /// Iterate cookies.
94    ///
95    /// Iterate cookie (key, value) in insertion order.
96    ///
97    /// ```
98    /// use hyperx::header::Cookie;
99    /// let mut cookie = Cookie::new();
100    /// cookie.append("foo", "bar");
101    /// cookie.append(String::from("dyn"), String::from("amic"));
102    ///
103    /// let mut keys = Vec::new();
104    /// let mut values = Vec::new();
105    /// for (k, v) in cookie.iter() {
106    ///     keys.push(k);
107    ///     values.push(v);
108    /// }
109    /// assert_eq!(keys, vec!["foo", "dyn"]);
110    /// assert_eq!(values, vec!["bar", "amic"]);
111    /// ```
112    pub fn iter(&self) -> CookieIter {
113        CookieIter(self.0.iter())
114    }
115}
116
117impl Header for Cookie {
118    fn header_name() -> &'static str {
119        static NAME: &'static str = "Cookie";
120        NAME
121    }
122
123    fn parse_header<'a, T>(raw: &'a T) -> ::Result<Cookie>
124    where T: RawLike<'a>
125    {
126        let mut vec_map = VecMap::with_capacity(raw.len());
127        for cookies_raw in raw.iter() {
128            let cookies_str = from_utf8(&cookies_raw[..])?;
129            for cookie_str in cookies_str.split(';') {
130                let mut key_val = cookie_str.splitn(2, '=');
131                let key_val = (key_val.next(), key_val.next());
132                if let (Some(key), Some(val)) = key_val {
133                    vec_map.insert(key.trim().to_owned().into(), val.trim().to_owned().into());
134                }
135            }
136        }
137
138        if vec_map.len() != 0 {
139            Ok(Cookie(vec_map))
140        } else {
141            Err(::Error::Header)
142        }
143    }
144
145    fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
146        f.fmt_line(self)
147    }
148}
149
150impl PartialEq for Cookie {
151    fn eq(&self, other: &Cookie) -> bool {
152        if self.0.len() == other.0.len() {
153            for &(ref k, ref v) in self.0.iter() {
154                if other.get(k) != Some(v) {
155                    return false;
156                }
157            }
158            true
159        } else {
160            false
161        }
162    }
163}
164
165impl fmt::Debug for Cookie {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        f.debug_map()
168            .entries(self.0.iter().map(|&(ref k, ref v)| (k, v)))
169            .finish()
170    }
171}
172
173impl fmt::Display for Cookie {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        let mut iter = self.0.iter();
176        if let Some(&(ref key, ref val)) = iter.next() {
177            write!(f, "{}={}", key, val)?;
178        }
179        for &(ref key, ref val) in iter {
180            write!(f, "; {}={}", key, val)?;
181        }
182        Ok(())
183    }
184}
185
186/// Iterator for cookie.
187#[derive(Debug)]
188pub struct CookieIter<'a>(::std::slice::Iter<'a, (Cow<'static, str>, Cow<'static, str>)>);
189
190impl<'a> Iterator for CookieIter<'a> {
191    type Item = (&'a str, &'a str);
192
193    fn next(&mut self) -> Option<Self::Item> {
194        self.0.next().map(|kv| (kv.0.as_ref(), kv.1.as_ref()))
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use header::{Header, Raw};
201    use super::Cookie;
202
203    #[test]
204    fn test_set_and_get() {
205        let mut cookie = Cookie::new();
206        cookie.append("foo", "bar");
207        cookie.append(String::from("dyn"), String::from("amic"));
208
209        assert_eq!(cookie.get("foo"), Some("bar"));
210        assert_eq!(cookie.get("dyn"), Some("amic"));
211        assert!(cookie.get("nope").is_none());
212
213        cookie.append("foo", "notbar");
214        assert_eq!(cookie.get("foo"), Some("bar"));
215
216        cookie.set("foo", "hi");
217        assert_eq!(cookie.get("foo"), Some("hi"));
218        assert_eq!(cookie.get("dyn"), Some("amic"));
219    }
220
221    #[test]
222    fn test_eq() {
223        let mut cookie = Cookie::new();
224        let mut cookie2 = Cookie::new();
225
226        // empty is equal
227        assert_eq!(cookie, cookie2);
228
229        // left has more params
230        cookie.append("foo", "bar");
231        assert_ne!(cookie, cookie2);
232
233        // same len, different params
234        cookie2.append("bar", "foo");
235        assert_ne!(cookie, cookie2);
236
237        // right has more params, and matching KV
238        cookie2.append("foo", "bar");
239        assert_ne!(cookie, cookie2);
240
241        // same params, different order
242        cookie.append("bar", "foo");
243        assert_eq!(cookie, cookie2);
244    }
245
246    #[test]
247    fn test_parse() {
248        let mut cookie = Cookie::new();
249        let r: Raw = b"foo=bar".to_vec().into();
250        let parsed = Cookie::parse_header(&r).unwrap();
251        cookie.append("foo", "bar");
252        assert_eq!(cookie, parsed);
253
254        let parsed = Cookie::parse_header(&r).unwrap();
255        assert_eq!(cookie, parsed);
256
257        let r: Raw = b"foo=bar; baz=quux".to_vec().into();
258        let parsed = Cookie::parse_header(&r).unwrap();
259        cookie.append("baz", "quux");
260        assert_eq!(cookie, parsed);
261
262        let r: Raw = b"foo=bar;; baz=quux".to_vec().into();
263        let parsed = Cookie::parse_header(&r).unwrap();
264        assert_eq!(cookie, parsed);
265
266        let r: Raw = b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into();
267        let parsed = Cookie::parse_header(&r).unwrap();
268        assert_eq!(cookie, parsed);
269
270        let r: Raw = b" foo  =    bar;baz= quux  ".to_vec().into();
271        let parsed = Cookie::parse_header(&r).unwrap();
272        assert_eq!(cookie, parsed);
273
274        let r: Raw = vec![b"foo  =    bar".to_vec(), b"baz= quux  ".to_vec()].into();
275        let parsed = Cookie::parse_header(&r).unwrap();
276        assert_eq!(cookie, parsed);
277        let r: Raw = b"foo=bar; baz=quux ; empty=".to_vec().into();
278        let parsed = Cookie::parse_header(&r).unwrap();
279        cookie.append("empty", "");
280        assert_eq!(cookie, parsed);
281
282        let mut cookie = Cookie::new();
283        let r: Raw = b"middle=equals=in=the=middle".to_vec().into();
284        let parsed = Cookie::parse_header(&r).unwrap();
285        cookie.append("middle", "equals=in=the=middle");
286        assert_eq!(cookie, parsed);
287
288        let r: Raw = b"middle=equals=in=the=middle; double==2".to_vec().into();
289        let parsed = Cookie::parse_header(&r).unwrap();
290        cookie.append("double", "=2");
291        assert_eq!(cookie, parsed);
292    }
293}
294
295bench_header!(bench, Cookie, {
296    vec![b"foo=bar; baz=quux".to_vec()]
297});
298
299standard_header!(Cookie, COOKIE);