diff --git a/src/auth/authorization.rs b/src/auth/authorization.rs index 3833cf14..0390f538 100644 --- a/src/auth/authorization.rs +++ b/src/auth/authorization.rs @@ -21,7 +21,7 @@ use crate::headers::{Header, HeaderName, HeaderValue, Headers, AUTHORIZATION}; /// let authz = Authorization::new(scheme, credentials.into()); /// /// let mut res = Response::new(200); -/// authz.apply(&mut res); +/// res.insert_header(&authz, &authz); /// /// let authz = Authorization::from_headers(res)?.unwrap(); /// @@ -71,24 +71,6 @@ impl Authorization { })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - self.header_name() - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = format!("{} {}", self.scheme, self.credentials); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the authorization scheme. pub fn scheme(&self) -> AuthenticationScheme { self.scheme @@ -116,7 +98,10 @@ impl Header for Authorization { } fn header_value(&self) -> HeaderValue { - self.value() + let output = format!("{} {}", self.scheme, self.credentials); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -132,7 +117,7 @@ mod test { let authz = Authorization::new(scheme, credentials.into()); let mut headers = Headers::new(); - authz.apply(&mut headers); + authz.apply_header(&mut headers); let authz = Authorization::from_headers(headers)?.unwrap(); diff --git a/src/auth/basic_auth.rs b/src/auth/basic_auth.rs index 441a4343..f8c99615 100644 --- a/src/auth/basic_auth.rs +++ b/src/auth/basic_auth.rs @@ -1,6 +1,9 @@ -use crate::auth::{AuthenticationScheme, Authorization}; use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION}; use crate::Status; +use crate::{ + auth::{AuthenticationScheme, Authorization}, + headers::Header, +}; use crate::{bail_status as bail, ensure_status as ensure}; /// HTTP Basic authorization. @@ -22,7 +25,7 @@ use crate::{bail_status as bail, ensure_status as ensure}; /// let authz = BasicAuth::new(username, password); /// /// let mut res = Response::new(200); -/// authz.apply(&mut res); +/// res.insert_header(&authz, &authz); /// /// let authz = BasicAuth::from_headers(res)?.unwrap(); /// @@ -84,24 +87,6 @@ impl BasicAuth { Ok(Self { username, password }) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - AUTHORIZATION - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let scheme = AuthenticationScheme::Basic; - let credentials = base64::encode(format!("{}:{}", self.username, self.password)); - let auth = Authorization::new(scheme, credentials); - auth.value() - } - /// Get the username. pub fn username(&self) -> &str { self.username.as_str() @@ -113,13 +98,16 @@ impl BasicAuth { } } -impl crate::headers::Header for BasicAuth { +impl Header for BasicAuth { fn header_name(&self) -> HeaderName { AUTHORIZATION } fn header_value(&self) -> HeaderValue { - self.value() + let scheme = AuthenticationScheme::Basic; + let credentials = base64::encode(format!("{}:{}", self.username, self.password)); + let auth = Authorization::new(scheme, credentials); + auth.header_value() } } @@ -135,7 +123,7 @@ mod test { let authz = BasicAuth::new(username, password); let mut headers = Headers::new(); - authz.apply(&mut headers); + authz.apply_header(&mut headers); let authz = BasicAuth::from_headers(headers)?.unwrap(); diff --git a/src/auth/mod.rs b/src/auth/mod.rs index a03efbaa..a8ea43d7 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -13,7 +13,7 @@ //! let authz = BasicAuth::new(username, password); //! //! let mut res = Response::new(200); -//! authz.apply(&mut res); +//! res.insert_header(&authz, &authz); //! //! let authz = BasicAuth::from_headers(res)?.unwrap(); //! diff --git a/src/auth/www_authenticate.rs b/src/auth/www_authenticate.rs index c52d25bd..2d204541 100644 --- a/src/auth/www_authenticate.rs +++ b/src/auth/www_authenticate.rs @@ -1,6 +1,6 @@ -use crate::auth::AuthenticationScheme; use crate::bail_status as bail; use crate::headers::{HeaderName, HeaderValue, Headers, WWW_AUTHENTICATE}; +use crate::{auth::AuthenticationScheme, headers::Header}; /// Define the authentication method that should be used to gain access to a /// resource. @@ -27,7 +27,7 @@ use crate::headers::{HeaderName, HeaderValue, Headers, WWW_AUTHENTICATE}; /// let authz = WwwAuthenticate::new(scheme, realm.into()); /// /// let mut res = Response::new(200); -/// authz.apply(&mut res); +/// res.insert_header(&authz, &authz); /// /// let authz = WwwAuthenticate::from_headers(res)?.unwrap(); /// @@ -93,24 +93,6 @@ impl WwwAuthenticate { Ok(Some(Self { scheme, realm })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - WWW_AUTHENTICATE - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = format!(r#"{} realm="{}", charset="UTF-8""#, self.scheme, self.realm); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the authorization scheme. pub fn scheme(&self) -> AuthenticationScheme { self.scheme @@ -132,13 +114,16 @@ impl WwwAuthenticate { } } -impl crate::headers::Header for WwwAuthenticate { +impl Header for WwwAuthenticate { fn header_name(&self) -> HeaderName { WWW_AUTHENTICATE } fn header_value(&self) -> HeaderValue { - self.value() + let output = format!(r#"{} realm="{}", charset="UTF-8""#, self.scheme, self.realm); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -154,7 +139,7 @@ mod test { let authz = WwwAuthenticate::new(scheme, realm.into()); let mut headers = Headers::new(); - authz.apply(&mut headers); + authz.apply_header(&mut headers); assert_eq!( headers["WWW-Authenticate"], diff --git a/src/cache/age.rs b/src/cache/age.rs index 169d15a7..e891de73 100644 --- a/src/cache/age.rs +++ b/src/cache/age.rs @@ -1,8 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, AGE}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, AGE}; use crate::Status; use std::fmt::Debug; -use std::option; + use std::time::Duration; /// HTTP `Age` header @@ -22,7 +22,7 @@ use std::time::Duration; /// let age = Age::from_secs(12); /// /// let mut res = Response::new(200); -/// age.apply(&mut res); +/// res.insert_header(&age, &age); /// /// let age = Age::from_headers(res)?.unwrap(); /// assert_eq!(age, Age::from_secs(12)); @@ -67,19 +67,14 @@ impl Age { Ok(Some(Self { dur })) } +} - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(AGE, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for Age { + fn header_name(&self) -> HeaderName { AGE } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let output = self.dur.as_secs().to_string(); // SAFETY: the internal string is validated to be ASCII. @@ -87,24 +82,6 @@ impl Age { } } -impl ToHeaderValues for Age { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - -impl crate::headers::Header for Age { - fn header_name(&self) -> HeaderName { - AGE - } - - fn header_value(&self) -> HeaderValue { - self.value() - } -} - #[cfg(test)] mod test { use super::*; @@ -115,7 +92,7 @@ mod test { let age = Age::new(Duration::from_secs(12)); let mut headers = Headers::new(); - age.apply(&mut headers); + age.apply_header(&mut headers); let age = Age::from_headers(headers)?.unwrap(); assert_eq!(age, Age::new(Duration::from_secs(12))); diff --git a/src/cache/cache_control/cache_control.rs b/src/cache/cache_control/cache_control.rs index 4257c133..deffd6bd 100644 --- a/src/cache/cache_control/cache_control.rs +++ b/src/cache/cache_control/cache_control.rs @@ -1,9 +1,11 @@ -use crate::cache::CacheDirective; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CACHE_CONTROL}; +use headers::Header; + +use crate::headers::{HeaderName, HeaderValue, Headers, CACHE_CONTROL}; +use crate::{cache::CacheDirective, headers}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::slice; /// A Cache-Control header. @@ -20,7 +22,7 @@ use std::slice; /// entries.push(CacheDirective::NoStore); /// /// let mut res = Response::new(200); -/// entries.apply(&mut res); +/// res.insert_header(&entries, &entries); /// /// let entries = CacheControl::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); @@ -59,31 +61,6 @@ impl CacheControl { Ok(Some(Self { entries })) } - - /// Sets the `Server-Timing` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(CACHE_CONTROL, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CACHE_CONTROL - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, directive) in self.entries.iter().enumerate() { - let directive: HeaderValue = directive.clone().into(); - match n { - 0 => write!(output, "{}", directive).unwrap(), - _ => write!(output, ", {}", directive).unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } /// Push a directive into the list of entries. pub fn push(&mut self, directive: CacheDirective) { self.entries.push(directive); @@ -104,12 +81,22 @@ impl CacheControl { } } -impl crate::headers::Header for CacheControl { +impl Header for CacheControl { fn header_name(&self) -> HeaderName { CACHE_CONTROL } fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, directive) in self.entries.iter().enumerate() { + let directive: HeaderValue = directive.clone().into(); + match n { + 0 => write!(output, "{}", directive).unwrap(), + _ => write!(output, ", {}", directive).unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -202,14 +189,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for CacheControl { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for CacheControl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); diff --git a/src/cache/cache_control/mod.rs b/src/cache/cache_control/mod.rs index d7e962bd..c13c7d44 100644 --- a/src/cache/cache_control/mod.rs +++ b/src/cache/cache_control/mod.rs @@ -16,7 +16,7 @@ pub use cache_directive::CacheDirective; #[cfg(test)] mod test { use super::*; - use crate::headers::{Headers, CACHE_CONTROL}; + use crate::headers::{Header, Headers, CACHE_CONTROL}; #[test] fn smoke() -> crate::Result<()> { @@ -25,7 +25,7 @@ mod test { entries.push(CacheDirective::NoStore); let mut headers = Headers::new(); - entries.apply(&mut headers); + entries.apply_header(&mut headers); let entries = CacheControl::from_headers(headers)?.unwrap(); let mut entries = entries.iter(); diff --git a/src/cache/clear_site_data/mod.rs b/src/cache/clear_site_data/mod.rs index 21294be5..73b471e6 100644 --- a/src/cache/clear_site_data/mod.rs +++ b/src/cache/clear_site_data/mod.rs @@ -1,17 +1,18 @@ //! Clear browsing data (cookies, storage, cache) associated with the //! requesting website -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CLEAR_SITE_DATA}; +use crate::headers::{self, HeaderName, HeaderValue, Headers, CLEAR_SITE_DATA}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::slice; use std::str::FromStr; mod directive; pub use directive::ClearDirective; +use headers::Header; /// Clear browsing data (cookies, storage, cache) associated with the /// requesting website. @@ -35,7 +36,7 @@ pub use directive::ClearDirective; /// entries.push(ClearDirective::Cookies); /// /// let mut res = Response::new(200); -/// entries.apply(&mut res); +/// res.insert_header(&entries, &entries); /// /// let entries = ClearSiteData::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); @@ -81,37 +82,6 @@ impl ClearSiteData { Ok(Some(Self { entries, wildcard })) } - /// Sets the `If-Match` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(CLEAR_SITE_DATA, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CLEAR_SITE_DATA - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, etag) in self.entries.iter().enumerate() { - match n { - 0 => write!(output, "{}", etag.to_string()).unwrap(), - _ => write!(output, ", {}", etag.to_string()).unwrap(), - }; - } - - if self.wildcard { - match output.len() { - 0 => write!(output, r#""*""#).unwrap(), - _ => write!(output, r#", "*""#).unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); @@ -231,14 +201,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for ClearSiteData { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for ClearSiteData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -249,17 +211,35 @@ impl Debug for ClearSiteData { } } -impl crate::headers::Header for ClearSiteData { +impl Header for ClearSiteData { fn header_name(&self) -> HeaderName { CLEAR_SITE_DATA } + fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, etag) in self.entries.iter().enumerate() { + match n { + 0 => write!(output, "{}", etag.to_string()).unwrap(), + _ => write!(output, ", {}", etag.to_string()).unwrap(), + }; + } + + if self.wildcard { + match output.len() { + 0 => write!(output, r#""*""#).unwrap(), + _ => write!(output, r#", "*""#).unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { + use super::*; use crate::cache::{ClearDirective, ClearSiteData}; use crate::Response; @@ -270,7 +250,7 @@ mod test { entries.push(ClearDirective::Cookies); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = ClearSiteData::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -286,7 +266,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = ClearSiteData::from_headers(res)?.unwrap(); assert_eq!(entries.wildcard(), true); diff --git a/src/cache/expires.rs b/src/cache/expires.rs index 92a6ba3f..1cc77e5b 100644 --- a/src/cache/expires.rs +++ b/src/cache/expires.rs @@ -1,8 +1,7 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPIRES}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, EXPIRES}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; -use std::option; use std::time::{Duration, SystemTime}; /// HTTP `Expires` header @@ -24,7 +23,7 @@ use std::time::{Duration, SystemTime}; /// let expires = Expires::new_at(time); /// /// let mut res = Response::new(200); -/// expires.apply(&mut res); +/// res.insert_header(&expires, &expires); /// /// let expires = Expires::from_headers(res)?.unwrap(); /// @@ -70,40 +69,17 @@ impl Expires { let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } - - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(EXPIRES, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - EXPIRES - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = fmt_http_date(self.instant); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } } -impl crate::headers::Header for Expires { +impl Header for Expires { fn header_name(&self) -> HeaderName { EXPIRES } fn header_value(&self) -> HeaderValue { - self.value() - } -} + let output = fmt_http_date(self.instant); -impl ToHeaderValues for Expires { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -118,7 +94,7 @@ mod test { let expires = Expires::new_at(time); let mut headers = Headers::new(); - expires.apply(&mut headers); + expires.apply_header(&mut headers); let expires = Expires::from_headers(headers)?.unwrap(); diff --git a/src/conditional/etag.rs b/src/conditional/etag.rs index bdf17ee8..505006a5 100644 --- a/src/conditional/etag.rs +++ b/src/conditional/etag.rs @@ -1,8 +1,7 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ETAG}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, ETAG}; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Display}; -use std::option; /// HTTP Entity Tags. /// @@ -24,7 +23,7 @@ use std::option; /// let etag = ETag::new("0xcafebeef".to_string()); /// /// let mut res = Response::new(200); -/// etag.apply(&mut res); +/// res.insert_header(&etag, &etag); /// /// let etag = ETag::from_headers(res)?.unwrap(); /// assert_eq!(etag, ETag::Strong(String::from("0xcafebeef"))); @@ -67,23 +66,6 @@ impl ETag { Self::from_str(s).map(Some) } - /// Sets the `ETag` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(ETAG, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - ETAG - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let s = self.to_string(); - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(s.into()) } - } - /// Returns `true` if the ETag is a `Strong` value. pub fn is_strong(&self) -> bool { matches!(self, Self::Strong(_)) @@ -130,12 +112,14 @@ impl ETag { } } -impl crate::headers::Header for ETag { +impl Header for ETag { fn header_name(&self) -> HeaderName { ETAG } fn header_value(&self) -> HeaderValue { - self.value() + let s = self.to_string(); + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(s.into()) } } } @@ -148,14 +132,6 @@ impl Display for ETag { } } -impl ToHeaderValues for ETag { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - #[cfg(test)] mod test { use super::*; @@ -166,7 +142,7 @@ mod test { let etag = ETag::new("0xcafebeef".to_string()); let mut headers = Headers::new(); - etag.apply(&mut headers); + etag.apply_header(&mut headers); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Strong(String::from("0xcafebeef"))); @@ -178,7 +154,7 @@ mod test { let etag = ETag::new_weak("0xcafebeef".to_string()); let mut headers = Headers::new(); - etag.apply(&mut headers); + etag.apply_header(&mut headers); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Weak(String::from("0xcafebeef"))); diff --git a/src/conditional/if_match.rs b/src/conditional/if_match.rs index 5e163f2a..15c15e3b 100644 --- a/src/conditional/if_match.rs +++ b/src/conditional/if_match.rs @@ -1,11 +1,11 @@ //! Apply the HTTP method if the ETag matches. -use crate::conditional::ETag; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_MATCH}; +use crate::headers::{HeaderName, HeaderValue, Headers, IF_MATCH}; +use crate::{conditional::ETag, headers::Header}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::slice; /// Apply the HTTP method if the ETag matches. @@ -27,7 +27,7 @@ use std::slice; /// entries.push(ETag::new("0xbeefcafe".to_string())); /// /// let mut res = Response::new(200); -/// entries.apply(&mut res); +/// res.insert_header(&entries, &entries); /// /// let entries = IfMatch::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); @@ -73,37 +73,6 @@ impl IfMatch { Ok(Some(Self { entries, wildcard })) } - /// Sets the `If-Match` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(IF_MATCH, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - IF_MATCH - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, etag) in self.entries.iter().enumerate() { - match n { - 0 => write!(output, "{}", etag.to_string()).unwrap(), - _ => write!(output, ", {}", etag.to_string()).unwrap(), - }; - } - - if self.wildcard { - match output.len() { - 0 => write!(output, "*").unwrap(), - _ => write!(output, ", *").unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); @@ -134,12 +103,28 @@ impl IfMatch { } } -impl crate::headers::Header for IfMatch { +impl Header for IfMatch { fn header_name(&self) -> HeaderName { IF_MATCH } fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, etag) in self.entries.iter().enumerate() { + match n { + 0 => write!(output, "{}", etag.to_string()).unwrap(), + _ => write!(output, ", {}", etag.to_string()).unwrap(), + }; + } + + if self.wildcard { + match output.len() { + 0 => write!(output, "*").unwrap(), + _ => write!(output, ", *").unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -232,14 +217,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for IfMatch { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for IfMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -252,6 +229,7 @@ impl Debug for IfMatch { #[cfg(test)] mod test { + use super::*; use crate::conditional::{ETag, IfMatch}; use crate::Response; @@ -262,7 +240,7 @@ mod test { entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = IfMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -284,7 +262,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = IfMatch::from_headers(res)?.unwrap(); assert_eq!(entries.wildcard(), true); diff --git a/src/conditional/if_modified_since.rs b/src/conditional/if_modified_since.rs index 8286a050..7ee1d179 100644 --- a/src/conditional/if_modified_since.rs +++ b/src/conditional/if_modified_since.rs @@ -1,8 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_MODIFIED_SINCE}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, IF_MODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; -use std::option; + use std::time::SystemTime; /// Apply the HTTP method if the entity has been modified after the given @@ -25,7 +25,7 @@ use std::time::SystemTime; /// let expires = IfModifiedSince::new(time); /// /// let mut res = Response::new(200); -/// expires.apply(&mut res); +/// res.insert_header(&expires, &expires); /// /// let expires = IfModifiedSince::from_headers(res)?.unwrap(); /// @@ -65,19 +65,13 @@ impl IfModifiedSince { let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } +} - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(IF_MODIFIED_SINCE, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for IfModifiedSince { + fn header_name(&self) -> HeaderName { IF_MODIFIED_SINCE } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. @@ -85,23 +79,6 @@ impl IfModifiedSince { } } -impl ToHeaderValues for IfModifiedSince { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - -impl crate::headers::Header for IfModifiedSince { - fn header_name(&self) -> HeaderName { - IF_MODIFIED_SINCE - } - fn header_value(&self) -> HeaderValue { - self.value() - } -} - #[cfg(test)] mod test { use super::*; @@ -114,7 +91,7 @@ mod test { let expires = IfModifiedSince::new(time); let mut headers = Headers::new(); - expires.apply(&mut headers); + expires.apply_header(&mut headers); let expires = IfModifiedSince::from_headers(headers)?.unwrap(); diff --git a/src/conditional/if_none_match.rs b/src/conditional/if_none_match.rs index 96cf0f94..56e342b7 100644 --- a/src/conditional/if_none_match.rs +++ b/src/conditional/if_none_match.rs @@ -3,12 +3,12 @@ //! This is used to update caches or to prevent uploading a new resource when //! one already exists. -use crate::conditional::ETag; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_NONE_MATCH}; +use crate::headers::{HeaderName, HeaderValue, Headers, IF_NONE_MATCH}; +use crate::{conditional::ETag, headers::Header}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::slice; /// Apply the HTTP method if the ETags do not match. @@ -33,7 +33,7 @@ use std::slice; /// entries.push(ETag::new("0xbeefcafe".to_string())); /// /// let mut res = Response::new(200); -/// entries.apply(&mut res); +/// res.insert_header(&entries, &entries); /// /// let entries = IfNoneMatch::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); @@ -79,37 +79,6 @@ impl IfNoneMatch { Ok(Some(Self { entries, wildcard })) } - /// Sets the `If-None-Match` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(IF_NONE_MATCH, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - IF_NONE_MATCH - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, etag) in self.entries.iter().enumerate() { - match n { - 0 => write!(output, "{}", etag.to_string()).unwrap(), - _ => write!(output, ", {}", etag.to_string()).unwrap(), - }; - } - - if self.wildcard { - match output.len() { - 0 => write!(output, "*").unwrap(), - _ => write!(output, ", *").unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); @@ -140,12 +109,28 @@ impl IfNoneMatch { } } -impl crate::headers::Header for IfNoneMatch { +impl Header for IfNoneMatch { fn header_name(&self) -> HeaderName { IF_NONE_MATCH } fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, etag) in self.entries.iter().enumerate() { + match n { + 0 => write!(output, "{}", etag.to_string()).unwrap(), + _ => write!(output, ", {}", etag.to_string()).unwrap(), + }; + } + + if self.wildcard { + match output.len() { + 0 => write!(output, "*").unwrap(), + _ => write!(output, ", *").unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -238,14 +223,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for IfNoneMatch { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for IfNoneMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -258,6 +235,7 @@ impl Debug for IfNoneMatch { #[cfg(test)] mod test { + use super::*; use crate::conditional::{ETag, IfNoneMatch}; use crate::Response; @@ -268,7 +246,7 @@ mod test { entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = IfNoneMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -290,7 +268,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = IfNoneMatch::from_headers(res)?.unwrap(); assert_eq!(entries.wildcard(), true); diff --git a/src/conditional/if_unmodified_since.rs b/src/conditional/if_unmodified_since.rs index 9439de42..e648d4d5 100644 --- a/src/conditional/if_unmodified_since.rs +++ b/src/conditional/if_unmodified_since.rs @@ -1,8 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_UNMODIFIED_SINCE}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, IF_UNMODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; -use std::option; + use std::time::SystemTime; /// Apply the HTTP method if the entity has not been modified after the @@ -25,7 +25,7 @@ use std::time::SystemTime; /// let expires = IfUnmodifiedSince::new(time); /// /// let mut res = Response::new(200); -/// expires.apply(&mut res); +/// res.insert_header(&expires, &expires); /// /// let expires = IfUnmodifiedSince::from_headers(res)?.unwrap(); /// @@ -65,40 +65,17 @@ impl IfUnmodifiedSince { let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } - - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(IF_UNMODIFIED_SINCE, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - IF_UNMODIFIED_SINCE - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = fmt_http_date(self.instant); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } } -impl crate::headers::Header for IfUnmodifiedSince { +impl Header for IfUnmodifiedSince { fn header_name(&self) -> HeaderName { IF_UNMODIFIED_SINCE } fn header_value(&self) -> HeaderValue { - self.value() - } -} + let output = fmt_http_date(self.instant); -impl ToHeaderValues for IfUnmodifiedSince { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -114,7 +91,7 @@ mod test { let expires = IfUnmodifiedSince::new(time); let mut headers = Headers::new(); - expires.apply(&mut headers); + expires.apply_header(&mut headers); let expires = IfUnmodifiedSince::from_headers(headers)?.unwrap(); diff --git a/src/conditional/last_modified.rs b/src/conditional/last_modified.rs index 556f2ed3..900fe545 100644 --- a/src/conditional/last_modified.rs +++ b/src/conditional/last_modified.rs @@ -1,8 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, LAST_MODIFIED}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, LAST_MODIFIED}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; -use std::option; + use std::time::SystemTime; /// The last modification date of a resource. @@ -24,7 +24,7 @@ use std::time::SystemTime; /// let last_modified = LastModified::new(time); /// /// let mut res = Response::new(200); -/// last_modified.apply(&mut res); +/// res.insert_header(&last_modified, &last_modified); /// /// let last_modified = LastModified::from_headers(res)?.unwrap(); /// @@ -64,40 +64,17 @@ impl LastModified { let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } - - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(LAST_MODIFIED, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - LAST_MODIFIED - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = fmt_http_date(self.instant); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } } -impl crate::headers::Header for LastModified { +impl Header for LastModified { fn header_name(&self) -> HeaderName { LAST_MODIFIED } fn header_value(&self) -> HeaderValue { - self.value() - } -} + let output = fmt_http_date(self.instant); -impl ToHeaderValues for LastModified { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -113,7 +90,7 @@ mod test { let last_modified = LastModified::new(time); let mut headers = Headers::new(); - last_modified.apply(&mut headers); + last_modified.apply_header(&mut headers); let last_modified = LastModified::from_headers(headers)?.unwrap(); diff --git a/src/conditional/vary.rs b/src/conditional/vary.rs index d6d73826..39bbd12d 100644 --- a/src/conditional/vary.rs +++ b/src/conditional/vary.rs @@ -1,10 +1,10 @@ //! Apply the HTTP method if the ETag matches. -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, VARY}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, VARY}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::slice; use std::str::FromStr; @@ -27,7 +27,7 @@ use std::str::FromStr; /// entries.push("Accept-Encoding")?; /// /// let mut res = Response::new(200); -/// entries.apply(&mut res); +/// res.insert_header(&entries, &entries); /// /// let entries = Vary::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); @@ -74,16 +74,6 @@ impl Vary { Ok(Some(Self { entries, wildcard })) } - /// Sets the `If-Match` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(VARY, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - VARY - } - /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard @@ -94,31 +84,6 @@ impl Vary { self.wildcard = wildcard } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, name) in self.entries.iter().enumerate() { - let directive: HeaderValue = name - .as_str() - .parse() - .expect("Could not convert a HeaderName into a HeaderValue"); - match n { - 0 => write!(output, "{}", directive).unwrap(), - _ => write!(output, ", {}", directive).unwrap(), - }; - } - - if self.wildcard { - match output.len() { - 0 => write!(output, "*").unwrap(), - _ => write!(output, ", *").unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) -> crate::Result<()> { self.entries.push(directive.into()); @@ -140,12 +105,33 @@ impl Vary { } } -impl crate::headers::Header for Vary { +impl Header for Vary { fn header_name(&self) -> HeaderName { VARY } + fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, name) in self.entries.iter().enumerate() { + let directive: HeaderValue = name + .as_str() + .parse() + .expect("Could not convert a HeaderName into a HeaderValue"); + match n { + 0 => write!(output, "{}", directive).unwrap(), + _ => write!(output, ", {}", directive).unwrap(), + }; + } + + if self.wildcard { + match output.len() { + 0 => write!(output, "*").unwrap(), + _ => write!(output, ", *").unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -238,14 +224,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for Vary { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for Vary { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -258,6 +236,7 @@ impl Debug for Vary { #[cfg(test)] mod test { + use super::*; use crate::conditional::Vary; use crate::Response; @@ -268,7 +247,7 @@ mod test { entries.push("Accept-Encoding")?; let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = Vary::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -284,7 +263,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply(&mut res); + entries.apply_header(&mut res); let entries = Vary::from_headers(res)?.unwrap(); assert_eq!(entries.wildcard(), true); diff --git a/src/content/accept.rs b/src/content/accept.rs index 2769259d..927e8dc5 100644 --- a/src/content/accept.rs +++ b/src/content/accept.rs @@ -1,13 +1,16 @@ //! Client header advertising which media types the client is able to understand. -use crate::content::{ContentType, MediaTypeProposal}; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT}; +use crate::headers::{HeaderName, HeaderValue, Headers, ACCEPT}; use crate::mime::Mime; use crate::utils::sort_by_weight; +use crate::{ + content::{ContentType, MediaTypeProposal}, + headers::Header, +}; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Write}; -use std::option; + use std::slice; /// Client header advertising which media types the client is able to understand. @@ -39,7 +42,7 @@ use std::slice; /// /// let mut res = Response::new(200); /// let content_type = accept.negotiate(&[mime::XML])?; -/// content_type.apply(&mut res); +/// res.insert_header(&content_type, &content_type); /// /// assert_eq!(res["Content-Type"], "application/xml;charset=utf-8"); /// # @@ -143,18 +146,26 @@ impl Accept { Err(err) } - /// Sets the `Accept-Encoding` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(ACCEPT, self.value()); + /// An iterator visiting all entries. + pub fn iter(&self) -> Iter<'_> { + Iter { + inner: self.entries.iter(), + } } - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - ACCEPT + /// An iterator visiting all entries. + pub fn iter_mut(&mut self) -> IterMut<'_> { + IterMut { + inner: self.entries.iter_mut(), + } } +} - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { +impl Header for Accept { + fn header_name(&self) -> HeaderName { + ACCEPT + } + fn header_value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = directive.clone().into(); @@ -174,29 +185,6 @@ impl Accept { // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } - - /// An iterator visiting all entries. - pub fn iter(&self) -> Iter<'_> { - Iter { - inner: self.entries.iter(), - } - } - - /// An iterator visiting all entries. - pub fn iter_mut(&mut self) -> IterMut<'_> { - IterMut { - inner: self.entries.iter_mut(), - } - } -} - -impl crate::headers::Header for Accept { - fn header_name(&self) -> HeaderName { - ACCEPT - } - fn header_value(&self) -> HeaderValue { - self.value() - } } impl IntoIterator for Accept { @@ -288,14 +276,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for Accept { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for Accept { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -318,7 +298,7 @@ mod test { accept.push(mime::HTML); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), mime::HTML); @@ -331,7 +311,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -345,7 +325,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -360,7 +340,7 @@ mod test { accept.push(mime::XML); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -377,7 +357,7 @@ mod test { accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let mut accept = Accept::from_headers(headers)?.unwrap(); accept.sort(); @@ -396,7 +376,7 @@ mod test { accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut res = Response::new(200); - accept.apply(&mut res); + accept.apply_header(&mut res); let mut accept = Accept::from_headers(res)?.unwrap(); accept.sort(); diff --git a/src/content/accept_encoding.rs b/src/content/accept_encoding.rs index 98b90310..17182c78 100644 --- a/src/content/accept_encoding.rs +++ b/src/content/accept_encoding.rs @@ -1,12 +1,15 @@ //! Client header advertising available compression algorithms. -use crate::content::{ContentEncoding, Encoding, EncodingProposal}; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING}; +use crate::headers::{HeaderName, HeaderValue, Headers, ACCEPT_ENCODING}; use crate::utils::sort_by_weight; +use crate::{ + content::{ContentEncoding, Encoding, EncodingProposal}, + headers::Header, +}; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Write}; -use std::option; + use std::slice; /// Client header advertising available compression algorithms. @@ -30,7 +33,7 @@ use std::slice; /// /// let mut res = Response::new(200); /// let encoding = accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?; -/// encoding.apply(&mut res); +/// res.insert_header(&encoding, &encoding); /// /// assert_eq!(res["Content-Encoding"], "br"); /// # @@ -135,18 +138,27 @@ impl AcceptEncoding { Err(err) } - /// Sets the `Accept-Encoding` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(ACCEPT_ENCODING, self.value()); + /// An iterator visiting all entries. + pub fn iter(&self) -> Iter<'_> { + Iter { + inner: self.entries.iter(), + } + } + + /// An iterator visiting all entries. + pub fn iter_mut(&mut self) -> IterMut<'_> { + IterMut { + inner: self.entries.iter_mut(), + } } +} - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for AcceptEncoding { + fn header_name(&self) -> HeaderName { ACCEPT_ENCODING } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = directive.clone().into(); @@ -166,29 +178,6 @@ impl AcceptEncoding { // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } - - /// An iterator visiting all entries. - pub fn iter(&self) -> Iter<'_> { - Iter { - inner: self.entries.iter(), - } - } - - /// An iterator visiting all entries. - pub fn iter_mut(&mut self) -> IterMut<'_> { - IterMut { - inner: self.entries.iter_mut(), - } - } -} - -impl crate::headers::Header for AcceptEncoding { - fn header_name(&self) -> HeaderName { - ACCEPT_ENCODING - } - fn header_value(&self) -> HeaderValue { - self.value() - } } impl IntoIterator for AcceptEncoding { @@ -280,14 +269,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for AcceptEncoding { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for AcceptEncoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -310,7 +291,7 @@ mod test { accept.push(Encoding::Gzip); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); @@ -323,7 +304,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -337,7 +318,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -352,7 +333,7 @@ mod test { accept.push(Encoding::Brotli); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -369,7 +350,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let mut accept = AcceptEncoding::from_headers(headers)?.unwrap(); accept.sort(); @@ -388,7 +369,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); - accept.apply(&mut res); + accept.apply_header(&mut res); let mut accept = AcceptEncoding::from_headers(res)?.unwrap(); accept.sort(); diff --git a/src/content/content_encoding.rs b/src/content/content_encoding.rs index 9fa9e6fd..bbab123c 100644 --- a/src/content/content_encoding.rs +++ b/src/content/content_encoding.rs @@ -1,11 +1,13 @@ //! Specify the compression algorithm. -use crate::content::{Encoding, EncodingProposal}; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CONTENT_ENCODING}; +use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_ENCODING}; +use crate::{ + content::{Encoding, EncodingProposal}, + headers::Header, +}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; -use std::option; /// Specify the compression algorithm. /// @@ -23,7 +25,7 @@ use std::option; /// let mut encoding = ContentEncoding::new(Encoding::Gzip); /// /// let mut res = Response::new(200); -/// encoding.apply(&mut res); +/// res.insert_header(&encoding, &encoding); /// /// let encoding = ContentEncoding::from_headers(res)?.unwrap(); /// assert_eq!(encoding, &Encoding::Gzip); @@ -59,41 +61,18 @@ impl ContentEncoding { Ok(Some(Self { inner })) } - /// Sets the `Content-Encoding` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(CONTENT_ENCODING, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CONTENT_ENCODING - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - self.inner.into() - } - /// Access the encoding kind. pub fn encoding(&self) -> Encoding { self.inner } } -impl crate::headers::Header for ContentEncoding { +impl Header for ContentEncoding { fn header_name(&self) -> HeaderName { CONTENT_ENCODING } fn header_value(&self) -> HeaderValue { - self.value() - } -} - -impl ToHeaderValues for ContentEncoding { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + self.inner.into() } } diff --git a/src/content/content_length.rs b/src/content/content_length.rs index 0f6fbd15..c36acb08 100644 --- a/src/content/content_length.rs +++ b/src/content/content_length.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_LENGTH}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, CONTENT_LENGTH}; use crate::Status; /// The size of the entity-body, in bytes, sent to the recipient. @@ -18,7 +18,7 @@ use crate::Status; /// let content_len = ContentLength::new(12); /// /// let mut res = Response::new(200); -/// content_len.apply(&mut res); +/// res.insert_header(&content_len, &content_len); /// /// let content_len = ContentLength::from_headers(res)?.unwrap(); /// assert_eq!(content_len.len(), 12); @@ -51,24 +51,6 @@ impl ContentLength { Ok(Some(Self { length })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CONTENT_LENGTH - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = format!("{}", self.length); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the content length. pub fn len(&self) -> u64 { self.length @@ -80,12 +62,15 @@ impl ContentLength { } } -impl crate::headers::Header for ContentLength { +impl Header for ContentLength { fn header_name(&self) -> HeaderName { CONTENT_LENGTH } fn header_value(&self) -> HeaderValue { - self.value() + let output = format!("{}", self.length); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -99,7 +84,7 @@ mod test { let content_len = ContentLength::new(12); let mut headers = Headers::new(); - content_len.apply(&mut headers); + content_len.apply_header(&mut headers); let content_len = ContentLength::from_headers(headers)?.unwrap(); assert_eq!(content_len.len(), 12); diff --git a/src/content/content_location.rs b/src/content/content_location.rs index 68135e33..c744aa5b 100644 --- a/src/content/content_location.rs +++ b/src/content/content_location.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_LOCATION}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, CONTENT_LOCATION}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -22,7 +22,7 @@ use std::convert::TryInto; /// let content_location = ContentLocation::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); -/// content_location.apply(&mut res); +/// res.insert_header(&content_location, &content_location); /// /// let url = Url::parse("https://example.net/")?; /// let content_location = ContentLocation::from_headers(url, res)?.unwrap(); @@ -64,24 +64,6 @@ impl ContentLocation { Ok(Some(Self { url })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CONTENT_LOCATION - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = self.url.to_string(); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the url. pub fn location(&self) -> &Url { &self.url @@ -99,12 +81,15 @@ impl ContentLocation { } } -impl crate::headers::Header for ContentLocation { +impl Header for ContentLocation { fn header_name(&self) -> HeaderName { CONTENT_LOCATION } fn header_value(&self) -> HeaderValue { - self.value() + let output = self.url.to_string(); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -118,7 +103,7 @@ mod test { let content_location = ContentLocation::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); - content_location.apply(&mut headers); + content_location.apply_header(&mut headers); let content_location = ContentLocation::from_headers(Url::parse("https://example.net/").unwrap(), headers)? diff --git a/src/content/content_type.rs b/src/content/content_type.rs index cb8c0be1..90153d89 100644 --- a/src/content/content_type.rs +++ b/src/content/content_type.rs @@ -1,6 +1,6 @@ use std::{convert::TryInto, str::FromStr}; -use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_TYPE}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, CONTENT_TYPE}; use crate::mime::Mime; /// Indicate the media type of a resource's content. @@ -18,17 +18,17 @@ use crate::mime::Mime; /// # fn main() -> http_types::Result<()> { /// # /// use http_types::content::ContentType; -/// use http_types::Response; +/// use http_types::{headers::Header, Response}; /// use http_types::mime::Mime; /// use std::str::FromStr; /// /// let content_type = ContentType::new("text/*"); /// /// let mut res = Response::new(200); -/// content_type.apply(&mut res); +/// res.insert_header(&content_type, &content_type); /// /// let content_type = ContentType::from_headers(res)?.unwrap(); -/// assert_eq!(content_type.value(), format!("{}", Mime::from_str("text/*")?).as_str()); +/// assert_eq!(content_type.header_value(), format!("{}", Mime::from_str("text/*")?).as_str()); /// # /// # Ok(()) } /// ``` @@ -73,31 +73,16 @@ impl ContentType { })?; Ok(Some(Self { media_type })) } - - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - CONTENT_TYPE - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = format!("{}", self.media_type); - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } } -impl crate::headers::Header for ContentType { +impl Header for ContentType { fn header_name(&self) -> HeaderName { CONTENT_TYPE } fn header_value(&self) -> HeaderValue { - self.value() + let output = format!("{}", self.media_type); + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -129,11 +114,11 @@ mod test { let ct = ContentType::new(Mime::from_str("text/*")?); let mut headers = Headers::new(); - ct.apply(&mut headers); + ct.apply_header(&mut headers); let ct = ContentType::from_headers(headers)?.unwrap(); assert_eq!( - ct.value(), + ct.header_value(), format!("{}", Mime::from_str("text/*")?).as_str() ); Ok(()) diff --git a/src/content/mod.rs b/src/content/mod.rs index 9c936941..aff9fb4b 100644 --- a/src/content/mod.rs +++ b/src/content/mod.rs @@ -24,7 +24,7 @@ //! //! let mut res = Response::new(200); //! let content_type = accept.negotiate(&[mime::XML])?; -//! content_type.apply(&mut res); +//! res.insert_header(&content_type, &content_type); //! //! assert_eq!(res["Content-Type"], "application/xml;charset=utf-8"); //! # diff --git a/src/headers/header.rs b/src/headers/header.rs index deffa5f3..527f6dd7 100644 --- a/src/headers/header.rs +++ b/src/headers/header.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use crate::headers::{HeaderName, HeaderValue, Headers}; /// A trait representing a [`HeaderName`] and [`HeaderValue`] pair. @@ -28,6 +30,16 @@ impl<'a, 'b> Header for (&'a str, &'b str) { } } +impl<'a, T: Header> Header for &'a T { + fn header_name(&self) -> HeaderName { + self.deref().header_name() + } + + fn header_value(&self) -> HeaderValue { + self.deref().header_value() + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/headers/header_name.rs b/src/headers/header_name.rs index d4eb21be..83fd4024 100644 --- a/src/headers/header_name.rs +++ b/src/headers/header_name.rs @@ -4,6 +4,8 @@ use std::str::FromStr; use crate::Error; +use super::Header; + /// A header name. #[derive(Clone, PartialEq, Eq, Hash)] pub struct HeaderName(Cow<'static, str>); @@ -88,6 +90,12 @@ impl From<&HeaderName> for HeaderName { } } +impl From for HeaderName { + fn from(header: T) -> HeaderName { + header.header_name() + } +} + impl<'a> From<&'a str> for HeaderName { fn from(value: &'a str) -> Self { Self::from_str(value).expect("String slice should be valid ASCII") diff --git a/src/headers/to_header_values.rs b/src/headers/to_header_values.rs index 1848cd74..37ef54d6 100644 --- a/src/headers/to_header_values.rs +++ b/src/headers/to_header_values.rs @@ -4,7 +4,7 @@ use std::iter; use std::option; use std::slice; -use crate::headers::{HeaderValue, HeaderValues, Values}; +use crate::headers::{Header, HeaderValue, HeaderValues, Values}; /// A trait for objects which can be converted or resolved to one or more `HeaderValue`s. pub trait ToHeaderValues { @@ -15,6 +15,14 @@ pub trait ToHeaderValues { fn to_header_values(&self) -> crate::Result; } +impl ToHeaderValues for T { + type Iter = option::IntoIter; + + fn to_header_values(&self) -> crate::Result { + Ok(Some(self.header_value()).into_iter()) + } +} + impl ToHeaderValues for HeaderValue { type Iter = option::IntoIter; diff --git a/src/other/date.rs b/src/other/date.rs index 94f1ac30..d01d6fab 100644 --- a/src/other/date.rs +++ b/src/other/date.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, DATE}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, DATE}; use crate::utils::HttpDate; use std::time::SystemTime; @@ -23,7 +23,7 @@ use std::time::SystemTime; /// let date = Date::new(now); /// /// let mut res = Response::new(200); -/// date.apply(&mut res); +/// res.insert_header(&date, &date); /// /// let date = Date::from_headers(res)?.unwrap(); /// @@ -71,19 +71,14 @@ impl Date { let at = date.into(); Ok(Some(Self { at })) } +} - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for Date { + fn header_name(&self) -> HeaderName { DATE } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let date: HttpDate = self.at.into(); let output = format!("{}", date); @@ -92,15 +87,6 @@ impl Date { } } -impl crate::headers::Header for Date { - fn header_name(&self) -> HeaderName { - DATE - } - fn header_value(&self) -> HeaderValue { - self.value() - } -} - impl From for SystemTime { fn from(date: Date) -> Self { date.at @@ -131,7 +117,7 @@ mod test { let date = Date::new(now); let mut headers = Headers::new(); - date.apply(&mut headers); + date.apply_header(&mut headers); let date = Date::from_headers(headers)?.unwrap(); diff --git a/src/other/expect.rs b/src/other/expect.rs index 0bcf1e19..ec6fdf6b 100644 --- a/src/other/expect.rs +++ b/src/other/expect.rs @@ -1,8 +1,7 @@ -use crate::ensure_eq_status; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPECT}; +use crate::headers::{HeaderName, HeaderValue, Headers, EXPECT}; +use crate::{ensure_eq_status, headers::Header}; use std::fmt::Debug; -use std::option; /// HTTP `Expect` header /// @@ -23,7 +22,7 @@ use std::option; /// let expect = Expect::new(); /// /// let mut res = Response::new(200); -/// expect.apply(&mut res); +/// res.insert_header(&expect, &expect); /// /// let expect = Expect::from_headers(res)?.unwrap(); /// assert_eq!(expect, Expect::new()); @@ -55,39 +54,16 @@ impl Expect { Ok(Some(Self { _priv: () })) } - - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(EXPECT, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - EXPECT - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let value = "100-continue"; - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(value.into()) } - } } -impl crate::headers::Header for Expect { +impl Header for Expect { fn header_name(&self) -> HeaderName { EXPECT } fn header_value(&self) -> HeaderValue { - self.value() - } -} - -impl ToHeaderValues for Expect { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + let value = "100-continue"; + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(value.into()) } } } @@ -101,7 +77,7 @@ mod test { let expect = Expect::new(); let mut headers = Headers::new(); - expect.apply(&mut headers); + expect.apply_header(&mut headers); let expect = Expect::from_headers(headers)?.unwrap(); assert_eq!(expect, Expect::new()); diff --git a/src/other/referer.rs b/src/other/referer.rs index e695b535..96a739b2 100644 --- a/src/other/referer.rs +++ b/src/other/referer.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, REFERER}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, REFERER}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -25,7 +25,7 @@ use std::convert::TryInto; /// let referer = Referer::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); -/// referer.apply(&mut res); +/// res.insert_header(&referer, &referer); /// /// let base_url = Url::parse("https://example.net/")?; /// let referer = Referer::from_headers(base_url, res)?.unwrap(); @@ -70,24 +70,6 @@ impl Referer { Ok(Some(Self { location: url })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - REFERER - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = self.location.to_string(); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the url. pub fn location(&self) -> &Url { &self.location @@ -104,12 +86,16 @@ impl Referer { } } -impl crate::headers::Header for Referer { +impl Header for Referer { fn header_name(&self) -> HeaderName { REFERER } + fn header_value(&self) -> HeaderValue { - self.value() + let output = self.location.to_string(); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -123,7 +109,7 @@ mod test { let referer = Referer::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); - referer.apply(&mut headers); + referer.apply_header(&mut headers); let base_url = Url::parse("https://example.net/")?; let referer = Referer::from_headers(base_url, headers)?.unwrap(); diff --git a/src/other/retry_after.rs b/src/other/retry_after.rs index bfe81f22..21404598 100644 --- a/src/other/retry_after.rs +++ b/src/other/retry_after.rs @@ -1,6 +1,6 @@ use std::time::{Duration, SystemTime, SystemTimeError}; -use crate::headers::{HeaderName, HeaderValue, Headers, RETRY_AFTER}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, RETRY_AFTER}; use crate::utils::{fmt_http_date, parse_http_date}; /// Indicate how long the user agent should wait before making a follow-up request. @@ -24,7 +24,7 @@ use crate::utils::{fmt_http_date, parse_http_date}; /// let retry = RetryAfter::new(Duration::from_secs(10)); /// /// let mut headers = Response::new(429); -/// retry.apply(&mut headers); +/// headers.insert_header(&retry, &retry); /// /// // Sleep for the duration, then try the task again. /// let retry = RetryAfter::from_headers(headers)?.unwrap(); @@ -87,19 +87,14 @@ impl RetryAfter { at.duration_since(earlier) } +} - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for RetryAfter { + fn header_name(&self) -> HeaderName { RETRY_AFTER } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let output = match self.inner { RetryDirective::Duration(dur) => format!("{}", dur.as_secs()), RetryDirective::SystemTime(at) => fmt_http_date(at), @@ -138,7 +133,7 @@ mod test { let retry = RetryAfter::new(Duration::from_secs(10)); let mut headers = Headers::new(); - retry.apply(&mut headers); + retry.apply_header(&mut headers); // `SystemTime::now` uses sub-second precision which means there's some // offset that's not encoded. @@ -157,7 +152,7 @@ mod test { let retry = RetryAfter::new_at(now + Duration::from_secs(10)); let mut headers = Headers::new(); - retry.apply(&mut headers); + retry.apply_header(&mut headers); // `SystemTime::now` uses sub-second precision which means there's some // offset that's not encoded. diff --git a/src/other/source_map.rs b/src/other/source_map.rs index 33e1527f..403275ec 100644 --- a/src/other/source_map.rs +++ b/src/other/source_map.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, SOURCE_MAP}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, SOURCE_MAP}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -22,7 +22,7 @@ use std::convert::TryInto; /// let source_map = SourceMap::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); -/// source_map.apply(&mut res); +/// res.insert_header(&source_map, &source_map); /// /// let base_url = Url::parse("https://example.net/")?; /// let source_map = SourceMap::from_headers(base_url, res)?.unwrap(); @@ -67,24 +67,6 @@ impl SourceMap { Ok(Some(Self { location: url })) } - /// Sets the header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(self.name(), self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - SOURCE_MAP - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = self.location.to_string(); - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Get the url. pub fn location(&self) -> &Url { &self.location @@ -101,12 +83,16 @@ impl SourceMap { } } -impl crate::headers::Header for SourceMap { +impl Header for SourceMap { fn header_name(&self) -> HeaderName { SOURCE_MAP } + fn header_value(&self) -> HeaderValue { - self.value() + let output = self.location.to_string(); + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -120,7 +106,7 @@ mod test { let source_map = SourceMap::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); - source_map.apply(&mut headers); + source_map.apply_header(&mut headers); let base_url = Url::parse("https://example.net/")?; let source_map = SourceMap::from_headers(base_url, headers)?.unwrap(); diff --git a/src/proxies/forwarded.rs b/src/proxies/forwarded.rs index 86f9dd84..1aebd3b9 100644 --- a/src/proxies/forwarded.rs +++ b/src/proxies/forwarded.rs @@ -1,5 +1,5 @@ use crate::{ - headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, FORWARDED}, + headers::{Header, HeaderName, HeaderValue, Headers, FORWARDED}, parse_utils::{parse_quoted_string, parse_token}, }; use std::{borrow::Cow, convert::TryFrom, fmt::Write, net::IpAddr}; @@ -42,9 +42,11 @@ impl<'a> Forwarded<'a> { /// /// # Examples /// ```rust - /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; - /// # fn main() -> Result<()> { - /// let mut request = Request::new(Get, Url::parse("http://_/")?); + /// # fn main() -> http_types::Result<()> { + /// use http_types::{Request}; + /// use http_types::proxies::Forwarded; + /// + /// let mut request = Request::get("http://_/"); /// request.insert_header( /// "Forwarded", /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# @@ -56,16 +58,18 @@ impl<'a> Forwarded<'a> { /// ``` /// /// ```rust - /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; - /// # fn main() -> Result<()> { - /// let mut request = Request::new(Get, Url::parse("http://_/")?); + /// # fn main() -> http_types::Result<()> { + /// use http_types::{headers::Header, Request}; + /// use http_types::proxies::Forwarded; + /// + /// let mut request = Request::get("http://_/"); /// request.insert_header("X-Forwarded-For", "192.0.2.43, 2001:db8:cafe::17, unknown"); /// request.insert_header("X-Forwarded-Proto", "https"); /// let forwarded = Forwarded::from_headers(&request)?.unwrap(); /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// assert_eq!(forwarded.proto(), Some("https")); /// assert_eq!( - /// forwarded.value()?, + /// forwarded.header_value(), /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// # Ok(()) } @@ -181,14 +185,16 @@ impl<'a> Forwarded<'a> { /// /// # Examples /// ```rust - /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; - /// # fn main() -> Result<()> { + /// # fn main() -> http_types::Result<()> { + /// use http_types::headers::Header; + /// use http_types::proxies::Forwarded; + /// /// let forwarded = Forwarded::parse( /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", FOR=unknown;proto=https"# /// )?; /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// assert_eq!( - /// forwarded.value()?, + /// forwarded.header_value(), /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// # Ok(()) } @@ -301,67 +307,6 @@ impl<'a> Forwarded<'a> { } } - /// Insert a header that represents this Forwarded. - /// - /// # Example - /// - /// ```rust - /// let mut response = http_types::Response::new(200); - /// let mut forwarded = http_types::proxies::Forwarded::new(); - /// forwarded.add_for("192.0.2.43"); - /// forwarded.add_for("[2001:db8:cafe::17]"); - /// forwarded.set_proto("https"); - /// forwarded.apply(&mut response); - /// assert_eq!(response["Forwarded"], r#"for=192.0.2.43, for="[2001:db8:cafe::17]";proto=https"#); - /// ``` - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(FORWARDED, self); - } - - /// Builds a Forwarded header as a String. - /// - /// # Example - /// - /// ```rust - /// # fn main() -> http_types::Result<()> { - /// let mut forwarded = http_types::proxies::Forwarded::new(); - /// forwarded.add_for("_haproxy"); - /// forwarded.add_for("[2001:db8:cafe::17]"); - /// forwarded.set_proto("https"); - /// assert_eq!(forwarded.value()?, r#"for=_haproxy, for="[2001:db8:cafe::17]";proto=https"#); - /// # Ok(()) } - /// ``` - pub fn value(&self) -> Result { - let mut buf = String::new(); - if let Some(by) = self.by() { - write!(&mut buf, "by={};", by)?; - } - - buf.push_str( - &self - .forwarded_for - .iter() - .map(|f| format!("for={}", format_value(f))) - .collect::>() - .join(", "), - ); - - buf.push(';'); - - if let Some(host) = self.host() { - write!(&mut buf, "host={};", host)?; - } - - if let Some(proto) = self.proto() { - write!(&mut buf, "proto={};", proto)?; - } - - // remove a trailing semicolon - buf.pop(); - - Ok(buf) - } - /// Builds a new empty Forwarded pub fn new() -> Self { Self::default() @@ -408,13 +353,38 @@ impl<'a> Forwarded<'a> { } } -impl<'a> crate::headers::Header for Forwarded<'a> { +impl<'a> Header for Forwarded<'a> { fn header_name(&self) -> HeaderName { FORWARDED } fn header_value(&self) -> HeaderValue { - // NOTE(yosh): This will never panic because we always write into a string. - let output = self.value().unwrap(); + let mut output = String::new(); + if let Some(by) = self.by() { + write!(&mut output, "by={};", by).unwrap(); + } + + output.push_str( + &self + .forwarded_for + .iter() + .map(|f| format!("for={}", format_value(f))) + .collect::>() + .join(", "), + ); + + output.push(';'); + + if let Some(host) = self.host() { + write!(&mut output, "host={};", host).unwrap(); + } + + if let Some(proto) = self.proto() { + write!(&mut output, "proto={};", proto).unwrap(); + } + + // remove a trailing semicolon + output.pop(); + // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } @@ -460,21 +430,7 @@ fn starts_with_ignore_case(start: &'static str, input: &str) -> bool { impl std::fmt::Display for Forwarded<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.value()?) - } -} - -impl ToHeaderValues for Forwarded<'_> { - type Iter = std::option::IntoIter; - fn to_header_values(&self) -> crate::Result { - Ok(self.value()?.to_header_values()?) - } -} - -impl ToHeaderValues for &Forwarded<'_> { - type Iter = std::option::IntoIter; - fn to_header_values(&self) -> crate::Result { - Ok(self.value()?.to_header_values()?) + f.write_str(&self.header_value().as_str()) } } diff --git a/src/security/timing_allow_origin.rs b/src/security/timing_allow_origin.rs index 0607955d..696243e5 100644 --- a/src/security/timing_allow_origin.rs +++ b/src/security/timing_allow_origin.rs @@ -10,14 +10,14 @@ //! ``` //! # fn main() -> http_types::Result<()> { //! # -//! use http_types::{Response, Url}; +//! use http_types::{Response, Url, headers::Header}; //! use http_types::security::TimingAllowOrigin; //! //! let mut origins = TimingAllowOrigin::new(); //! origins.push(Url::parse("https://example.com")?); //! //! let mut res = Response::new(200); -//! origins.apply(&mut res); +//! origins.apply_header(&mut res); //! //! let origins = TimingAllowOrigin::from_headers(res)?.unwrap(); //! let origin = origins.iter().next().unwrap(); @@ -26,13 +26,13 @@ //! # Ok(()) } //! ``` -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, TIMING_ALLOW_ORIGIN}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, TIMING_ALLOW_ORIGIN}; use crate::{Status, Url}; use std::fmt::Write; use std::fmt::{self, Debug}; use std::iter::Iterator; -use std::option; + use std::slice; /// Specify origins that are allowed to see values via the Resource Timing API. @@ -49,7 +49,7 @@ use std::slice; /// origins.push(Url::parse("https://example.com")?); /// /// let mut res = Response::new(200); -/// origins.apply(&mut res); +/// res.insert_header(&origins, &origins); /// /// let origins = TimingAllowOrigin::from_headers(res)?.unwrap(); /// let origin = origins.iter().next().unwrap(); @@ -106,37 +106,6 @@ impl TimingAllowOrigin { self.origins.push(origin.into()); } - /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(TIMING_ALLOW_ORIGIN, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - TIMING_ALLOW_ORIGIN - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, origin) in self.origins.iter().enumerate() { - match n { - 0 => write!(output, "{}", origin).unwrap(), - _ => write!(output, ", {}", origin).unwrap(), - }; - } - - if self.wildcard { - match output.len() { - 0 => write!(output, "*").unwrap(), - _ => write!(output, ", *").unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard @@ -162,12 +131,28 @@ impl TimingAllowOrigin { } } -impl crate::headers::Header for TimingAllowOrigin { +impl Header for TimingAllowOrigin { fn header_name(&self) -> HeaderName { TIMING_ALLOW_ORIGIN } fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, origin) in self.origins.iter().enumerate() { + match n { + 0 => write!(output, "{}", origin).unwrap(), + _ => write!(output, ", {}", origin).unwrap(), + }; + } + + if self.wildcard { + match output.len() { + 0 => write!(output, "*").unwrap(), + _ => write!(output, ", *").unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -260,14 +245,6 @@ impl<'a> Iterator for IterMut<'a> { } } -// Conversion from `AllowOrigin` -> `HeaderValue`. -impl ToHeaderValues for TimingAllowOrigin { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for TimingAllowOrigin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -289,7 +266,7 @@ mod test { origins.push(Url::parse("https://example.com")?); let mut headers = Headers::new(); - origins.apply(&mut headers); + origins.apply_header(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let origin = origins.iter().next().unwrap(); @@ -304,7 +281,7 @@ mod test { origins.push(Url::parse("https://mozilla.org/")?); let mut headers = Headers::new(); - origins.apply(&mut headers); + origins.apply_header(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let mut origins = origins.iter(); @@ -331,7 +308,7 @@ mod test { origins.set_wildcard(true); let mut headers = Headers::new(); - origins.apply(&mut headers); + origins.apply_header(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); assert_eq!(origins.wildcard(), true); diff --git a/src/server/allow.rs b/src/server/allow.rs index 0ebda194..9377a3f6 100644 --- a/src/server/allow.rs +++ b/src/server/allow.rs @@ -1,12 +1,12 @@ //! List the set of methods supported by a resource. -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ALLOW}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, ALLOW}; use crate::Method; use std::collections::{hash_set, HashSet}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; -use std::option; + use std::str::FromStr; /// List the set of methods supported by a resource. @@ -28,7 +28,7 @@ use std::str::FromStr; /// allow.insert(Method::Post); /// /// let mut res = Response::new(200); -/// allow.apply(&mut res); +/// res.insert_header(&allow, &allow); /// /// let allow = Allow::from_headers(res)?.unwrap(); /// assert!(allow.contains(Method::Put)); @@ -66,30 +66,6 @@ impl Allow { Ok(Some(Self { entries })) } - /// Sets the `Allow` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(ALLOW, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - ALLOW - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, method) in self.entries.iter().enumerate() { - match n { - 0 => write!(output, "{}", method).unwrap(), - _ => write!(output, ", {}", method).unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push a method into the set of methods. pub fn insert(&mut self, method: Method) { self.entries.insert(method); @@ -108,12 +84,21 @@ impl Allow { } } -impl crate::headers::Header for Allow { +impl Header for Allow { fn header_name(&self) -> HeaderName { ALLOW } fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, method) in self.entries.iter().enumerate() { + match n { + 0 => write!(output, "{}", method).unwrap(), + _ => write!(output, ", {}", method).unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -177,14 +162,6 @@ impl<'a> Iterator for Iter<'a> { } } -impl ToHeaderValues for Allow { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for Allow { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -207,7 +184,7 @@ mod test { allow.insert(Method::Post); let mut headers = Headers::new(); - allow.apply(&mut headers); + allow.apply_header(&mut headers); let allow = Allow::from_headers(headers)?.unwrap(); assert!(allow.contains(Method::Put)); diff --git a/src/trace/server_timing/mod.rs b/src/trace/server_timing/mod.rs index b94e0f0c..79c2be17 100644 --- a/src/trace/server_timing/mod.rs +++ b/src/trace/server_timing/mod.rs @@ -12,7 +12,7 @@ //! timings.push(Metric::new("server".to_owned(), None, None)?); //! //! let mut res = Response::new(200); -//! timings.apply(&mut res); +//! res.insert_header(&timings, &timings); //! //! let timings = ServerTiming::from_headers(res)?.unwrap(); //! let entry = timings.iter().next().unwrap(); @@ -27,13 +27,12 @@ mod parse; pub use metric::Metric; use parse::parse_header; -use std::convert::AsMut; use std::fmt::Write; use std::iter::Iterator; -use std::option; + use std::slice; -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, SERVER_TIMING}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, SERVER_TIMING}; /// Metrics and descriptions for the given request-response cycle. /// @@ -53,7 +52,7 @@ use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, SERVER_TI /// timings.push(Metric::new("server".to_owned(), None, None)?); /// /// let mut res = Response::new(200); -/// timings.apply(&mut res); +/// res.insert_header(&timings, &timings); /// /// let timings = ServerTiming::from_headers(res)?.unwrap(); /// let entry = timings.iter().next().unwrap(); @@ -86,31 +85,6 @@ impl ServerTiming { Ok(Some(Self { timings })) } - /// Sets the `Server-Timing` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(SERVER_TIMING, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - SERVER_TIMING - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let mut output = String::new(); - for (n, timing) in self.timings.iter().enumerate() { - let timing: HeaderValue = timing.clone().into(); - match n { - 0 => write!(output, "{}", timing).unwrap(), - _ => write!(output, ", {}", timing).unwrap(), - }; - } - - // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Push an entry into the list of entries. pub fn push(&mut self, entry: Metric) { self.timings.push(entry); @@ -131,12 +105,23 @@ impl ServerTiming { } } -impl crate::headers::Header for ServerTiming { +impl Header for ServerTiming { fn header_name(&self) -> HeaderName { SERVER_TIMING } + fn header_value(&self) -> HeaderValue { - self.value() + let mut output = String::new(); + for (n, timing) in self.timings.iter().enumerate() { + let timing: HeaderValue = timing.clone().into(); + match n { + 0 => write!(output, "{}", timing).unwrap(), + _ => write!(output, ", {}", timing).unwrap(), + }; + } + + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } @@ -229,14 +214,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for ServerTiming { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - #[cfg(test)] mod test { use super::*; @@ -248,7 +225,7 @@ mod test { timings.push(Metric::new("server".to_owned(), None, None)?); let mut headers = Headers::new(); - timings.apply(&mut headers); + timings.apply_header(&mut headers); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); @@ -262,7 +239,7 @@ mod test { timings.push(Metric::new("server".to_owned(), None, None)?); let mut headers = Headers::new(); - timings.apply(&mut headers); + timings.apply_header(&mut headers); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); diff --git a/src/trace/trace_context.rs b/src/trace/trace_context.rs index a2b39dba..5198416f 100644 --- a/src/trace/trace_context.rs +++ b/src/trace/trace_context.rs @@ -1,7 +1,7 @@ use rand::Rng; use std::fmt; -use crate::headers::{HeaderName, HeaderValue, Headers, TRACEPARENT}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, TRACEPARENT}; use crate::Status; /// Extract and apply [Trace-Context](https://w3c.github.io/trace-context/) headers. @@ -121,51 +121,6 @@ impl TraceContext { })) } - /// Add the traceparent header to the http headers - /// - /// # Examples - /// - /// ``` - /// # fn main() -> http_types::Result<()> { - /// # - /// use http_types::trace::TraceContext; - /// use http_types::{Request, Response, Url, Method}; - /// - /// let mut req = Request::new(Method::Get, Url::parse("https://example.com").unwrap()); - /// req.insert_header( - /// "traceparent", - /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" - /// ); - /// - /// let parent = TraceContext::from_headers(&req)?.unwrap(); - /// - /// let mut res = Response::new(200); - /// parent.apply(&mut res); - /// - /// let child = TraceContext::from_headers(&res)?.unwrap(); - /// - /// assert_eq!(child.version(), parent.version()); - /// assert_eq!(child.trace_id(), parent.trace_id()); - /// assert_eq!(child.parent_id(), Some(parent.id())); - /// # - /// # Ok(()) } - /// ``` - pub fn apply(&self, mut headers: impl AsMut) { - let headers = headers.as_mut(); - headers.insert(TRACEPARENT, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - TRACEPARENT - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - let output = format!("{}", self); - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } - } - /// Generate a child of the current TraceContext and return it. /// /// The child will have a new randomly genrated `id` and its `parent_id` will be set to the @@ -246,12 +201,14 @@ impl TraceContext { } } -impl crate::headers::Header for TraceContext { +impl Header for TraceContext { fn header_name(&self) -> HeaderName { TRACEPARENT } + fn header_value(&self) -> HeaderValue { - self.value() + let output = format!("{}", self); + unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } diff --git a/src/transfer/te.rs b/src/transfer/te.rs index c5456347..51279f01 100644 --- a/src/transfer/te.rs +++ b/src/transfer/te.rs @@ -1,10 +1,10 @@ -use crate::headers::{self, HeaderName, HeaderValue, Headers, ToHeaderValues}; +use crate::headers::{self, Header, HeaderName, HeaderValue, Headers}; use crate::transfer::{Encoding, EncodingProposal, TransferEncoding}; use crate::utils::sort_by_weight; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Write}; -use std::option; + use std::slice; /// Client header advertising the transfer encodings the user agent is willing to @@ -31,7 +31,7 @@ use std::slice; /// /// let mut res = Response::new(200); /// let encoding = te.negotiate(&[Encoding::Brotli, Encoding::Gzip])?; -/// encoding.apply(&mut res); +/// res.insert_header(&encoding, &encoding); /// /// assert_eq!(res["Transfer-Encoding"], "br"); /// # @@ -136,18 +136,27 @@ impl TE { Err(err) } - /// Sets the `Accept-Encoding` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(headers::TE, self.value()); + /// An iterator visiting all entries. + pub fn iter(&self) -> Iter<'_> { + Iter { + inner: self.entries.iter(), + } + } + + /// An iterator visiting all entries. + pub fn iter_mut(&mut self) -> IterMut<'_> { + IterMut { + inner: self.entries.iter_mut(), + } } +} - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { +impl Header for TE { + fn header_name(&self) -> HeaderName { headers::TE } - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { + fn header_value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = directive.clone().into(); @@ -167,29 +176,6 @@ impl TE { // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } - - /// An iterator visiting all entries. - pub fn iter(&self) -> Iter<'_> { - Iter { - inner: self.entries.iter(), - } - } - - /// An iterator visiting all entries. - pub fn iter_mut(&mut self) -> IterMut<'_> { - IterMut { - inner: self.entries.iter_mut(), - } - } -} - -impl crate::headers::Header for TE { - fn header_name(&self) -> HeaderName { - headers::TE - } - fn header_value(&self) -> HeaderValue { - self.value() - } } impl IntoIterator for TE { @@ -281,14 +267,6 @@ impl<'a> Iterator for IterMut<'a> { } } -impl ToHeaderValues for TE { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) - } -} - impl Debug for TE { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); @@ -311,7 +289,7 @@ mod test { accept.push(Encoding::Gzip); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); @@ -324,7 +302,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -338,7 +316,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -353,7 +331,7 @@ mod test { accept.push(Encoding::Brotli); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -370,7 +348,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); - accept.apply(&mut headers); + accept.apply_header(&mut headers); let mut accept = TE::from_headers(headers)?.unwrap(); accept.sort(); @@ -389,7 +367,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); - accept.apply(&mut res); + accept.apply_header(&mut res); let mut accept = TE::from_headers(res)?.unwrap(); accept.sort(); diff --git a/src/transfer/transfer_encoding.rs b/src/transfer/transfer_encoding.rs index e67827fc..7fede4aa 100644 --- a/src/transfer/transfer_encoding.rs +++ b/src/transfer/transfer_encoding.rs @@ -1,9 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, TRANSFER_ENCODING}; +use crate::headers::{Header, HeaderName, HeaderValue, Headers, TRANSFER_ENCODING}; use crate::transfer::{Encoding, EncodingProposal}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; -use std::option; /// The form of encoding used to safely transfer the payload body to the user. /// @@ -23,7 +22,7 @@ use std::option; /// let mut encoding = TransferEncoding::new(Encoding::Chunked); /// /// let mut res = Response::new(200); -/// encoding.apply(&mut res); +/// res.insert_header(&encoding, &encoding); /// /// let encoding = TransferEncoding::from_headers(res)?.unwrap(); /// assert_eq!(encoding, &Encoding::Chunked); @@ -59,41 +58,18 @@ impl TransferEncoding { Ok(Some(Self { inner })) } - /// Sets the `Content-Encoding` header. - pub fn apply(&self, mut headers: impl AsMut) { - headers.as_mut().insert(TRANSFER_ENCODING, self.value()); - } - - /// Get the `HeaderName`. - pub fn name(&self) -> HeaderName { - TRANSFER_ENCODING - } - - /// Get the `HeaderValue`. - pub fn value(&self) -> HeaderValue { - self.inner.into() - } - /// Access the encoding kind. pub fn encoding(&self) -> Encoding { self.inner } } -impl crate::headers::Header for TransferEncoding { +impl Header for TransferEncoding { fn header_name(&self) -> HeaderName { TRANSFER_ENCODING } fn header_value(&self) -> HeaderValue { - self.value() - } -} - -impl ToHeaderValues for TransferEncoding { - type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - // A HeaderValue will always convert into itself. - Ok(self.value().to_header_values().unwrap()) + self.inner.into() } }