From 68e1eaf88da5d0197c478ff6967e51de56091a2e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 27 Jul 2022 02:27:46 +0100 Subject: [PATCH] implement parsing for StrictTransportSecurity header --- actix-web-lab/CHANGELOG.md | 2 + actix-web-lab/README.md | 2 +- .../src/strict_transport_security.rs | 103 +++++++++++++++++- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/actix-web-lab/CHANGELOG.md b/actix-web-lab/CHANGELOG.md index 51a87e7e..5e130559 100644 --- a/actix-web-lab/CHANGELOG.md +++ b/actix-web-lab/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Unreleased - 2022-xx-xx +- Implement `FromStr` and `Header::parse` for `StrictTransportSecurity`. +- Implement `PartialEq` and `Eq` for `StrictTransportSecurity`. ## 0.16.6 - 2022-07-27 diff --git a/actix-web-lab/README.md b/actix-web-lab/README.md index 59bc058a..efc23f66 100644 --- a/actix-web-lab/README.md +++ b/actix-web-lab/README.md @@ -78,4 +78,4 @@ - Breaking changes will likely happen on most 0.x version bumps. - Documentation might be limited for some items. - Items that graduate to Actix Web crate will be marked deprecated here for a reasonable amount of time so you can migrate. -- Migrating will often be as easy as dropping the `_lab` suffix from imports when migrating. +- Migrating will often be as easy as dropping the `_lab` suffix from imports. diff --git a/actix-web-lab/src/strict_transport_security.rs b/actix-web-lab/src/strict_transport_security.rs index 3f06e69c..42a235eb 100644 --- a/actix-web-lab/src/strict_transport_security.rs +++ b/actix-web-lab/src/strict_transport_security.rs @@ -2,11 +2,13 @@ //! //! See [`StrictTransportSecurity`] docs. -use std::{convert::Infallible, time::Duration}; +use std::{convert::Infallible, str, time::Duration}; use actix_web::{ + error::ParseError, http::header::{ - Header, HeaderName, HeaderValue, TryIntoHeaderValue, STRICT_TRANSPORT_SECURITY, + from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue, + STRICT_TRANSPORT_SECURITY, }, HttpMessage, }; @@ -35,7 +37,7 @@ pub type Hsts = StrictTransportSecurity; /// See the [HSTS page on MDN] for more information. /// /// [HSTS page on MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[doc(alias = "hsts", alias = "sts")] pub struct StrictTransportSecurity { duration: Duration, @@ -94,6 +96,49 @@ impl Default for StrictTransportSecurity { } } +impl str::FromStr for StrictTransportSecurity { + type Err = ParseError; + + fn from_str(val: &str) -> Result { + let mut parts = val.split(';').map(str::trim); + + // parse max-age/duration from first part of header + let duration = parts + .next() + .ok_or(ParseError::Header)? + .split_once('=') + .and_then(|(key, max_age)| { + if key.trim() != "max-age" { + return None; + } + + max_age.trim().parse().ok() + }) + .map(Duration::from_secs) + .ok_or(ParseError::Header)?; + + let mut include_subdomains = false; + let mut preload = false; + + // find known attributes in remaining parts + for part in parts { + if part == "includeSubdomains" { + include_subdomains = true; + } + + if part == "preload" { + preload = true; + } + } + + Ok(Self { + duration, + include_subdomains, + preload, + }) + } +} + impl TryIntoHeaderValue for StrictTransportSecurity { type Error = Infallible; @@ -119,8 +164,8 @@ impl Header for StrictTransportSecurity { STRICT_TRANSPORT_SECURITY } - fn parse(_msg: &M) -> Result { - unimplemented!("Strict-Transport-Security header cannot yet be parsed"); + fn parse(msg: &M) -> Result { + from_one_raw_str(msg.headers().get(Self::name())) } } @@ -195,4 +240,52 @@ mod test { "max-age=63072000; includeSubDomains" ); } + + #[test] + fn parsing() { + assert!("".parse::().is_err()); + assert!("duration=1".parse::().is_err()); + + assert_eq!( + "max-age=1".parse::().unwrap(), + StrictTransportSecurity { + duration: Duration::from_secs(1), + include_subdomains: false, + preload: false, + } + ); + + assert_eq!( + "max-age=1; includeSubdomains" + .parse::() + .unwrap(), + StrictTransportSecurity { + duration: Duration::from_secs(1), + include_subdomains: true, + preload: false, + } + ); + + assert_eq!( + "max-age=1; preload" + .parse::() + .unwrap(), + StrictTransportSecurity { + duration: Duration::from_secs(1), + include_subdomains: false, + preload: true, + } + ); + + assert_eq!( + "max-age=1; includeSubdomains; preload" + .parse::() + .unwrap(), + StrictTransportSecurity { + duration: Duration::from_secs(1), + include_subdomains: true, + preload: true, + } + ); + } }