hyper_sync/header/common/
strict_transport_security.rs1use std::fmt;
2use std::str::{self, FromStr};
3
4use unicase;
5
6use header::{Header, Raw, parsing};
7
8#[derive(Clone, PartialEq, Debug)]
49pub struct StrictTransportSecurity {
50 pub include_subdomains: bool,
53
54 pub max_age: u64
58}
59
60impl StrictTransportSecurity {
61 pub fn including_subdomains(max_age: u64) -> StrictTransportSecurity {
63 StrictTransportSecurity {
64 max_age: max_age,
65 include_subdomains: true
66 }
67 }
68
69 pub fn excluding_subdomains(max_age: u64) -> StrictTransportSecurity {
71 StrictTransportSecurity {
72 max_age: max_age,
73 include_subdomains: false
74 }
75 }
76}
77
78enum Directive {
79 MaxAge(u64),
80 IncludeSubdomains,
81 Unknown
82}
83
84impl FromStr for StrictTransportSecurity {
85 type Err = ::Error;
86
87 fn from_str(s: &str) -> ::Result<StrictTransportSecurity> {
88 s.split(';')
89 .map(str::trim)
90 .map(|sub| if unicase::eq_ascii(sub, "includeSubdomains") {
91 Ok(Directive::IncludeSubdomains)
92 } else {
93 let mut sub = sub.splitn(2, '=');
94 match (sub.next(), sub.next()) {
95 (Some(left), Some(right))
96 if unicase::eq_ascii(left.trim(), "max-age") => {
97 right
98 .trim()
99 .trim_matches('"')
100 .parse()
101 .map(Directive::MaxAge)
102 },
103 _ => Ok(Directive::Unknown)
104 }
105 })
106 .fold(Ok((None, None)), |res, dir| match (res, dir) {
107 (Ok((None, sub)), Ok(Directive::MaxAge(age))) => Ok((Some(age), sub)),
108 (Ok((age, None)), Ok(Directive::IncludeSubdomains)) => Ok((age, Some(()))),
109 (Ok((Some(_), _)), Ok(Directive::MaxAge(_))) |
110 (Ok((_, Some(_))), Ok(Directive::IncludeSubdomains)) |
111 (_, Err(_)) => Err(::Error::Header),
112 (res, _) => res
113 })
114 .and_then(|res| match res {
115 (Some(age), sub) => Ok(StrictTransportSecurity {
116 max_age: age,
117 include_subdomains: sub.is_some()
118 }),
119 _ => Err(::Error::Header)
120 })
121 }
122}
123
124impl Header for StrictTransportSecurity {
125 fn header_name() -> &'static str {
126 static NAME: &'static str = "Strict-Transport-Security";
127 NAME
128 }
129
130 fn parse_header(raw: &Raw) -> ::Result<StrictTransportSecurity> {
131 parsing::from_one_raw_str(raw)
132 }
133
134 fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
135 f.fmt_line(self)
136 }
137}
138
139impl fmt::Display for StrictTransportSecurity {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 if self.include_subdomains {
142 write!(f, "max-age={}; includeSubdomains", self.max_age)
143 } else {
144 write!(f, "max-age={}", self.max_age)
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::StrictTransportSecurity;
152 use header::Header;
153
154 #[test]
155 fn test_parse_max_age() {
156 let h = Header::parse_header(&"max-age=31536000".into());
157 assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
158 }
159
160 #[test]
161 fn test_parse_max_age_no_value() {
162 let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age".into());
163 assert!(h.is_err());
164 }
165
166 #[test]
167 fn test_parse_quoted_max_age() {
168 let h = Header::parse_header(&"max-age=\"31536000\"".into());
169 assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
170 }
171
172 #[test]
173 fn test_parse_spaces_max_age() {
174 let h = Header::parse_header(&"max-age = 31536000".into());
175 assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
176 }
177
178 #[test]
179 fn test_parse_include_subdomains() {
180 let h = Header::parse_header(&"max-age=15768000 ; includeSubDomains".into());
181 assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: true, max_age: 15768000u64 }));
182 }
183
184 #[test]
185 fn test_parse_no_max_age() {
186 let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"includeSubDomains".into());
187 assert!(h.is_err());
188 }
189
190 #[test]
191 fn test_parse_max_age_nan() {
192 let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age = derp".into());
193 assert!(h.is_err());
194 }
195
196 #[test]
197 fn test_parse_duplicate_directives() {
198 assert!(StrictTransportSecurity::parse_header(&"max-age=100; max-age=5; max-age=0".into()).is_err());
199 }
200}
201
202bench_header!(bench, StrictTransportSecurity, { vec![b"max-age=15768000 ; includeSubDomains".to_vec()] });