hyper_sync/header/common/
set_cookie.rs

1use std::fmt;
2use std::str::from_utf8;
3
4use header::{Header, Raw};
5
6/// `Set-Cookie` header, defined [RFC6265](http://tools.ietf.org/html/rfc6265#section-4.1)
7///
8/// The Set-Cookie HTTP response header is used to send cookies from the
9/// server to the user agent.
10///
11/// Informally, the Set-Cookie response header contains the header name
12/// "Set-Cookie" followed by a ":" and a cookie.  Each cookie begins with
13/// a name-value-pair, followed by zero or more attribute-value pairs.
14///
15/// # ABNF
16///
17/// ```text
18/// set-cookie-header = "Set-Cookie:" SP set-cookie-string
19/// set-cookie-string = cookie-pair *( ";" SP cookie-av )
20/// cookie-pair       = cookie-name "=" cookie-value
21/// cookie-name       = token
22/// cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
23/// cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
24///                       ; US-ASCII characters excluding CTLs,
25///                       ; whitespace DQUOTE, comma, semicolon,
26///                       ; and backslash
27/// token             = <token, defined in [RFC2616], Section 2.2>
28///
29/// cookie-av         = expires-av / max-age-av / domain-av /
30///                    path-av / secure-av / httponly-av /
31///                     extension-av
32/// expires-av        = "Expires=" sane-cookie-date
33/// sane-cookie-date  = <rfc1123-date, defined in [RFC2616], Section 3.3.1>
34/// max-age-av        = "Max-Age=" non-zero-digit *DIGIT
35///                       ; In practice, both expires-av and max-age-av
36///                       ; are limited to dates representable by the
37///                       ; user agent.
38/// non-zero-digit    = %x31-39
39///                       ; digits 1 through 9
40/// domain-av         = "Domain=" domain-value
41/// domain-value      = <subdomain>
42///                       ; defined in [RFC1034], Section 3.5, as
43///                       ; enhanced by [RFC1123], Section 2.1
44/// path-av           = "Path=" path-value
45/// path-value        = <any CHAR except CTLs or ";">
46/// secure-av         = "Secure"
47/// httponly-av       = "HttpOnly"
48/// extension-av      = <any CHAR except CTLs or ";">
49/// ```
50///
51/// # Example values
52///
53/// * `SID=31d4d96e407aad42`
54/// * `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT`
55/// * `lang=; Expires=Sun, 06 Nov 1994 08:49:37 GMT`
56/// * `lang=en-US; Path=/; Domain=example.com`
57///
58/// # Example
59///
60/// ```
61/// use hyper_sync::header::{Headers, SetCookie};
62///
63/// let mut headers = Headers::new();
64///
65/// headers.set(
66///     SetCookie(vec![
67///         String::from("foo=bar; Path=/path; Domain=example.com")
68///     ])
69/// );
70/// ```
71#[derive(Clone, PartialEq, Debug)]
72pub struct SetCookie(pub Vec<String>);
73
74__hyper__deref!(SetCookie => Vec<String>);
75
76impl Header for SetCookie {
77    fn header_name() -> &'static str {
78        static NAME: &'static str = "Set-Cookie";
79        NAME
80    }
81
82    fn parse_header(raw: &Raw) -> ::Result<SetCookie> {
83        let mut set_cookies = Vec::with_capacity(raw.len());
84        for set_cookies_raw in raw {
85            if let Ok(s) = from_utf8(&set_cookies_raw[..]) {
86                set_cookies.push(s.trim().to_owned());
87            }
88        }
89
90        if !set_cookies.is_empty() {
91            Ok(SetCookie(set_cookies))
92        } else {
93            Err(::Error::Header)
94        }
95    }
96
97    fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
98        for cookie in &self.0 {
99            try!(f.fmt_line(cookie));
100        }
101        Ok(())
102    }
103}
104
105#[test]
106fn test_set_cookie_fmt() {
107    use ::header::Headers;
108    let mut headers = Headers::new();
109    headers.set(SetCookie(vec![
110        "foo=bar".into(),
111        "baz=quux".into(),
112    ]));
113    assert_eq!(headers.to_string(), "Set-Cookie: foo=bar\r\nSet-Cookie: baz=quux\r\n");
114}