hyperx/header/common/
set_cookie.rs

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