cogo_http/header/common/
set_cookie.rs

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