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}