hyper_sync/header/common/
cookie.rs

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