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}