diff --git a/Cargo.toml b/Cargo.toml index e63b41ec..267e3700 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["http", "types", "request", "response", "h2"] categories = ["asynchronous", "web-programming", "web-programming::http-client", "web-programming::http-server", "web-programming::websocket"] authors = ["Yoshua Wuyts "] readme = "README.md" -edition = "2018" +edition = "2021" [package.metadata.docs.rs] features = ["docs"] diff --git a/src/auth/authorization.rs b/src/auth/authorization.rs index cf2f2ddb..590eb50d 100644 --- a/src/auth/authorization.rs +++ b/src/auth/authorization.rs @@ -1,6 +1,6 @@ use crate::auth::AuthenticationScheme; use crate::bail_status as bail; -use crate::headers::{Header, HeaderName, HeaderValue, Headers, AUTHORIZATION}; +use crate::headers::{Field, FieldName, FieldValue, Fields, AUTHORIZATION}; /// Credentials to authenticate a user agent with a server. /// @@ -46,7 +46,7 @@ impl Authorization { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(AUTHORIZATION) { Some(headers) => headers, None => return Ok(None), @@ -92,23 +92,21 @@ impl Authorization { } } -impl Header for Authorization { - fn header_name(&self) -> HeaderName { - AUTHORIZATION - } +impl Field for Authorization { + const FIELD_NAME: FieldName = AUTHORIZATION; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = format!("{} {}", self.scheme, self.credentials); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { @@ -116,8 +114,8 @@ mod test { let credentials = "0xdeadbeef202020"; let authz = Authorization::new(scheme, credentials.into()); - let mut headers = Headers::new(); - authz.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(authz); let authz = Authorization::from_headers(headers)?.unwrap(); @@ -128,7 +126,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(AUTHORIZATION, "") .unwrap(); diff --git a/src/auth/basic_auth.rs b/src/auth/basic_auth.rs index 664cfcb5..677e9fc1 100644 --- a/src/auth/basic_auth.rs +++ b/src/auth/basic_auth.rs @@ -1,8 +1,8 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION}; +use crate::headers::{FieldName, FieldValue, Fields, AUTHORIZATION}; use crate::Status; use crate::{ auth::{AuthenticationScheme, Authorization}, - headers::Header, + headers::Field, }; use crate::{bail_status as bail, ensure_status as ensure}; @@ -53,7 +53,7 @@ impl BasicAuth { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let auth = match Authorization::from_headers(headers)? { Some(auth) => auth, None => return Ok(None), @@ -98,23 +98,21 @@ impl BasicAuth { } } -impl Header for BasicAuth { - fn header_name(&self) -> HeaderName { - AUTHORIZATION - } +impl Field for BasicAuth { + const FIELD_NAME: FieldName = AUTHORIZATION; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let scheme = AuthenticationScheme::Basic; let credentials = base64::encode(format!("{}:{}", self.username, self.password)); let auth = Authorization::new(scheme, credentials); - auth.header_value() + auth.field_value() } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { @@ -122,8 +120,8 @@ mod test { let password = "secret_fish!!"; let authz = BasicAuth::new(username, password); - let mut headers = Headers::new(); - authz.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(authz); let authz = BasicAuth::from_headers(headers)?.unwrap(); @@ -134,7 +132,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(AUTHORIZATION, "") .unwrap(); diff --git a/src/auth/www_authenticate.rs b/src/auth/www_authenticate.rs index bf9d7a05..40ce0bae 100644 --- a/src/auth/www_authenticate.rs +++ b/src/auth/www_authenticate.rs @@ -1,6 +1,6 @@ use crate::bail_status as bail; -use crate::headers::{HeaderName, HeaderValue, Headers, WWW_AUTHENTICATE}; -use crate::{auth::AuthenticationScheme, headers::Header}; +use crate::headers::{FieldName, FieldValue, Fields, WWW_AUTHENTICATE}; +use crate::{auth::AuthenticationScheme, headers::Field}; /// Define the authentication method that should be used to gain access to a /// resource. @@ -49,7 +49,7 @@ impl WwwAuthenticate { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(WWW_AUTHENTICATE) { Some(headers) => headers, None => return Ok(None), @@ -114,23 +114,21 @@ impl WwwAuthenticate { } } -impl Header for WwwAuthenticate { - fn header_name(&self) -> HeaderName { - WWW_AUTHENTICATE - } +impl Field for WwwAuthenticate { + const FIELD_NAME: FieldName = WWW_AUTHENTICATE; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { 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()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { @@ -138,8 +136,8 @@ mod test { let realm = "Access to the staging site"; let authz = WwwAuthenticate::new(scheme, realm.into()); - let mut headers = Headers::new(); - authz.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(authz); assert_eq!( headers["WWW-Authenticate"], @@ -155,7 +153,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(WWW_AUTHENTICATE, "") .unwrap(); diff --git a/src/cache/age.rs b/src/cache/age.rs index 0ee78a00..6262bc50 100644 --- a/src/cache/age.rs +++ b/src/cache/age.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, AGE}; +use crate::headers::{Field, FieldName, FieldValue, Fields, AGE}; use crate::Status; use std::fmt::Debug; @@ -52,7 +52,7 @@ impl Age { } /// Create an instance of `Age` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(AGE) { Some(headers) => headers, None => return Ok(None), @@ -69,30 +69,28 @@ impl Age { } } -impl Header for Age { - fn header_name(&self) -> HeaderName { - AGE - } +impl Field for Age { + const FIELD_NAME: FieldName = AGE; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = self.dur.as_secs().to_string(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let age = Age::new(Duration::from_secs(12)); - let mut headers = Headers::new(); - age.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(age); let age = Age::from_headers(headers)?.unwrap(); assert_eq!(age, Age::new(Duration::from_secs(12))); @@ -101,7 +99,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(AGE, "").unwrap(); let err = Age::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); diff --git a/src/cache/cache_control/cache_control.rs b/src/cache/cache_control/cache_control.rs index deffd6bd..52491a14 100644 --- a/src/cache/cache_control/cache_control.rs +++ b/src/cache/cache_control/cache_control.rs @@ -1,6 +1,6 @@ -use headers::Header; +use headers::Field; -use crate::headers::{HeaderName, HeaderValue, Headers, CACHE_CONTROL}; +use crate::headers::{FieldName, FieldValue, Fields, CACHE_CONTROL}; use crate::{cache::CacheDirective, headers}; use std::fmt::{self, Debug, Write}; @@ -42,7 +42,7 @@ impl CacheControl { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(CACHE_CONTROL) { Some(headers) => headers, @@ -81,14 +81,12 @@ impl CacheControl { } } -impl Header for CacheControl { - fn header_name(&self) -> HeaderName { - CACHE_CONTROL - } - fn header_value(&self) -> HeaderValue { +impl Field for CacheControl { + const FIELD_NAME: FieldName = CACHE_CONTROL; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { - let directive: HeaderValue = directive.clone().into(); + let directive: FieldValue = directive.clone().into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), @@ -96,7 +94,7 @@ impl Header for CacheControl { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } diff --git a/src/cache/cache_control/cache_directive.rs b/src/cache/cache_control/cache_directive.rs index 5e8b0b98..afcdb8b0 100644 --- a/src/cache/cache_control/cache_directive.rs +++ b/src/cache/cache_control/cache_directive.rs @@ -1,4 +1,4 @@ -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use crate::Status; use std::time::Duration; @@ -127,10 +127,10 @@ impl CacheDirective { } } -impl From for HeaderValue { +impl From for FieldValue { fn from(directive: CacheDirective) -> Self { use CacheDirective::*; - let h = |s: String| unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) }; + let h = |s: String| unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) }; match directive { Immutable => h("immutable".to_string()), diff --git a/src/cache/cache_control/mod.rs b/src/cache/cache_control/mod.rs index f9150363..28145b76 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::{Header, Headers, CACHE_CONTROL}; + use crate::headers::{Fields, CACHE_CONTROL}; #[test] fn smoke() -> crate::Result<()> { @@ -24,8 +24,8 @@ mod test { entries.push(CacheDirective::Immutable); entries.push(CacheDirective::NoStore); - let mut headers = Headers::new(); - entries.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(entries); let entries = CacheControl::from_headers(headers)?.unwrap(); let mut entries = entries.iter(); @@ -36,7 +36,7 @@ mod test { #[test] fn ignore_unkonwn_directives() -> crate::Result<()> { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(CACHE_CONTROL, "barrel_roll").unwrap(); let entries = CacheControl::from_headers(headers)?.unwrap(); let mut entries = entries.iter(); @@ -46,7 +46,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(CACHE_CONTROL, "min-fresh=0.9").unwrap(); // floats are not supported let err = CacheControl::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); diff --git a/src/cache/clear_site_data/mod.rs b/src/cache/clear_site_data/mod.rs index bb38ab80..b217e44f 100644 --- a/src/cache/clear_site_data/mod.rs +++ b/src/cache/clear_site_data/mod.rs @@ -1,7 +1,7 @@ //! Clear browsing data (cookies, storage, cache) associated with the //! requesting website -use crate::headers::{self, HeaderName, HeaderValue, Headers, CLEAR_SITE_DATA}; +use crate::headers::{self, FieldName, FieldValue, Fields, CLEAR_SITE_DATA}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; @@ -12,7 +12,7 @@ use std::str::FromStr; mod directive; pub use directive::ClearDirective; -use headers::Header; +use headers::Field; /// Clear browsing data (cookies, storage, cache) associated with the /// requesting website. @@ -60,7 +60,7 @@ impl ClearSiteData { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let header_values = match headers.as_ref().get(CLEAR_SITE_DATA) { Some(headers) => headers, @@ -211,12 +211,10 @@ impl Debug for ClearSiteData { } } -impl Header for ClearSiteData { - fn header_name(&self) -> HeaderName { - CLEAR_SITE_DATA - } +impl Field for ClearSiteData { + const FIELD_NAME: FieldName = CLEAR_SITE_DATA; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { @@ -233,7 +231,7 @@ impl Header for ClearSiteData { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -250,7 +248,7 @@ mod test { entries.push(ClearDirective::Cookies); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = ClearSiteData::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -266,7 +264,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = ClearSiteData::from_headers(res)?.unwrap(); assert!(entries.wildcard()); diff --git a/src/cache/expires.rs b/src/cache/expires.rs index 89c4e6f4..1496b7ea 100644 --- a/src/cache/expires.rs +++ b/src/cache/expires.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, EXPIRES}; +use crate::headers::{Field, FieldName, FieldValue, Fields, EXPIRES}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; @@ -56,7 +56,7 @@ impl Expires { } /// Create an instance of `Expires` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(EXPIRES) { Some(headers) => headers, None => return Ok(None), @@ -71,30 +71,28 @@ impl Expires { } } -impl Header for Expires { - fn header_name(&self) -> HeaderName { - EXPIRES - } - fn header_value(&self) -> HeaderValue { +impl Field for Expires { + const FIELD_NAME: FieldName = EXPIRES; + fn field_value(&self) -> FieldValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = Expires::new_at(time); - let mut headers = Headers::new(); - expires.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(expires); let expires = Expires::from_headers(headers)?.unwrap(); @@ -106,7 +104,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(EXPIRES, "").unwrap(); let err = Expires::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); diff --git a/src/conditional/etag.rs b/src/conditional/etag.rs index c7245f86..e13b5324 100644 --- a/src/conditional/etag.rs +++ b/src/conditional/etag.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, ETAG}; +use crate::headers::{Field, FieldName, FieldValue, Fields, ETAG}; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Display}; @@ -55,7 +55,7 @@ impl ETag { /// /// Only a single ETag per resource is assumed to exist. If multiple ETag /// headers are found the last one is used. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(ETAG) { Some(headers) => headers, None => return Ok(None), @@ -112,14 +112,12 @@ impl ETag { } } -impl Header for ETag { - fn header_name(&self) -> HeaderName { - ETAG - } - fn header_value(&self) -> HeaderValue { +impl Field for ETag { + const FIELD_NAME: FieldName = ETAG; + fn field_value(&self) -> FieldValue { let s = self.to_string(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(s.into()) } + unsafe { FieldValue::from_bytes_unchecked(s.into()) } } } @@ -135,14 +133,14 @@ impl Display for ETag { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let etag = ETag::new("0xcafebeef".to_string()); - let mut headers = Headers::new(); - etag.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(etag); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Strong(String::from("0xcafebeef"))); @@ -153,8 +151,8 @@ mod test { fn smoke_weak() -> crate::Result<()> { let etag = ETag::new_weak("0xcafebeef".to_string()); - let mut headers = Headers::new(); - etag.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(etag); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Weak(String::from("0xcafebeef"))); @@ -163,7 +161,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(ETAG, "").unwrap(); let err = ETag::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); @@ -178,7 +176,7 @@ mod test { } fn assert_entry_err(s: &str, msg: &str) { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(ETAG, s).unwrap(); let err = ETag::from_headers(headers).unwrap_err(); assert_eq!(format!("{}", err), msg); diff --git a/src/conditional/if_match.rs b/src/conditional/if_match.rs index 5f84c2bd..fed73c3a 100644 --- a/src/conditional/if_match.rs +++ b/src/conditional/if_match.rs @@ -1,7 +1,7 @@ //! Apply the HTTP method if the ETag matches. -use crate::headers::{HeaderName, HeaderValue, Headers, IF_MATCH}; -use crate::{conditional::ETag, headers::Header}; +use crate::headers::{FieldName, FieldValue, Fields, IF_MATCH}; +use crate::{conditional::ETag, headers::Field}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; @@ -51,7 +51,7 @@ impl IfMatch { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(IF_MATCH) { Some(headers) => headers, @@ -103,11 +103,9 @@ impl IfMatch { } } -impl Header for IfMatch { - fn header_name(&self) -> HeaderName { - IF_MATCH - } - fn header_value(&self) -> HeaderValue { +impl Field for IfMatch { + const FIELD_NAME: FieldName = IF_MATCH; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { @@ -124,7 +122,7 @@ impl Header for IfMatch { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -240,7 +238,7 @@ mod test { entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = IfMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -262,7 +260,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = IfMatch::from_headers(res)?.unwrap(); assert!(entries.wildcard()); diff --git a/src/conditional/if_modified_since.rs b/src/conditional/if_modified_since.rs index e5566a12..8c6d0d27 100644 --- a/src/conditional/if_modified_since.rs +++ b/src/conditional/if_modified_since.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, IF_MODIFIED_SINCE}; +use crate::headers::{Field, FieldName, FieldValue, Fields, IF_MODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; @@ -52,7 +52,7 @@ impl IfModifiedSince { } /// Create an instance of `IfModifiedSince` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(IF_MODIFIED_SINCE) { Some(headers) => headers, None => return Ok(None), @@ -67,22 +67,20 @@ impl IfModifiedSince { } } -impl Header for IfModifiedSince { - fn header_name(&self) -> HeaderName { - IF_MODIFIED_SINCE - } - fn header_value(&self) -> HeaderValue { +impl Field for IfModifiedSince { + const FIELD_NAME: FieldName = IF_MODIFIED_SINCE; + fn field_value(&self) -> FieldValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; use std::time::Duration; #[test] @@ -90,8 +88,8 @@ mod test { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = IfModifiedSince::new(time); - let mut headers = Headers::new(); - expires.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(expires); let expires = IfModifiedSince::from_headers(headers)?.unwrap(); @@ -103,7 +101,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(IF_MODIFIED_SINCE, "") .unwrap(); diff --git a/src/conditional/if_none_match.rs b/src/conditional/if_none_match.rs index b4b37af9..4b26abc5 100644 --- a/src/conditional/if_none_match.rs +++ b/src/conditional/if_none_match.rs @@ -3,8 +3,8 @@ //! This is used to update caches or to prevent uploading a new resource when //! one already exists. -use crate::headers::{HeaderName, HeaderValue, Headers, IF_NONE_MATCH}; -use crate::{conditional::ETag, headers::Header}; +use crate::headers::{FieldName, FieldValue, Fields, IF_NONE_MATCH}; +use crate::{conditional::ETag, headers::Field}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; @@ -57,7 +57,7 @@ impl IfNoneMatch { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(IF_NONE_MATCH) { Some(headers) => headers, @@ -109,11 +109,9 @@ impl IfNoneMatch { } } -impl Header for IfNoneMatch { - fn header_name(&self) -> HeaderName { - IF_NONE_MATCH - } - fn header_value(&self) -> HeaderValue { +impl Field for IfNoneMatch { + const FIELD_NAME: FieldName = IF_NONE_MATCH; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { @@ -130,7 +128,7 @@ impl Header for IfNoneMatch { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -246,7 +244,7 @@ mod test { entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = IfNoneMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -268,7 +266,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = IfNoneMatch::from_headers(res)?.unwrap(); assert!(entries.wildcard()); diff --git a/src/conditional/if_unmodified_since.rs b/src/conditional/if_unmodified_since.rs index 2d964271..0891a6e9 100644 --- a/src/conditional/if_unmodified_since.rs +++ b/src/conditional/if_unmodified_since.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, IF_UNMODIFIED_SINCE}; +use crate::headers::{Field, FieldName, FieldValue, Fields, IF_UNMODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; @@ -52,7 +52,7 @@ impl IfUnmodifiedSince { } /// Create an instance of `IfUnmodifiedSince` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(IF_UNMODIFIED_SINCE) { Some(headers) => headers, None => return Ok(None), @@ -67,22 +67,20 @@ impl IfUnmodifiedSince { } } -impl Header for IfUnmodifiedSince { - fn header_name(&self) -> HeaderName { - IF_UNMODIFIED_SINCE - } - fn header_value(&self) -> HeaderValue { +impl Field for IfUnmodifiedSince { + const FIELD_NAME: FieldName = IF_UNMODIFIED_SINCE; + fn field_value(&self) -> FieldValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; use std::time::Duration; #[test] @@ -90,8 +88,8 @@ mod test { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = IfUnmodifiedSince::new(time); - let mut headers = Headers::new(); - expires.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(expires); let expires = IfUnmodifiedSince::from_headers(headers)?.unwrap(); @@ -103,7 +101,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(IF_UNMODIFIED_SINCE, "") .unwrap(); diff --git a/src/conditional/last_modified.rs b/src/conditional/last_modified.rs index dc71fc1b..92eb6f05 100644 --- a/src/conditional/last_modified.rs +++ b/src/conditional/last_modified.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, LAST_MODIFIED}; +use crate::headers::{Field, FieldName, FieldValue, Fields, LAST_MODIFIED}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; @@ -51,7 +51,7 @@ impl LastModified { } /// Create an instance of `LastModified` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(LAST_MODIFIED) { Some(headers) => headers, None => return Ok(None), @@ -66,22 +66,20 @@ impl LastModified { } } -impl Header for LastModified { - fn header_name(&self) -> HeaderName { - LAST_MODIFIED - } - fn header_value(&self) -> HeaderValue { +impl Field for LastModified { + const FIELD_NAME: FieldName = LAST_MODIFIED; + fn field_value(&self) -> FieldValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; use std::time::Duration; #[test] @@ -89,8 +87,8 @@ mod test { let time = SystemTime::now() + Duration::from_secs(5 * 60); let last_modified = LastModified::new(time); - let mut headers = Headers::new(); - last_modified.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(last_modified); let last_modified = LastModified::from_headers(headers)?.unwrap(); @@ -102,7 +100,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(LAST_MODIFIED, "") .unwrap(); diff --git a/src/conditional/vary.rs b/src/conditional/vary.rs index 9023811a..503a640b 100644 --- a/src/conditional/vary.rs +++ b/src/conditional/vary.rs @@ -1,6 +1,6 @@ //! Apply the HTTP method if the ETag matches. -use crate::headers::{Header, HeaderName, HeaderValue, Headers, VARY}; +use crate::headers::{Field, FieldName, FieldValue, Fields, VARY}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; @@ -37,7 +37,7 @@ use std::str::FromStr; /// # Ok(()) } /// ``` pub struct Vary { - entries: Vec, + entries: Vec, wildcard: bool, } @@ -51,7 +51,7 @@ impl Vary { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(VARY) { Some(headers) => headers, @@ -66,7 +66,7 @@ impl Vary { wildcard = true; continue; } - let entry = HeaderName::from_str(part.trim())?; + let entry = FieldName::from_str(part.trim())?; entries.push(entry); } } @@ -85,7 +85,7 @@ impl Vary { } /// Push a directive into the list of entries. - pub fn push(&mut self, directive: impl Into) -> crate::Result<()> { + pub fn push(&mut self, directive: impl Into) -> crate::Result<()> { self.entries.push(directive.into()); Ok(()) } @@ -105,15 +105,13 @@ impl Vary { } } -impl Header for Vary { - fn header_name(&self) -> HeaderName { - VARY - } +impl Field for Vary { + const FIELD_NAME: FieldName = VARY; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, name) in self.entries.iter().enumerate() { - let directive: HeaderValue = name + let directive: FieldValue = name .as_str() .parse() .expect("Could not convert a HeaderName into a HeaderValue"); @@ -131,12 +129,12 @@ impl Header for Vary { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } impl IntoIterator for Vary { - type Item = HeaderName; + type Item = FieldName; type IntoIter = IntoIter; #[inline] @@ -148,7 +146,7 @@ impl IntoIterator for Vary { } impl<'a> IntoIterator for &'a Vary { - type Item = &'a HeaderName; + type Item = &'a FieldName; type IntoIter = Iter<'a>; #[inline] @@ -158,7 +156,7 @@ impl<'a> IntoIterator for &'a Vary { } impl<'a> IntoIterator for &'a mut Vary { - type Item = &'a mut HeaderName; + type Item = &'a mut FieldName; type IntoIter = IterMut<'a>; #[inline] @@ -170,11 +168,11 @@ impl<'a> IntoIterator for &'a mut Vary { /// A borrowing iterator over entries in `Vary`. #[derive(Debug)] pub struct IntoIter { - inner: std::vec::IntoIter, + inner: std::vec::IntoIter, } impl Iterator for IntoIter { - type Item = HeaderName; + type Item = FieldName; fn next(&mut self) -> Option { self.inner.next() @@ -189,11 +187,11 @@ impl Iterator for IntoIter { /// A lending iterator over entries in `Vary`. #[derive(Debug)] pub struct Iter<'a> { - inner: slice::Iter<'a, HeaderName>, + inner: slice::Iter<'a, FieldName>, } impl<'a> Iterator for Iter<'a> { - type Item = &'a HeaderName; + type Item = &'a FieldName; fn next(&mut self) -> Option { self.inner.next() @@ -208,11 +206,11 @@ impl<'a> Iterator for Iter<'a> { /// A mutable iterator over entries in `Vary`. #[derive(Debug)] pub struct IterMut<'a> { - inner: slice::IterMut<'a, HeaderName>, + inner: slice::IterMut<'a, FieldName>, } impl<'a> Iterator for IterMut<'a> { - type Item = &'a mut HeaderName; + type Item = &'a mut FieldName; fn next(&mut self) -> Option { self.inner.next() @@ -247,7 +245,7 @@ mod test { entries.push("Accept-Encoding")?; let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = Vary::from_headers(res)?.unwrap(); let mut entries = entries.iter(); @@ -263,7 +261,7 @@ mod test { entries.set_wildcard(true); let mut res = Response::new(200); - entries.apply_header(&mut res); + res.insert_typed_header(entries); let entries = Vary::from_headers(res)?.unwrap(); assert!(entries.wildcard()); diff --git a/src/content/accept.rs b/src/content/accept.rs index 87dad909..84fb4ca3 100644 --- a/src/content/accept.rs +++ b/src/content/accept.rs @@ -1,11 +1,11 @@ //! Client header advertising which media types the client is able to understand. -use crate::headers::{HeaderName, HeaderValue, Headers, ACCEPT}; +use crate::headers::{FieldName, FieldValue, Fields, ACCEPT}; use crate::mime::Mime; use crate::utils::sort_by_weight; use crate::{ content::{ContentType, MediaTypeProposal}, - headers::Header, + headers::Field, }; use crate::{Error, StatusCode}; @@ -63,7 +63,7 @@ impl Accept { } /// Create an instance of `Accept` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(ACCEPT) { Some(headers) => headers, @@ -161,14 +161,12 @@ impl Accept { } } -impl Header for Accept { - fn header_name(&self) -> HeaderName { - ACCEPT - } - fn header_value(&self) -> HeaderValue { +impl Field for Accept { + const FIELD_NAME: FieldName = ACCEPT; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { - let directive: HeaderValue = directive.clone().into(); + let directive: FieldValue = directive.clone().into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), @@ -183,7 +181,7 @@ impl Header for Accept { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -298,7 +296,7 @@ mod test { accept.push(mime::HTML); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = Accept::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), mime::HTML); @@ -311,7 +309,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -325,7 +323,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -340,7 +338,7 @@ mod test { accept.push(mime::XML); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = Accept::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -357,7 +355,7 @@ mod test { accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let mut accept = Accept::from_headers(headers)?.unwrap(); accept.sort(); @@ -376,7 +374,7 @@ mod test { accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut res = Response::new(200); - accept.apply_header(&mut res); + res.insert_typed_header(accept); 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 5c469a40..e2c969ec 100644 --- a/src/content/accept_encoding.rs +++ b/src/content/accept_encoding.rs @@ -1,10 +1,10 @@ //! Client header advertising available compression algorithms. -use crate::headers::{HeaderName, HeaderValue, Headers, ACCEPT_ENCODING}; +use crate::headers::{FieldName, FieldValue, Fields, ACCEPT_ENCODING}; use crate::utils::sort_by_weight; use crate::{ content::{ContentEncoding, Encoding, EncodingProposal}, - headers::Header, + headers::Field, }; use crate::{Error, StatusCode}; @@ -54,7 +54,7 @@ impl AcceptEncoding { } /// Create an instance of `AcceptEncoding` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(ACCEPT_ENCODING) { Some(headers) => headers, @@ -153,15 +153,13 @@ impl AcceptEncoding { } } -impl Header for AcceptEncoding { - fn header_name(&self) -> HeaderName { - ACCEPT_ENCODING - } +impl Field for AcceptEncoding { + const FIELD_NAME: FieldName = ACCEPT_ENCODING; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { - let directive: HeaderValue = (*directive).into(); + let directive: FieldValue = (*directive).into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), @@ -176,7 +174,7 @@ impl Header for AcceptEncoding { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -291,7 +289,7 @@ mod test { accept.push(Encoding::Gzip); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); @@ -304,7 +302,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -318,7 +316,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -333,7 +331,7 @@ mod test { accept.push(Encoding::Brotli); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -350,7 +348,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let mut accept = AcceptEncoding::from_headers(headers)?.unwrap(); accept.sort(); @@ -369,7 +367,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); - accept.apply_header(&mut res); + res.insert_typed_header(accept); 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 bbab123c..2da82b95 100644 --- a/src/content/content_encoding.rs +++ b/src/content/content_encoding.rs @@ -1,9 +1,9 @@ //! Specify the compression algorithm. -use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_ENCODING}; +use crate::headers::{FieldName, FieldValue, Fields, CONTENT_ENCODING}; use crate::{ content::{Encoding, EncodingProposal}, - headers::Header, + headers::Field, }; use std::fmt::{self, Debug}; @@ -43,7 +43,7 @@ impl ContentEncoding { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_ENCODING) { Some(headers) => headers, None => return Ok(None), @@ -67,11 +67,9 @@ impl ContentEncoding { } } -impl Header for ContentEncoding { - fn header_name(&self) -> HeaderName { - CONTENT_ENCODING - } - fn header_value(&self) -> HeaderValue { +impl Field for ContentEncoding { + const FIELD_NAME: FieldName = CONTENT_ENCODING; + fn field_value(&self) -> FieldValue { self.inner.into() } } diff --git a/src/content/content_length.rs b/src/content/content_length.rs index 4bae42f2..3ef3334e 100644 --- a/src/content/content_length.rs +++ b/src/content/content_length.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, CONTENT_LENGTH}; +use crate::headers::{Field, FieldName, FieldValue, Fields, CONTENT_LENGTH}; use crate::Status; /// The size of the entity-body, in bytes, sent to the recipient. @@ -38,7 +38,7 @@ impl ContentLength { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_LENGTH) { Some(headers) => headers, None => return Ok(None), @@ -62,29 +62,27 @@ impl ContentLength { } } -impl Header for ContentLength { - fn header_name(&self) -> HeaderName { - CONTENT_LENGTH - } - fn header_value(&self) -> HeaderValue { +impl Field for ContentLength { + const FIELD_NAME: FieldName = CONTENT_LENGTH; + fn field_value(&self) -> FieldValue { let output = format!("{}", self.length); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let content_len = ContentLength::new(12); - let mut headers = Headers::new(); - content_len.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(content_len); let content_len = ContentLength::from_headers(headers)?.unwrap(); assert_eq!(content_len.len(), 12); @@ -93,7 +91,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(CONTENT_LENGTH, "") .unwrap(); diff --git a/src/content/content_location.rs b/src/content/content_location.rs index 61c09135..d0cee5de 100644 --- a/src/content/content_location.rs +++ b/src/content/content_location.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, CONTENT_LOCATION}; +use crate::headers::{Field, FieldName, FieldValue, Fields, CONTENT_LOCATION}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -42,7 +42,7 @@ impl ContentLocation { } /// Create a new instance from headers. - pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> + pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, @@ -81,29 +81,27 @@ impl ContentLocation { } } -impl Header for ContentLocation { - fn header_name(&self) -> HeaderName { - CONTENT_LOCATION - } - fn header_value(&self) -> HeaderValue { +impl Field for ContentLocation { + const FIELD_NAME: FieldName = CONTENT_LOCATION; + fn field_value(&self) -> FieldValue { let output = self.url.to_string(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let content_location = ContentLocation::new(Url::parse("https://example.net/test.json")?); - let mut headers = Headers::new(); - content_location.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(content_location); let content_location = ContentLocation::from_headers(Url::parse("https://example.net/").unwrap(), headers)? @@ -117,7 +115,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(CONTENT_LOCATION, "htt://") .unwrap(); diff --git a/src/content/content_type.rs b/src/content/content_type.rs index 9397165c..51c3cc1b 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::{Header, HeaderName, HeaderValue, Headers, CONTENT_TYPE}; +use crate::headers::{Field, FieldName, FieldValue, Fields, CONTENT_TYPE}; use crate::mime::Mime; /// Indicate the media type of a resource's content. @@ -57,7 +57,7 @@ impl ContentType { /// order to always return fully qualified URLs, a base URL must be passed to /// reference the current environment. In HTTP/1.1 and above this value can /// always be determined from the request. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_TYPE) { Some(headers) => headers, None => return Ok(None), @@ -75,14 +75,12 @@ impl ContentType { } } -impl Header for ContentType { - fn header_name(&self) -> HeaderName { - CONTENT_TYPE - } - fn header_value(&self) -> HeaderValue { +impl Field for ContentType { + const FIELD_NAME: FieldName = CONTENT_TYPE; + fn field_value(&self) -> FieldValue { let output = format!("{}", self.media_type); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -107,18 +105,18 @@ impl From for ContentType { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let ct = ContentType::new(Mime::from_str("text/*")?); - let mut headers = Headers::new(); - ct.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(ct); let ct = ContentType::from_headers(headers)?.unwrap(); assert_eq!( - ct.header_value(), + ct.field_value(), format!("{}", Mime::from_str("text/*")?).as_str() ); Ok(()) @@ -126,7 +124,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(CONTENT_TYPE, "") .unwrap(); diff --git a/src/content/encoding.rs b/src/content/encoding.rs index 992194ef..05d836cd 100644 --- a/src/content/encoding.rs +++ b/src/content/encoding.rs @@ -1,4 +1,4 @@ -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use std::fmt::{self, Display}; /// Available compression algorithms. @@ -50,9 +50,9 @@ impl Display for Encoding { } } -impl From for HeaderValue { +impl From for FieldValue { fn from(directive: Encoding) -> Self { let s = directive.to_string(); - unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) } } } diff --git a/src/content/encoding_proposal.rs b/src/content/encoding_proposal.rs index 7d329447..92abc99f 100644 --- a/src/content/encoding_proposal.rs +++ b/src/content/encoding_proposal.rs @@ -1,6 +1,6 @@ use crate::content::Encoding; use crate::ensure; -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use crate::utils::parse_weight; use std::cmp::{Ordering, PartialEq}; @@ -108,13 +108,13 @@ impl PartialOrd for EncodingProposal { } } -impl From for HeaderValue { - fn from(entry: EncodingProposal) -> HeaderValue { +impl From for FieldValue { + fn from(entry: EncodingProposal) -> FieldValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.encoding, weight), None => entry.encoding.to_string(), }; - unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) } } } diff --git a/src/content/media_type_proposal.rs b/src/content/media_type_proposal.rs index 622558de..795fadf3 100644 --- a/src/content/media_type_proposal.rs +++ b/src/content/media_type_proposal.rs @@ -1,5 +1,5 @@ use crate::ensure; -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use crate::mime::Mime; use std::ops::{Deref, DerefMut}; @@ -125,13 +125,13 @@ impl PartialOrd for MediaTypeProposal { } } -impl From for HeaderValue { - fn from(entry: MediaTypeProposal) -> HeaderValue { +impl From for FieldValue { + fn from(entry: MediaTypeProposal) -> FieldValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.media_type, weight), None => entry.media_type.to_string(), }; - unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) } } } diff --git a/src/headers/constants.rs b/src/headers/constants.rs index b332b26e..2aa86eff 100644 --- a/src/headers/constants.rs +++ b/src/headers/constants.rs @@ -1,188 +1,188 @@ -use super::HeaderName; +use super::FieldName; /// The `Content-Encoding` Header -pub const CONTENT_ENCODING: HeaderName = HeaderName::from_lowercase_str("content-encoding"); +pub const CONTENT_ENCODING: FieldName = FieldName::from_lowercase_str("content-encoding"); /// The `Content-Language` Header -pub const CONTENT_LANGUAGE: HeaderName = HeaderName::from_lowercase_str("content-language"); +pub const CONTENT_LANGUAGE: FieldName = FieldName::from_lowercase_str("content-language"); /// The `Content-Length` Header -pub const CONTENT_LENGTH: HeaderName = HeaderName::from_lowercase_str("content-length"); +pub const CONTENT_LENGTH: FieldName = FieldName::from_lowercase_str("content-length"); /// The `Content-Location` Header -pub const CONTENT_LOCATION: HeaderName = HeaderName::from_lowercase_str("content-location"); +pub const CONTENT_LOCATION: FieldName = FieldName::from_lowercase_str("content-location"); /// The `Content-MD5` Header -pub const CONTENT_MD5: HeaderName = HeaderName::from_lowercase_str("content-md5"); +pub const CONTENT_MD5: FieldName = FieldName::from_lowercase_str("content-md5"); /// The `Content-Range` Header -pub const CONTENT_RANGE: HeaderName = HeaderName::from_lowercase_str("content-range"); +pub const CONTENT_RANGE: FieldName = FieldName::from_lowercase_str("content-range"); /// The `Content-Type` Header -pub const CONTENT_TYPE: HeaderName = HeaderName::from_lowercase_str("content-type"); +pub const CONTENT_TYPE: FieldName = FieldName::from_lowercase_str("content-type"); /// The `Cookie` Header -pub const COOKIE: HeaderName = HeaderName::from_lowercase_str("cookie"); +pub const COOKIE: FieldName = FieldName::from_lowercase_str("cookie"); /// The `Set-Cookie` Header -pub const SET_COOKIE: HeaderName = HeaderName::from_lowercase_str("set-cookie"); +pub const SET_COOKIE: FieldName = FieldName::from_lowercase_str("set-cookie"); /// The `Transfer-Encoding` Header -pub const TRANSFER_ENCODING: HeaderName = HeaderName::from_lowercase_str("transfer-encoding"); +pub const TRANSFER_ENCODING: FieldName = FieldName::from_lowercase_str("transfer-encoding"); /// The `Date` Header -pub const DATE: HeaderName = HeaderName::from_lowercase_str("date"); +pub const DATE: FieldName = FieldName::from_lowercase_str("date"); /// The `Host` Header -pub const HOST: HeaderName = HeaderName::from_lowercase_str("host"); +pub const HOST: FieldName = FieldName::from_lowercase_str("host"); /// The `Origin` Header -pub const ORIGIN: HeaderName = HeaderName::from_lowercase_str("origin"); +pub const ORIGIN: FieldName = FieldName::from_lowercase_str("origin"); /// The `access-control-max-age` Header -pub const ACCESS_CONTROL_MAX_AGE: HeaderName = - HeaderName::from_lowercase_str("access-control-max-age"); +pub const ACCESS_CONTROL_MAX_AGE: FieldName = + FieldName::from_lowercase_str("access-control-max-age"); /// The `access-control-allow-origin` Header -pub const ACCESS_CONTROL_ALLOW_ORIGIN: HeaderName = - HeaderName::from_lowercase_str("access-control-allow-origin"); +pub const ACCESS_CONTROL_ALLOW_ORIGIN: FieldName = + FieldName::from_lowercase_str("access-control-allow-origin"); /// The `access-control-allow-headers` Header -pub const ACCESS_CONTROL_ALLOW_HEADERS: HeaderName = - HeaderName::from_lowercase_str("access-control-allow-headers"); +pub const ACCESS_CONTROL_ALLOW_HEADERS: FieldName = + FieldName::from_lowercase_str("access-control-allow-headers"); /// The `access-control-allow-methods` Header -pub const ACCESS_CONTROL_ALLOW_METHODS: HeaderName = - HeaderName::from_lowercase_str("access-control-allow-methods"); +pub const ACCESS_CONTROL_ALLOW_METHODS: FieldName = + FieldName::from_lowercase_str("access-control-allow-methods"); /// The `access-control-expose-headers` Header -pub const ACCESS_CONTROL_EXPOSE_HEADERS: HeaderName = - HeaderName::from_lowercase_str("access-control-expose-headers"); +pub const ACCESS_CONTROL_EXPOSE_HEADERS: FieldName = + FieldName::from_lowercase_str("access-control-expose-headers"); /// The `access-control-request-method` Header -pub const ACCESS_CONTROL_REQUEST_METHOD: HeaderName = - HeaderName::from_lowercase_str("access-control-request-method"); +pub const ACCESS_CONTROL_REQUEST_METHOD: FieldName = + FieldName::from_lowercase_str("access-control-request-method"); /// The `access-control-request-headers` Header -pub const ACCESS_CONTROL_REQUEST_HEADERS: HeaderName = - HeaderName::from_lowercase_str("access-control-request-headers"); +pub const ACCESS_CONTROL_REQUEST_HEADERS: FieldName = + FieldName::from_lowercase_str("access-control-request-headers"); /// The `access-control-allow-credentials` Header -pub const ACCESS_CONTROL_ALLOW_CREDENTIALS: HeaderName = - HeaderName::from_lowercase_str("access-control-allow-credentials"); +pub const ACCESS_CONTROL_ALLOW_CREDENTIALS: FieldName = + FieldName::from_lowercase_str("access-control-allow-credentials"); /// The `Accept` Header -pub const ACCEPT: HeaderName = HeaderName::from_lowercase_str("accept"); +pub const ACCEPT: FieldName = FieldName::from_lowercase_str("accept"); /// The `Accept-Charset` Header -pub const ACCEPT_CHARSET: HeaderName = HeaderName::from_lowercase_str("accept-charset"); +pub const ACCEPT_CHARSET: FieldName = FieldName::from_lowercase_str("accept-charset"); /// The `Accept-Encoding` Header -pub const ACCEPT_ENCODING: HeaderName = HeaderName::from_lowercase_str("accept-encoding"); +pub const ACCEPT_ENCODING: FieldName = FieldName::from_lowercase_str("accept-encoding"); /// The `Accept-Language` Header -pub const ACCEPT_LANGUAGE: HeaderName = HeaderName::from_lowercase_str("accept-language"); +pub const ACCEPT_LANGUAGE: FieldName = FieldName::from_lowercase_str("accept-language"); /// The `Accept-Ranges` Header -pub const ACCEPT_RANGES: HeaderName = HeaderName::from_lowercase_str("accept-ranges"); +pub const ACCEPT_RANGES: FieldName = FieldName::from_lowercase_str("accept-ranges"); /// The `Age` Header -pub const AGE: HeaderName = HeaderName::from_lowercase_str("age"); +pub const AGE: FieldName = FieldName::from_lowercase_str("age"); /// The `Allow` Header -pub const ALLOW: HeaderName = HeaderName::from_lowercase_str("allow"); +pub const ALLOW: FieldName = FieldName::from_lowercase_str("allow"); /// The `Authorization` Header -pub const AUTHORIZATION: HeaderName = HeaderName::from_lowercase_str("authorization"); +pub const AUTHORIZATION: FieldName = FieldName::from_lowercase_str("authorization"); /// The `Cache-Control` Header -pub const CACHE_CONTROL: HeaderName = HeaderName::from_lowercase_str("cache-control"); +pub const CACHE_CONTROL: FieldName = FieldName::from_lowercase_str("cache-control"); /// The `Clear-Site-Data` Header -pub const CLEAR_SITE_DATA: HeaderName = HeaderName::from_lowercase_str("clear-site-data"); +pub const CLEAR_SITE_DATA: FieldName = FieldName::from_lowercase_str("clear-site-data"); /// The `Connection` Header -pub const CONNECTION: HeaderName = HeaderName::from_lowercase_str("connection"); +pub const CONNECTION: FieldName = FieldName::from_lowercase_str("connection"); /// The `ETag` Header -pub const ETAG: HeaderName = HeaderName::from_lowercase_str("etag"); +pub const ETAG: FieldName = FieldName::from_lowercase_str("etag"); /// The `Expect` Header -pub const EXPECT: HeaderName = HeaderName::from_lowercase_str("expect"); +pub const EXPECT: FieldName = FieldName::from_lowercase_str("expect"); /// The `Expires` Header -pub const EXPIRES: HeaderName = HeaderName::from_lowercase_str("expires"); +pub const EXPIRES: FieldName = FieldName::from_lowercase_str("expires"); /// The `Forwarded` Header -pub const FORWARDED: HeaderName = HeaderName::from_lowercase_str("forwarded"); +pub const FORWARDED: FieldName = FieldName::from_lowercase_str("forwarded"); /// The `From` Header -pub const FROM: HeaderName = HeaderName::from_lowercase_str("from"); +pub const FROM: FieldName = FieldName::from_lowercase_str("from"); /// The `If-Match` Header -pub const IF_MATCH: HeaderName = HeaderName::from_lowercase_str("if-match"); +pub const IF_MATCH: FieldName = FieldName::from_lowercase_str("if-match"); /// The `If-Modified-Since` Header -pub const IF_MODIFIED_SINCE: HeaderName = HeaderName::from_lowercase_str("if-modified-since"); +pub const IF_MODIFIED_SINCE: FieldName = FieldName::from_lowercase_str("if-modified-since"); /// The `If-None-Match` Header -pub const IF_NONE_MATCH: HeaderName = HeaderName::from_lowercase_str("if-none-match"); +pub const IF_NONE_MATCH: FieldName = FieldName::from_lowercase_str("if-none-match"); /// The `If-Range` Header -pub const IF_RANGE: HeaderName = HeaderName::from_lowercase_str("if-range"); +pub const IF_RANGE: FieldName = FieldName::from_lowercase_str("if-range"); /// The `If-Unmodified-Since` Header -pub const IF_UNMODIFIED_SINCE: HeaderName = HeaderName::from_lowercase_str("if-unmodified-since"); +pub const IF_UNMODIFIED_SINCE: FieldName = FieldName::from_lowercase_str("if-unmodified-since"); /// The `Last-Modified` Header -pub const LAST_MODIFIED: HeaderName = HeaderName::from_lowercase_str("last-modified"); +pub const LAST_MODIFIED: FieldName = FieldName::from_lowercase_str("last-modified"); /// The `Location` Header -pub const LOCATION: HeaderName = HeaderName::from_lowercase_str("location"); +pub const LOCATION: FieldName = FieldName::from_lowercase_str("location"); /// The `Max-Forwards` Header -pub const MAX_FORWARDS: HeaderName = HeaderName::from_lowercase_str("max-forwards"); +pub const MAX_FORWARDS: FieldName = FieldName::from_lowercase_str("max-forwards"); /// The `Pragma` Header -pub const PRAGMA: HeaderName = HeaderName::from_lowercase_str("pragma"); +pub const PRAGMA: FieldName = FieldName::from_lowercase_str("pragma"); /// The `Proxy-Authenticate` Header -pub const PROXY_AUTHENTICATE: HeaderName = HeaderName::from_lowercase_str("proxy-authenticate"); +pub const PROXY_AUTHENTICATE: FieldName = FieldName::from_lowercase_str("proxy-authenticate"); /// The `Proxy-Authorization` Header -pub const PROXY_AUTHORIZATION: HeaderName = HeaderName::from_lowercase_str("proxy-authorization"); +pub const PROXY_AUTHORIZATION: FieldName = FieldName::from_lowercase_str("proxy-authorization"); /// The `Proxy-Connection` Header -pub const PROXY_CONNECTION: HeaderName = HeaderName::from_lowercase_str("proxy-connection"); +pub const PROXY_CONNECTION: FieldName = FieldName::from_lowercase_str("proxy-connection"); /// The `Referer` Header -pub const REFERER: HeaderName = HeaderName::from_lowercase_str("referer"); +pub const REFERER: FieldName = FieldName::from_lowercase_str("referer"); /// The `Retry-After` Header -pub const RETRY_AFTER: HeaderName = HeaderName::from_lowercase_str("retry-after"); +pub const RETRY_AFTER: FieldName = FieldName::from_lowercase_str("retry-after"); /// The `Server` Header -pub const SERVER: HeaderName = HeaderName::from_lowercase_str("server"); +pub const SERVER: FieldName = FieldName::from_lowercase_str("server"); /// The `Server` Header -pub const SERVER_TIMING: HeaderName = HeaderName::from_lowercase_str("server-timing"); +pub const SERVER_TIMING: FieldName = FieldName::from_lowercase_str("server-timing"); /// The `SourceMap` Header -pub const SOURCE_MAP: HeaderName = HeaderName::from_lowercase_str("sourcemap"); +pub const SOURCE_MAP: FieldName = FieldName::from_lowercase_str("sourcemap"); /// The `Strict-Transport-Security` Header -pub const STRICT_TRANSPORT_SECURITY: HeaderName = - HeaderName::from_lowercase_str("strict-transport-security"); +pub const STRICT_TRANSPORT_SECURITY: FieldName = + FieldName::from_lowercase_str("strict-transport-security"); /// The `Te` Header -pub const TE: HeaderName = HeaderName::from_lowercase_str("te"); +pub const TE: FieldName = FieldName::from_lowercase_str("te"); /// The `Timing-Allow-Origin` Header -pub const TIMING_ALLOW_ORIGIN: HeaderName = HeaderName::from_lowercase_str("timing-allow-origin"); +pub const TIMING_ALLOW_ORIGIN: FieldName = FieldName::from_lowercase_str("timing-allow-origin"); /// The `Traceparent` Header -pub const TRACEPARENT: HeaderName = HeaderName::from_lowercase_str("traceparent"); +pub const TRACEPARENT: FieldName = FieldName::from_lowercase_str("traceparent"); /// The `Trailer` Header -pub const TRAILER: HeaderName = HeaderName::from_lowercase_str("trailer"); +pub const TRAILER: FieldName = FieldName::from_lowercase_str("trailer"); /// The `Upgrade` Header -pub const UPGRADE: HeaderName = HeaderName::from_lowercase_str("upgrade"); +pub const UPGRADE: FieldName = FieldName::from_lowercase_str("upgrade"); /// The `User-Agent` Header -pub const USER_AGENT: HeaderName = HeaderName::from_lowercase_str("user-agent"); +pub const USER_AGENT: FieldName = FieldName::from_lowercase_str("user-agent"); /// The `Vary` Header -pub const VARY: HeaderName = HeaderName::from_lowercase_str("vary"); +pub const VARY: FieldName = FieldName::from_lowercase_str("vary"); /// The `Via` Header -pub const VIA: HeaderName = HeaderName::from_lowercase_str("via"); +pub const VIA: FieldName = FieldName::from_lowercase_str("via"); /// The `Warning` Header -pub const WARNING: HeaderName = HeaderName::from_lowercase_str("warning"); +pub const WARNING: FieldName = FieldName::from_lowercase_str("warning"); /// The `WWW-Authenticate` Header -pub const WWW_AUTHENTICATE: HeaderName = HeaderName::from_lowercase_str("www-authenticate"); +pub const WWW_AUTHENTICATE: FieldName = FieldName::from_lowercase_str("www-authenticate"); diff --git a/src/headers/header.rs b/src/headers/header.rs index df2f07bc..9e9587b4 100644 --- a/src/headers/header.rs +++ b/src/headers/header.rs @@ -1,56 +1,23 @@ -use std::ops::Deref; - -use crate::headers::{HeaderName, HeaderValue, Headers}; +use crate::headers::{FieldName, FieldValue}; /// A trait representing a [`HeaderName`] and [`HeaderValue`] pair. -pub trait Header { - /// Access the header's name. - fn header_name(&self) -> HeaderName; +/// +/// # Specifications +/// +/// - [RFC 9110, section 5: Fields](https://www.rfc-editor.org/rfc/rfc9110.html#fields) +#[doc(alias = "Header")] +#[doc(alias = "FieldHeader")] +pub trait Field +where + Self: Sized, +{ + /// The header's field name. + const FIELD_NAME: FieldName; /// Access the header's value. - fn header_value(&self) -> HeaderValue; - - /// Insert the header name and header value into something that looks like a - /// [`Headers`] map. - fn apply_header>(&self, mut headers: H) { - let name = self.header_name(); - let value = self.header_value(); - - // The value should already have been validated when it was created, so this should - // possibly be done with an unsafe call - headers.as_mut().insert(name, value).unwrap(); - } -} - -impl<'a, 'b> Header for (&'a str, &'b str) { - fn header_name(&self) -> HeaderName { - HeaderName::from(self.0) - } - - fn header_value(&self) -> HeaderValue { - HeaderValue::from_bytes(self.1.to_owned().into_bytes()) - .expect("String slice should be valid ASCII") - } -} - -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::*; + fn field_value(&self) -> FieldValue; - #[test] - fn header_from_strings() { - let strings = ("Content-Length", "12"); - assert_eq!(strings.header_name(), "Content-Length"); - assert_eq!(strings.header_value(), "12"); - } + // /// Create a field from its parts. + // // TODO: move this to a separate trait. + // fn from_field_pair(name: FieldName, value: FieldValue) -> Result; } diff --git a/src/headers/header_name.rs b/src/headers/header_name.rs index 430687f8..ac597e66 100644 --- a/src/headers/header_name.rs +++ b/src/headers/header_name.rs @@ -4,13 +4,13 @@ use std::str::FromStr; use crate::Error; -use super::Header; +use super::Field; /// A header name. #[derive(Clone, PartialEq, Eq, Hash)] -pub struct HeaderName(Cow<'static, str>); +pub struct FieldName(Cow<'static, str>); -impl HeaderName { +impl FieldName { /// Create a new `HeaderName` from a Vec of ASCII bytes. /// /// # Error @@ -22,7 +22,7 @@ impl HeaderName { // This is permitted because ASCII is valid UTF-8, and we just checked that. let string = unsafe { String::from_utf8_unchecked(bytes.to_vec()) }; - Ok(HeaderName(Cow::Owned(string))) + Ok(FieldName(Cow::Owned(string))) } /// Create a new `HeaderName` from an ASCII string. @@ -51,28 +51,28 @@ impl HeaderName { pub unsafe fn from_bytes_unchecked(mut bytes: Vec) -> Self { bytes.make_ascii_lowercase(); let string = String::from_utf8_unchecked(bytes); - HeaderName(Cow::Owned(string)) + FieldName(Cow::Owned(string)) } /// Converts a string assumed to lowercase into a `HeaderName` pub(crate) const fn from_lowercase_str(str: &'static str) -> Self { - HeaderName(Cow::Borrowed(str)) + FieldName(Cow::Borrowed(str)) } } -impl Debug for HeaderName { +impl Debug for FieldName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) } } -impl Display for HeaderName { +impl Display for FieldName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } -impl FromStr for HeaderName { +impl FromStr for FieldName { type Err = Error; /// Create a new `HeaderName`. @@ -80,58 +80,58 @@ impl FromStr for HeaderName { /// This checks it's valid ASCII, and lowercases it. fn from_str(s: &str) -> Result { crate::ensure!(s.is_ascii(), "String slice should be valid ASCII"); - Ok(HeaderName(Cow::Owned(s.to_ascii_lowercase()))) + Ok(FieldName(Cow::Owned(s.to_ascii_lowercase()))) } } -impl From<&HeaderName> for HeaderName { - fn from(value: &HeaderName) -> HeaderName { +impl From<&FieldName> for FieldName { + fn from(value: &FieldName) -> FieldName { value.clone() } } -impl From for HeaderName { - fn from(header: T) -> HeaderName { - header.header_name() +impl From for FieldName { + fn from(_field: T) -> FieldName { + T::FIELD_NAME } } -impl<'a> From<&'a str> for HeaderName { +impl<'a> From<&'a str> for FieldName { fn from(value: &'a str) -> Self { Self::from_str(value).expect("String slice should be valid ASCII") } } -impl PartialEq for HeaderName { +impl PartialEq for FieldName { fn eq(&self, other: &str) -> bool { - match HeaderName::from_str(other) { + match FieldName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } -impl<'a> PartialEq<&'a str> for HeaderName { +impl<'a> PartialEq<&'a str> for FieldName { fn eq(&self, other: &&'a str) -> bool { - match HeaderName::from_str(other) { + match FieldName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } -impl PartialEq for HeaderName { +impl PartialEq for FieldName { fn eq(&self, other: &String) -> bool { - match HeaderName::from_str(other) { + match FieldName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } -impl<'a> PartialEq<&String> for HeaderName { +impl<'a> PartialEq<&String> for FieldName { fn eq(&self, other: &&String) -> bool { - match HeaderName::from_str(other) { + match FieldName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } @@ -145,8 +145,8 @@ mod tests { #[test] #[allow(clippy::eq_op)] fn test_header_name_static_non_static() { - let static_header = HeaderName::from_lowercase_str("hello"); - let non_static_header = HeaderName::from_str("hello").unwrap(); + let static_header = FieldName::from_lowercase_str("hello"); + let non_static_header = FieldName::from_str("hello").unwrap(); assert_eq!(&static_header, &non_static_header); assert_eq!(&static_header, &static_header); @@ -159,7 +159,7 @@ mod tests { #[test] fn equality() { - let static_header = HeaderName::from_lowercase_str("hello"); + let static_header = FieldName::from_lowercase_str("hello"); assert_eq!(static_header, "hello"); assert_eq!(&static_header, "hello"); assert_eq!(static_header, String::from("hello")); @@ -178,7 +178,7 @@ mod tests { #[test] fn test_debug() { - let header_name = HeaderName::from_str("hello").unwrap(); + let header_name = FieldName::from_str("hello").unwrap(); assert_eq!(format!("{:?}", header_name), "\"hello\""); } } diff --git a/src/headers/header_value.rs b/src/headers/header_value.rs index c8287a1c..a23d3791 100644 --- a/src/headers/header_value.rs +++ b/src/headers/header_value.rs @@ -4,17 +4,17 @@ use std::str::FromStr; #[cfg(feature = "cookies")] use crate::cookies::Cookie; -use crate::headers::HeaderValues; +use crate::headers::FieldValues; use crate::mime::Mime; use crate::Error; /// A header value. #[derive(Clone, Eq, PartialEq, Hash)] -pub struct HeaderValue { +pub struct FieldValue { inner: String, } -impl HeaderValue { +impl FieldValue { /// Create a new `HeaderValue` from a Vec of ASCII bytes. /// /// # Error @@ -48,32 +48,32 @@ impl HeaderValue { } } -impl From for HeaderValue { +impl From for FieldValue { fn from(mime: Mime) -> Self { - HeaderValue { + FieldValue { inner: format!("{}", mime), } } } #[cfg(feature = "cookies")] -impl From> for HeaderValue { +impl From> for FieldValue { fn from(cookie: Cookie<'_>) -> Self { - HeaderValue { + FieldValue { inner: cookie.to_string(), } } } -impl From<&Mime> for HeaderValue { +impl From<&Mime> for FieldValue { fn from(mime: &Mime) -> Self { - HeaderValue { + FieldValue { inner: format!("{}", mime), } } } -impl FromStr for HeaderValue { +impl FromStr for FieldValue { type Err = Error; /// Create a new `HeaderValue`. @@ -87,7 +87,7 @@ impl FromStr for HeaderValue { } } -impl<'a> TryFrom<&'a str> for HeaderValue { +impl<'a> TryFrom<&'a str> for FieldValue { type Error = Error; fn try_from(value: &'a str) -> Result { @@ -95,44 +95,44 @@ impl<'a> TryFrom<&'a str> for HeaderValue { } } -impl Debug for HeaderValue { +impl Debug for FieldValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.inner) } } -impl Display for HeaderValue { +impl Display for FieldValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.inner) } } -impl PartialEq for HeaderValue { +impl PartialEq for FieldValue { fn eq(&self, other: &str) -> bool { self.inner == other } } -impl<'a> PartialEq<&'a str> for HeaderValue { +impl<'a> PartialEq<&'a str> for FieldValue { fn eq(&self, other: &&'a str) -> bool { &self.inner == other } } -impl PartialEq for HeaderValue { +impl PartialEq for FieldValue { fn eq(&self, other: &String) -> bool { &self.inner == other } } -impl<'a> PartialEq<&String> for HeaderValue { +impl<'a> PartialEq<&String> for FieldValue { fn eq(&self, other: &&String) -> bool { &&self.inner == other } } -impl From for HeaderValue { - fn from(mut other: HeaderValues) -> Self { +impl From for FieldValue { + fn from(mut other: FieldValues) -> Self { other.inner.reverse(); other .inner @@ -147,7 +147,7 @@ mod tests { #[test] fn test_debug() { - let header_value = HeaderValue::from_str("foo0").unwrap(); + let header_value = FieldValue::from_str("foo0").unwrap(); assert_eq!(format!("{:?}", header_value), "\"foo0\""); } } diff --git a/src/headers/header_values.rs b/src/headers/header_values.rs index 27fc0045..ee46b521 100644 --- a/src/headers/header_values.rs +++ b/src/headers/header_values.rs @@ -1,4 +1,4 @@ -use crate::headers::{HeaderValue, Values}; +use crate::headers::{FieldValue, Values}; use std::fmt::{self, Debug, Display}; use std::iter::FromIterator; @@ -9,34 +9,34 @@ use std::slice::SliceIndex; /// /// This always contains at least one header value. #[derive(Clone)] -pub struct HeaderValues { - pub(crate) inner: Vec, +pub struct FieldValues { + pub(crate) inner: Vec, } -impl HeaderValues { +impl FieldValues { /// Move all values from `other` into `self`, leaving `other` empty. pub fn append(&mut self, other: &mut Self) { self.inner.append(&mut other.inner) } /// Returns a reference or a value depending on the type of index. - pub fn get(&self, index: usize) -> Option<&HeaderValue> { + pub fn get(&self, index: usize) -> Option<&FieldValue> { self.inner.get(index) } /// Returns a mutable reference or a value depending on the type of index. - pub fn get_mut(&mut self, index: usize) -> Option<&mut HeaderValue> { + pub fn get_mut(&mut self, index: usize) -> Option<&mut FieldValue> { self.inner.get_mut(index) } /// Returns `true` if there is a value corresponding to the specified `HeaderValue` in the list, /// `false` otherwise. - pub fn contains(&self, value: &HeaderValue) -> bool { + pub fn contains(&self, value: &FieldValue) -> bool { self.inner.contains(value) } /// Returns the last `HeaderValue`. - pub fn last(&self) -> &HeaderValue { + pub fn last(&self) -> &FieldValue { self.inner .last() .expect("HeaderValues must always contain at least one value") @@ -56,7 +56,7 @@ impl HeaderValues { // } } -impl> Index for HeaderValues { +impl> Index for FieldValues { type Output = I::Output; #[inline] @@ -65,21 +65,21 @@ impl> Index for HeaderValues { } } -impl FromIterator for HeaderValues { - fn from_iter(iter: I) -> HeaderValues +impl FromIterator for FieldValues { + fn from_iter(iter: I) -> FieldValues where - I: IntoIterator, + I: IntoIterator, { let iter = iter.into_iter(); let mut output = Vec::with_capacity(iter.size_hint().0); for v in iter { output.push(v); } - HeaderValues { inner: output } + FieldValues { inner: output } } } -impl Debug for HeaderValues { +impl Debug for FieldValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.inner.len() == 1 { write!(f, "{:?}", self.inner[0]) @@ -89,7 +89,7 @@ impl Debug for HeaderValues { } } -impl Display for HeaderValues { +impl Display for FieldValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for v in &self.inner { @@ -99,70 +99,70 @@ impl Display for HeaderValues { } } -impl PartialEq for HeaderValues { +impl PartialEq for FieldValues { fn eq(&self, other: &str) -> bool { self.inner.len() == 1 && self.inner[0] == other } } -impl<'a> PartialEq<&'a str> for HeaderValues { +impl<'a> PartialEq<&'a str> for FieldValues { fn eq(&self, other: &&'a str) -> bool { self.inner.len() == 1 && &self.inner[0] == other } } -impl<'a> PartialEq<[&'a str]> for HeaderValues { +impl<'a> PartialEq<[&'a str]> for FieldValues { fn eq(&self, other: &[&'a str]) -> bool { self.inner.iter().eq(other.iter()) } } -impl PartialEq for HeaderValues { +impl PartialEq for FieldValues { fn eq(&self, other: &String) -> bool { self.inner.len() == 1 && self.inner[0] == *other } } -impl<'a> PartialEq<&String> for HeaderValues { +impl<'a> PartialEq<&String> for FieldValues { fn eq(&self, other: &&String) -> bool { self.inner.len() == 1 && self.inner[0] == **other } } -impl From for HeaderValues { - fn from(other: HeaderValue) -> Self { +impl From for FieldValues { + fn from(other: FieldValue) -> Self { Self { inner: vec![other] } } } -impl AsRef for HeaderValues { - fn as_ref(&self) -> &HeaderValue { +impl AsRef for FieldValues { + fn as_ref(&self) -> &FieldValue { &self.inner[0] } } -impl AsMut for HeaderValues { - fn as_mut(&mut self) -> &mut HeaderValue { +impl AsMut for FieldValues { + fn as_mut(&mut self) -> &mut FieldValue { &mut self.inner[0] } } -impl Deref for HeaderValues { - type Target = HeaderValue; +impl Deref for FieldValues { + type Target = FieldValue; - fn deref(&self) -> &HeaderValue { + fn deref(&self) -> &FieldValue { &self.inner[0] } } -impl DerefMut for HeaderValues { - fn deref_mut(&mut self) -> &mut HeaderValue { +impl DerefMut for FieldValues { + fn deref_mut(&mut self) -> &mut FieldValue { &mut self.inner[0] } } -impl<'a> IntoIterator for &'a HeaderValues { - type Item = &'a HeaderValue; +impl<'a> IntoIterator for &'a FieldValues { + type Item = &'a FieldValue; type IntoIter = Values<'a>; #[inline] @@ -171,15 +171,15 @@ impl<'a> IntoIterator for &'a HeaderValues { } } -impl From> for HeaderValues { - fn from(headers: Vec) -> Self { +impl From> for FieldValues { + fn from(headers: Vec) -> Self { Self { inner: headers } } } -impl IntoIterator for HeaderValues { - type Item = HeaderValue; - type IntoIter = std::vec::IntoIter; +impl IntoIterator for FieldValues { + type Item = FieldValue; + type IntoIter = std::vec::IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -193,14 +193,14 @@ mod tests { #[test] fn test_debug_single() { - let header_values = HeaderValues { + let header_values = FieldValues { inner: vec!["foo0".parse().unwrap()], }; assert_eq!(format!("{:?}", header_values), "\"foo0\""); } #[test] fn test_debug_multiple() { - let header_values = HeaderValues { + let header_values = FieldValues { inner: vec!["foo0".parse().unwrap(), "foo1".parse().unwrap()], }; assert_eq!(format!("{:?}", header_values), r#"["foo0", "foo1"]"#); diff --git a/src/headers/headers.rs b/src/headers/headers.rs index a7a9d90e..47dba549 100644 --- a/src/headers/headers.rs +++ b/src/headers/headers.rs @@ -8,14 +8,14 @@ use std::ops::Index; use std::str::FromStr; use crate::headers::{ - HeaderName, HeaderValues, IntoIter, Iter, IterMut, Names, ToHeaderValues, Values, + Field, FieldName, FieldValues, IntoIter, Iter, IterMut, Names, ToFieldValues, Values, }; -/// A collection of HTTP Headers. +/// A collection of HTTP Fields. /// -/// Headers are never manually constructed, but are part of `Request`, -/// `Response`, and `Trailers`. Each of these types implements `AsRef` -/// and `AsMut` so functions that want to modify headers can be generic +/// Fields are never manually constructed, but are part of `Request`, +/// `Response`, and `Trailers`. Each of these types implements `AsRef` +/// and `AsMut` so functions that want to modify headers can be generic /// over either of these traits. /// /// # Examples @@ -28,11 +28,11 @@ use crate::headers::{ /// assert_eq!(res["hello"], "foo0"); /// ``` #[derive(Clone)] -pub struct Headers { - pub(crate) headers: HashMap, +pub struct Fields { + pub(crate) headers: HashMap, } -impl Headers { +impl Fields { /// Create a new instance. pub(crate) fn new() -> Self { Self { @@ -43,31 +43,37 @@ impl Headers { /// Insert a header into the headers. /// /// Not that this will replace all header values for a given header name. - /// If you wish to add header values for a header name that already exists - /// use `Headers::append` pub fn insert( &mut self, - name: impl Into, - values: impl ToHeaderValues, - ) -> crate::Result> { + name: impl Into, + values: impl ToFieldValues, + ) -> crate::Result> { let name = name.into(); - let values: HeaderValues = values.to_header_values()?.collect(); + let values: FieldValues = values.to_field_values().unwrap().collect(); Ok(self.headers.insert(name, values)) } + /// Insert a typed header into the headers. + /// + /// Not that this will replace all header values for a given header name. + pub fn insert_typed(&mut self, field: F) -> Option { + self.headers + .insert(F::FIELD_NAME, field.field_value().into()) + } + /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a header, but insert a /// header if there aren't any. Or else append to the existing list of headers. pub fn append( &mut self, - name: impl Into, - values: impl ToHeaderValues, + name: impl Into, + values: impl ToFieldValues, ) -> crate::Result<()> { let name = name.into(); match self.get_mut(&name) { Some(headers) => { - let mut values: HeaderValues = values.to_header_values()?.collect(); + let mut values: FieldValues = values.to_field_values()?.collect(); headers.append(&mut values); } None => { @@ -78,17 +84,17 @@ impl Headers { } /// Get a reference to a header. - pub fn get(&self, name: impl Into) -> Option<&HeaderValues> { + pub fn get(&self, name: impl Into) -> Option<&FieldValues> { self.headers.get(&name.into()) } /// Get a mutable reference to a header. - pub fn get_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { + pub fn get_mut(&mut self, name: impl Into) -> Option<&mut FieldValues> { self.headers.get_mut(&name.into()) } /// Remove a header. - pub fn remove(&mut self, name: impl Into) -> Option { + pub fn remove(&mut self, name: impl Into) -> Option { self.headers.remove(&name.into()) } @@ -120,37 +126,37 @@ impl Headers { } } -impl Index for Headers { - type Output = HeaderValues; +impl Index for Fields { + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// - /// Panics if the name is not present in `Headers`. + /// Panics if the name is not present in `Fields`. #[inline] - fn index(&self, name: HeaderName) -> &HeaderValues { + fn index(&self, name: FieldName) -> &FieldValues { self.get(name).expect("no entry found for name") } } -impl Index<&str> for Headers { - type Output = HeaderValues; +impl Index<&str> for Fields { + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// - /// Panics if the name is not present in `Headers`. + /// Panics if the name is not present in `Fields`. #[inline] - fn index(&self, name: &str) -> &HeaderValues { - let name = HeaderName::from_str(name).expect("string slice needs to be valid ASCII"); + fn index(&self, name: &str) -> &FieldValues { + let name = FieldName::from_str(name).expect("string slice needs to be valid ASCII"); self.get(name).expect("no entry found for name") } } -impl IntoIterator for Headers { - type Item = (HeaderName, HeaderValues); +impl IntoIterator for Fields { + type Item = (FieldName, FieldValues); type IntoIter = IntoIter; /// Returns a iterator of references over the remaining items. @@ -162,8 +168,8 @@ impl IntoIterator for Headers { } } -impl<'a> IntoIterator for &'a Headers { - type Item = (&'a HeaderName, &'a HeaderValues); +impl<'a> IntoIterator for &'a Fields { + type Item = (&'a FieldName, &'a FieldValues); type IntoIter = Iter<'a>; #[inline] @@ -172,8 +178,8 @@ impl<'a> IntoIterator for &'a Headers { } } -impl<'a> IntoIterator for &'a mut Headers { - type Item = (&'a HeaderName, &'a mut HeaderValues); +impl<'a> IntoIterator for &'a mut Fields { + type Item = (&'a FieldName, &'a mut FieldValues); type IntoIter = IterMut<'a>; #[inline] @@ -182,20 +188,20 @@ impl<'a> IntoIterator for &'a mut Headers { } } -impl Debug for Headers { +impl Debug for Fields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.headers.iter()).finish() } } -impl AsRef for Headers { - fn as_ref(&self) -> &Headers { +impl AsRef for Fields { + fn as_ref(&self) -> &Fields { self } } -impl AsMut for Headers { - fn as_mut(&mut self) -> &mut Headers { +impl AsMut for Fields { + fn as_mut(&mut self) -> &mut Fields { self } } @@ -205,14 +211,14 @@ mod tests { use super::*; use std::str::FromStr; - const STATIC_HEADER: HeaderName = HeaderName::from_lowercase_str("hello"); + const STATIC_HEADER: FieldName = FieldName::from_lowercase_str("hello"); #[test] fn test_header_name_static_non_static() -> crate::Result<()> { - let static_header = HeaderName::from_lowercase_str("hello"); - let non_static_header = HeaderName::from_str("hello")?; + let static_header = FieldName::from_lowercase_str("hello"); + let non_static_header = FieldName::from_str("hello")?; - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.append(STATIC_HEADER, "foo0").unwrap(); headers.append(static_header.clone(), "foo1").unwrap(); headers.append(non_static_header.clone(), "foo2").unwrap(); @@ -226,7 +232,7 @@ mod tests { #[test] fn index_into_headers() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert("hello", "foo0").unwrap(); assert_eq!(headers["hello"], "foo0"); assert_eq!(headers.get("hello").unwrap(), "foo0"); @@ -234,14 +240,14 @@ mod tests { #[test] fn test_debug_single() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert("single", "foo0").unwrap(); assert_eq!(format!("{:?}", headers), r#"{"single": "foo0"}"#); } #[test] fn test_debug_multiple() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.append("multi", "foo0").unwrap(); headers.append("multi", "foo1").unwrap(); assert_eq!(format!("{:?}", headers), r#"{"multi": ["foo0", "foo1"]}"#); diff --git a/src/headers/into_iter.rs b/src/headers/into_iter.rs index 2f1514c2..fdf56e7d 100644 --- a/src/headers/into_iter.rs +++ b/src/headers/into_iter.rs @@ -1,16 +1,16 @@ use std::collections::hash_map; use std::iter::Iterator; -use crate::headers::{HeaderName, HeaderValues}; +use crate::headers::{FieldName, FieldValues}; /// An owning iterator over the entries of `Headers`. #[derive(Debug)] pub struct IntoIter { - pub(super) inner: hash_map::IntoIter, + pub(super) inner: hash_map::IntoIter, } impl Iterator for IntoIter { - type Item = (HeaderName, HeaderValues); + type Item = (FieldName, FieldValues); fn next(&mut self) -> Option { self.inner.next() diff --git a/src/headers/iter.rs b/src/headers/iter.rs index 2b7aca05..cc5da6fe 100644 --- a/src/headers/iter.rs +++ b/src/headers/iter.rs @@ -1,16 +1,16 @@ use std::collections::hash_map; use std::iter::Iterator; -use crate::headers::{HeaderName, HeaderValues}; +use crate::headers::{FieldName, FieldValues}; /// Iterator over the headers. #[derive(Debug)] pub struct Iter<'a> { - pub(super) inner: hash_map::Iter<'a, HeaderName, HeaderValues>, + pub(super) inner: hash_map::Iter<'a, FieldName, FieldValues>, } impl<'a> Iterator for Iter<'a> { - type Item = (&'a HeaderName, &'a HeaderValues); + type Item = (&'a FieldName, &'a FieldValues); fn next(&mut self) -> Option { self.inner.next() diff --git a/src/headers/iter_mut.rs b/src/headers/iter_mut.rs index 88e5d922..fd822faf 100644 --- a/src/headers/iter_mut.rs +++ b/src/headers/iter_mut.rs @@ -1,16 +1,16 @@ use std::collections::hash_map; use std::iter::Iterator; -use crate::headers::{HeaderName, HeaderValues}; +use crate::headers::{FieldName, FieldValues}; /// Iterator over the headers. #[derive(Debug)] pub struct IterMut<'a> { - pub(super) inner: hash_map::IterMut<'a, HeaderName, HeaderValues>, + pub(super) inner: hash_map::IterMut<'a, FieldName, FieldValues>, } impl<'a> Iterator for IterMut<'a> { - type Item = (&'a HeaderName, &'a mut HeaderValues); + type Item = (&'a FieldName, &'a mut FieldValues); fn next(&mut self) -> Option { self.inner.next() diff --git a/src/headers/mod.rs b/src/headers/mod.rs index e23aede0..b1979d5f 100644 --- a/src/headers/mod.rs +++ b/src/headers/mod.rs @@ -15,14 +15,14 @@ mod to_header_values; mod values; pub use constants::*; -pub use header::Header; -pub use header_name::HeaderName; -pub use header_value::HeaderValue; -pub use header_values::HeaderValues; -pub use headers::Headers; +pub use header::Field; +pub use header_name::FieldName; +pub use header_value::FieldValue; +pub use header_values::FieldValues; +pub use headers::Fields; pub use into_iter::IntoIter; pub use iter::Iter; pub use iter_mut::IterMut; pub use names::Names; -pub use to_header_values::ToHeaderValues; +pub use to_header_values::ToFieldValues; pub use values::Values; diff --git a/src/headers/names.rs b/src/headers/names.rs index 1bb4c26d..d4753c0b 100644 --- a/src/headers/names.rs +++ b/src/headers/names.rs @@ -1,16 +1,16 @@ use std::collections::hash_map; use std::iter::Iterator; -use crate::headers::{HeaderName, HeaderValues}; +use crate::headers::{FieldName, FieldValues}; /// Iterator over the headers. #[derive(Debug)] pub struct Names<'a> { - pub(super) inner: hash_map::Keys<'a, HeaderName, HeaderValues>, + pub(super) inner: hash_map::Keys<'a, FieldName, FieldValues>, } impl<'a> Iterator for Names<'a> { - type Item = &'a HeaderName; + type Item = &'a FieldName; fn next(&mut self) -> Option { self.inner.next() diff --git a/src/headers/to_header_values.rs b/src/headers/to_header_values.rs index 37ef54d6..274f052f 100644 --- a/src/headers/to_header_values.rs +++ b/src/headers/to_header_values.rs @@ -4,53 +4,53 @@ use std::iter; use std::option; use std::slice; -use crate::headers::{Header, HeaderValue, HeaderValues, Values}; +use crate::headers::{Field, FieldValue, FieldValues, Values}; /// A trait for objects which can be converted or resolved to one or more `HeaderValue`s. -pub trait ToHeaderValues { +pub trait ToFieldValues { /// Returned iterator over header values which this type may correspond to. - type Iter: Iterator; + type Iter: Iterator; /// Converts this object to an iterator of resolved `HeaderValues`. - fn to_header_values(&self) -> crate::Result; + fn to_field_values(&self) -> crate::Result; } -impl ToHeaderValues for T { - type Iter = option::IntoIter; +impl ToFieldValues for T { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - Ok(Some(self.header_value()).into_iter()) + fn to_field_values(&self) -> crate::Result { + Ok(Some(self.field_value()).into_iter()) } } -impl ToHeaderValues for HeaderValue { - type Iter = option::IntoIter; +impl ToFieldValues for FieldValue { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { + fn to_field_values(&self) -> crate::Result { Ok(Some(self.clone()).into_iter()) } } -impl<'a> ToHeaderValues for &'a HeaderValues { +impl<'a> ToFieldValues for &'a FieldValues { type Iter = iter::Cloned>; - fn to_header_values(&self) -> crate::Result { + fn to_field_values(&self) -> crate::Result { Ok(self.iter().cloned()) } } -impl<'a> ToHeaderValues for &'a [HeaderValue] { - type Iter = iter::Cloned>; +impl<'a> ToFieldValues for &'a [FieldValue] { + type Iter = iter::Cloned>; - fn to_header_values(&self) -> crate::Result { + fn to_field_values(&self) -> crate::Result { Ok(self.iter().cloned()) } } -impl<'a> ToHeaderValues for &'a str { - type Iter = option::IntoIter; +impl<'a> ToFieldValues for &'a str { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { + fn to_field_values(&self) -> crate::Result { let value = self .parse() .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; @@ -58,26 +58,26 @@ impl<'a> ToHeaderValues for &'a str { } } -impl ToHeaderValues for String { - type Iter = option::IntoIter; +impl ToFieldValues for String { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - self.as_str().to_header_values() + fn to_field_values(&self) -> crate::Result { + self.as_str().to_field_values() } } -impl ToHeaderValues for &String { - type Iter = option::IntoIter; +impl ToFieldValues for &String { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - self.as_str().to_header_values() + fn to_field_values(&self) -> crate::Result { + self.as_str().to_field_values() } } -impl ToHeaderValues for Cow<'_, str> { - type Iter = option::IntoIter; +impl ToFieldValues for Cow<'_, str> { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { - self.as_ref().to_header_values() + fn to_field_values(&self) -> crate::Result { + self.as_ref().to_field_values() } } diff --git a/src/headers/values.rs b/src/headers/values.rs index 810c0514..12e28a83 100644 --- a/src/headers/values.rs +++ b/src/headers/values.rs @@ -1,19 +1,19 @@ use std::collections::hash_map; use std::iter::Iterator; -use crate::headers::{HeaderName, HeaderValue, HeaderValues}; +use crate::headers::{FieldName, FieldValue, FieldValues}; /// Iterator over the header values. #[derive(Debug)] pub struct Values<'a> { - pub(super) inner: Option>, - slot: Option<&'a HeaderValues>, + pub(super) inner: Option>, + slot: Option<&'a FieldValues>, cursor: usize, } impl<'a> Values<'a> { /// Constructor for `Headers`. - pub(crate) fn new(inner: hash_map::Values<'a, HeaderName, HeaderValues>) -> Self { + pub(crate) fn new(inner: hash_map::Values<'a, FieldName, FieldValues>) -> Self { Self { inner: Some(inner), slot: None, @@ -22,7 +22,7 @@ impl<'a> Values<'a> { } /// Constructor for `HeaderValues`. - pub(crate) fn new_values(values: &'a HeaderValues) -> Self { + pub(crate) fn new_values(values: &'a FieldValues) -> Self { Self { inner: None, slot: Some(values), @@ -32,7 +32,7 @@ impl<'a> Values<'a> { } impl<'a> Iterator for Values<'a> { - type Item = &'a HeaderValue; + type Item = &'a FieldValue; fn next(&mut self) -> Option { loop { diff --git a/src/hyperium_http.rs b/src/hyperium_http.rs index 81d33425..f0d01ffc 100644 --- a/src/hyperium_http.rs +++ b/src/hyperium_http.rs @@ -112,7 +112,7 @@ impl TryFrom for Headers { } } -impl TryFrom for http::HeaderMap { +impl TryFrom for http::HeaderMap { type Error = Error; fn try_from(headers: Headers) -> Result { @@ -156,7 +156,7 @@ fn hyperium_headers_to_headers( Ok(()) } -fn headers_to_hyperium_headers(headers: &mut Headers, hyperium_headers: &mut http::HeaderMap) { +fn headers_to_hyperium_headers(headers: &mut Fields, hyperium_headers: &mut http::HeaderMap) { for (name, values) in headers { let name = format!("{}", name).into_bytes(); let name = http::header::HeaderName::from_bytes(&name).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index c8821a41..7c55acd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,9 @@ pub use version::Version; #[doc(inline)] pub use crate::url::Url; +#[doc(inline)] +pub use crate::headers::Field; + pub mod security; pub mod trailers; diff --git a/src/mime/mod.rs b/src/mime/mod.rs index 8aa3b03d..d098f2bb 100644 --- a/src/mime/mod.rs +++ b/src/mime/mod.rs @@ -12,7 +12,7 @@ use std::fmt::{self, Debug, Display}; use std::option; use std::str::FromStr; -use crate::headers::{HeaderValue, ToHeaderValues}; +use crate::headers::{FieldValue, ToFieldValues}; use infer::Infer; @@ -195,15 +195,15 @@ impl<'a> From<&'a str> for Mime { } } -impl ToHeaderValues for Mime { - type Iter = option::IntoIter; +impl ToFieldValues for Mime { + type Iter = option::IntoIter; - fn to_header_values(&self) -> crate::Result { + fn to_field_values(&self) -> crate::Result { let mime = self.clone(); - let header: HeaderValue = mime.into(); + let header: FieldValue = mime.into(); // A HeaderValue will always convert into itself. - Ok(header.to_header_values().unwrap()) + Ok(header.to_field_values().unwrap()) } } diff --git a/src/other/date.rs b/src/other/date.rs index 1e381ffe..78b8b4b8 100644 --- a/src/other/date.rs +++ b/src/other/date.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, DATE}; +use crate::headers::{Field, FieldName, FieldValue, Fields, DATE}; use crate::utils::HttpDate; use std::time::SystemTime; @@ -51,7 +51,7 @@ impl Date { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(DATE) { Some(headers) => headers, None => return Ok(None), @@ -73,17 +73,15 @@ impl Date { } } -impl Header for Date { - fn header_name(&self) -> HeaderName { - DATE - } +impl Field for Date { + const FIELD_NAME: FieldName = DATE; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let date: HttpDate = self.at.into(); let output = format!("{}", date); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -108,7 +106,7 @@ impl PartialEq for Date { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; use std::time::Duration; #[test] @@ -116,8 +114,8 @@ mod test { let now = SystemTime::now(); let date = Date::new(now); - let mut headers = Headers::new(); - date.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(date); let date = Date::from_headers(headers)?.unwrap(); @@ -128,7 +126,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(DATE, "").unwrap(); let err = Date::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); diff --git a/src/other/expect.rs b/src/other/expect.rs index 086c0d09..88690c49 100644 --- a/src/other/expect.rs +++ b/src/other/expect.rs @@ -1,5 +1,5 @@ -use crate::headers::{HeaderName, HeaderValue, Headers, EXPECT}; -use crate::{ensure_eq_status, headers::Header}; +use crate::headers::{FieldName, FieldValue, Fields, EXPECT}; +use crate::{ensure_eq_status, headers::Field}; use std::fmt::Debug; @@ -41,7 +41,7 @@ impl Expect { } /// Create an instance of `Expect` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(EXPECT) { Some(headers) => headers, None => return Ok(None), @@ -56,28 +56,26 @@ impl Expect { } } -impl Header for Expect { - fn header_name(&self) -> HeaderName { - EXPECT - } - fn header_value(&self) -> HeaderValue { +impl Field for Expect { + const FIELD_NAME: FieldName = EXPECT; + fn field_value(&self) -> FieldValue { let value = "100-continue"; // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(value.into()) } + unsafe { FieldValue::from_bytes_unchecked(value.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let expect = Expect::new(); - let mut headers = Headers::new(); - expect.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(expect); let expect = Expect::from_headers(headers)?.unwrap(); assert_eq!(expect, Expect::new()); @@ -86,7 +84,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(EXPECT, "").unwrap(); let err = Expect::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); diff --git a/src/other/referer.rs b/src/other/referer.rs index 5a860ab9..65138553 100644 --- a/src/other/referer.rs +++ b/src/other/referer.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, REFERER}; +use crate::headers::{Field, FieldName, FieldValue, Fields, REFERER}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -45,7 +45,7 @@ impl Referer { } /// Create a new instance from headers. - pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> + pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, @@ -86,30 +86,28 @@ impl Referer { } } -impl Header for Referer { - fn header_name(&self) -> HeaderName { - REFERER - } +impl Field for Referer { + const FIELD_NAME: FieldName = REFERER; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = self.location.to_string(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let referer = Referer::new(Url::parse("https://example.net/test.json")?); - let mut headers = Headers::new(); - referer.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(referer); let base_url = Url::parse("https://example.net/")?; let referer = Referer::from_headers(base_url, headers)?.unwrap(); @@ -122,7 +120,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(REFERER, "htt://") .unwrap(); @@ -133,7 +131,7 @@ mod test { #[test] fn fallback_works() -> crate::Result<()> { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(REFERER, "/test.json").unwrap(); let base_url = Url::parse("https://fallback.net/")?; @@ -143,7 +141,7 @@ mod test { &Url::parse("https://fallback.net/test.json")? ); - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(REFERER, "https://example.com/test.json") .unwrap(); diff --git a/src/other/retry_after.rs b/src/other/retry_after.rs index 21404598..f0a78695 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::{Header, HeaderName, HeaderValue, Headers, RETRY_AFTER}; +use crate::headers::{Field, FieldName, FieldValue, Fields, 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. @@ -58,7 +58,7 @@ impl RetryAfter { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let header = match headers.as_ref().get(RETRY_AFTER) { Some(headers) => headers.last(), None => return Ok(None), @@ -89,18 +89,16 @@ impl RetryAfter { } } -impl Header for RetryAfter { - fn header_name(&self) -> HeaderName { - RETRY_AFTER - } +impl Field for RetryAfter { + const FIELD_NAME: FieldName = RETRY_AFTER; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = match self.inner { RetryDirective::Duration(dur) => format!("{}", dur.as_secs()), RetryDirective::SystemTime(at) => fmt_http_date(at), }; // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -126,14 +124,14 @@ enum RetryDirective { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let retry = RetryAfter::new(Duration::from_secs(10)); - let mut headers = Headers::new(); - retry.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(retry); // `SystemTime::now` uses sub-second precision which means there's some // offset that's not encoded. @@ -151,8 +149,8 @@ mod test { let now = SystemTime::now(); let retry = RetryAfter::new_at(now + Duration::from_secs(10)); - let mut headers = Headers::new(); - retry.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(retry); // `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 5412c6e1..f528f12e 100644 --- a/src/other/source_map.rs +++ b/src/other/source_map.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, SOURCE_MAP}; +use crate::headers::{Field, FieldName, FieldValue, Fields, SOURCE_MAP}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; @@ -42,7 +42,7 @@ impl SourceMap { } /// Create a new instance from headers. - pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> + pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, @@ -83,30 +83,28 @@ impl SourceMap { } } -impl Header for SourceMap { - fn header_name(&self) -> HeaderName { - SOURCE_MAP - } +impl Field for SourceMap { + const FIELD_NAME: FieldName = SOURCE_MAP; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = self.location.to_string(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let source_map = SourceMap::new(Url::parse("https://example.net/test.json")?); - let mut headers = Headers::new(); - source_map.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(source_map); let base_url = Url::parse("https://example.net/")?; let source_map = SourceMap::from_headers(base_url, headers)?.unwrap(); @@ -119,7 +117,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(SOURCE_MAP, "htt://") .unwrap(); @@ -130,7 +128,7 @@ mod test { #[test] fn fallback_works() -> crate::Result<()> { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers.insert(SOURCE_MAP, "/test.json").unwrap(); let base_url = Url::parse("https://fallback.net/")?; @@ -140,7 +138,7 @@ mod test { &Url::parse("https://fallback.net/test.json")? ); - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(SOURCE_MAP, "https://example.com/test.json") .unwrap(); diff --git a/src/proxies/forwarded.rs b/src/proxies/forwarded.rs index 30e491b0..301c1351 100644 --- a/src/proxies/forwarded.rs +++ b/src/proxies/forwarded.rs @@ -1,14 +1,14 @@ use crate::{ - headers::{Header, HeaderName, HeaderValue, Headers, FORWARDED}, + headers::{Field, FieldName, FieldValue, Fields, FORWARDED}, parse_utils::{parse_quoted_string, parse_token}, }; use std::{borrow::Cow, convert::TryFrom, fmt::Write, net::IpAddr}; // these constants are private because they are nonstandard -const X_FORWARDED_FOR: HeaderName = HeaderName::from_lowercase_str("x-forwarded-for"); -const X_FORWARDED_PROTO: HeaderName = HeaderName::from_lowercase_str("x-forwarded-proto"); -const X_FORWARDED_BY: HeaderName = HeaderName::from_lowercase_str("x-forwarded-by"); -const X_FORWARDED_HOST: HeaderName = HeaderName::from_lowercase_str("x-forwarded-host"); +const X_FORWARDED_FOR: FieldName = FieldName::from_lowercase_str("x-forwarded-for"); +const X_FORWARDED_PROTO: FieldName = FieldName::from_lowercase_str("x-forwarded-proto"); +const X_FORWARDED_BY: FieldName = FieldName::from_lowercase_str("x-forwarded-by"); +const X_FORWARDED_HOST: FieldName = FieldName::from_lowercase_str("x-forwarded-host"); /// A rust representation of the [forwarded /// header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded). @@ -76,7 +76,7 @@ impl<'a> Forwarded<'a> { /// # Ok(()) } /// ``` - pub fn from_headers(headers: &'a impl AsRef) -> Result, ParseError> { + pub fn from_headers(headers: &'a impl AsRef) -> Result, ParseError> { if let Some(forwarded) = Self::from_forwarded_header(headers)? { Ok(Some(forwarded)) } else { @@ -109,7 +109,7 @@ impl<'a> Forwarded<'a> { /// # Ok(()) } /// ``` pub fn from_forwarded_header( - headers: &'a impl AsRef, + headers: &'a impl AsRef, ) -> Result, ParseError> { if let Some(headers) = headers.as_ref().get(FORWARDED) { Ok(Some(Self::parse(headers.as_ref().as_str())?)) @@ -143,7 +143,7 @@ impl<'a> Forwarded<'a> { /// assert!(Forwarded::from_x_headers(&request)?.is_none()); /// # Ok(()) } /// ``` - pub fn from_x_headers(headers: &'a impl AsRef) -> Result, ParseError> { + pub fn from_x_headers(headers: &'a impl AsRef) -> Result, ParseError> { let headers = headers.as_ref(); let forwarded_for: Vec> = headers @@ -358,11 +358,9 @@ impl<'a> Forwarded<'a> { } } -impl<'a> Header for Forwarded<'a> { - fn header_name(&self) -> HeaderName { - FORWARDED - } - fn header_value(&self) -> HeaderValue { +impl<'a> Field for Forwarded<'a> { + const FIELD_NAME: FieldName = FORWARDED; + fn field_value(&self) -> FieldValue { let mut output = String::new(); if let Some(by) = self.by() { write!(&mut output, "by={};", by).unwrap(); @@ -391,7 +389,7 @@ impl<'a> Header for Forwarded<'a> { output.pop(); // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -439,7 +437,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.header_value().as_str()) + f.write_str(self.field_value().as_str()) } } @@ -665,7 +663,7 @@ mod tests { ]; for input in inputs { let forwarded = Forwarded::parse(input).map_err(|_| crate::Error::new_adhoc(input))?; - let header = forwarded.header_value(); + let header = forwarded.field_value(); let parsed = Forwarded::parse(header.as_str())?; assert_eq!(forwarded, parsed); } diff --git a/src/request.rs b/src/request.rs index 6b3bb355..d9b83754 100644 --- a/src/request.rs +++ b/src/request.rs @@ -9,12 +9,11 @@ use std::task::{Context, Poll}; #[cfg(feature = "serde")] use crate::convert::{DeserializeOwned, Serialize}; use crate::headers::{ - self, HeaderName, HeaderValue, HeaderValues, Headers, Names, ToHeaderValues, Values, - CONTENT_TYPE, + self, FieldName, FieldValue, FieldValues, Fields, Names, ToFieldValues, Values, CONTENT_TYPE, }; use crate::mime::Mime; use crate::trailers::{self, Trailers}; -use crate::{Body, Extensions, Method, Url, Version}; +use crate::{Body, Extensions, Field, Method, Url, Version}; pin_project_lite::pin_project! { /// An HTTP request. @@ -31,7 +30,7 @@ pin_project_lite::pin_project! { pub struct Request { method: Method, url: Url, - headers: Headers, + headers: Fields, version: Option, #[pin] body: Body, @@ -56,7 +55,7 @@ impl Request { Self { method, url, - headers: Headers::new(), + headers: Fields::new(), version: None, body: Body::empty(), ext: Extensions::new(), @@ -408,17 +407,17 @@ impl Request { } /// Get an HTTP header. - pub fn header(&self, name: impl Into) -> Option<&HeaderValues> { - self.headers.get(name) + pub fn header(&self, name: impl Into) -> Option<&FieldValues> { + self.headers.get(name.into()) } /// Get a mutable reference to a header. - pub fn header_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { + pub fn header_mut(&mut self, name: impl Into) -> Option<&mut FieldValues> { self.headers.get_mut(name.into()) } /// Remove a header. - pub fn remove_header(&mut self, name: impl Into) -> Option { + pub fn remove_header(&mut self, name: impl Into) -> Option { self.headers.remove(name.into()) } @@ -438,12 +437,17 @@ impl Request { /// ``` pub fn insert_header( &mut self, - name: impl Into, - values: impl ToHeaderValues, - ) -> crate::Result> { + name: impl Into, + values: impl ToFieldValues, + ) -> crate::Result> { self.headers.insert(name, values) } + /// Set a typed HTTP header. + pub fn insert_typed_header(&mut self, field: F) -> Option { + self.headers.insert_typed(field) + } + /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a @@ -464,16 +468,16 @@ impl Request { /// ``` pub fn append_header( &mut self, - name: impl Into, - values: impl ToHeaderValues, + name: impl Into, + values: impl ToFieldValues, ) -> crate::Result<()> { self.headers.append(name, values) } /// Set the response MIME. // TODO: return a parsed MIME - pub fn set_content_type(&mut self, mime: Mime) -> Option { - let value: HeaderValue = mime.into(); + pub fn set_content_type(&mut self, mime: Mime) -> Option { + let value: FieldValue = mime.into(); // A Mime instance is guaranteed to be valid header name. self.insert_header(CONTENT_TYPE, value).unwrap() @@ -927,14 +931,14 @@ impl AsyncBufRead for Request { } } -impl AsRef for Request { - fn as_ref(&self) -> &Headers { +impl AsRef for Request { + fn as_ref(&self) -> &Fields { &self.headers } } -impl AsMut for Request { - fn as_mut(&mut self) -> &mut Headers { +impl AsMut for Request { + fn as_mut(&mut self) -> &mut Fields { &mut self.headers } } @@ -945,8 +949,8 @@ impl From for Body { } } -impl Index for Request { - type Output = HeaderValues; +impl Index for Request { + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -954,13 +958,13 @@ impl Index for Request { /// /// Panics if the name is not present in `Request`. #[inline] - fn index(&self, name: HeaderName) -> &HeaderValues { + fn index(&self, name: FieldName) -> &FieldValues { self.headers.index(name) } } impl Index<&str> for Request { - type Output = HeaderValues; + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -968,13 +972,13 @@ impl Index<&str> for Request { /// /// Panics if the name is not present in `Request`. #[inline] - fn index(&self, name: &str) -> &HeaderValues { + fn index(&self, name: &str) -> &FieldValues { self.headers.index(name) } } impl IntoIterator for Request { - type Item = (HeaderName, HeaderValues); + type Item = (FieldName, FieldValues); type IntoIter = headers::IntoIter; /// Returns a iterator of references over the remaining items. @@ -985,7 +989,7 @@ impl IntoIterator for Request { } impl<'a> IntoIterator for &'a Request { - type Item = (&'a HeaderName, &'a HeaderValues); + type Item = (&'a FieldName, &'a FieldValues); type IntoIter = headers::Iter<'a>; #[inline] @@ -995,7 +999,7 @@ impl<'a> IntoIterator for &'a Request { } impl<'a> IntoIterator for &'a mut Request { - type Item = (&'a HeaderName, &'a mut HeaderValues); + type Item = (&'a FieldName, &'a mut FieldValues); type IntoIter = headers::IterMut<'a>; #[inline] diff --git a/src/response.rs b/src/response.rs index 4b4a4aa6..0037bab9 100644 --- a/src/response.rs +++ b/src/response.rs @@ -10,12 +10,11 @@ use std::task::{Context, Poll}; #[cfg(feature = "serde")] use crate::convert::DeserializeOwned; use crate::headers::{ - self, HeaderName, HeaderValue, HeaderValues, Headers, Names, ToHeaderValues, Values, - CONTENT_TYPE, + self, FieldName, FieldValue, FieldValues, Fields, Names, ToFieldValues, Values, CONTENT_TYPE, }; use crate::mime::Mime; use crate::trailers::{self, Trailers}; -use crate::upgrade; +use crate::{upgrade, Field}; use crate::{Body, Extensions, StatusCode, Version}; pin_project_lite::pin_project! { @@ -36,7 +35,7 @@ pin_project_lite::pin_project! { #[derive(Debug)] pub struct Response { status: StatusCode, - headers: Headers, + headers: Fields, version: Option, has_trailers: bool, trailers_sender: Option>, @@ -66,7 +65,7 @@ impl Response { let (upgrade_sender, upgrade_receiver) = async_channel::bounded(1); Self { status, - headers: Headers::new(), + headers: Fields::new(), version: None, body: Body::empty(), trailers_sender: Some(trailers_sender), @@ -87,17 +86,17 @@ impl Response { } /// Get a mutable reference to a header. - pub fn header_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { + pub fn header_mut(&mut self, name: impl Into) -> Option<&mut FieldValues> { self.headers.get_mut(name.into()) } /// Get an HTTP header. - pub fn header(&self, name: impl Into) -> Option<&HeaderValues> { + pub fn header(&self, name: impl Into) -> Option<&FieldValues> { self.headers.get(name.into()) } /// Remove a header. - pub fn remove_header(&mut self, name: impl Into) -> Option { + pub fn remove_header(&mut self, name: impl Into) -> Option { self.headers.remove(name.into()) } @@ -117,12 +116,17 @@ impl Response { /// ``` pub fn insert_header( &mut self, - name: impl Into, - values: impl ToHeaderValues, - ) -> crate::Result> { + name: impl Into, + values: impl ToFieldValues, + ) -> crate::Result> { self.headers.insert(name, values) } + /// Set a typed HTTP header. + pub fn insert_typed_header(&mut self, field: F) -> Option { + self.headers.insert_typed(field) + } + /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a @@ -143,8 +147,8 @@ impl Response { /// ``` pub fn append_header( &mut self, - name: impl Into, - values: impl ToHeaderValues, + name: impl Into, + values: impl ToFieldValues, ) -> crate::Result<()> { self.headers.append(name, values) } @@ -377,8 +381,8 @@ impl Response { } /// Set the response MIME. - pub fn set_content_type(&mut self, mime: Mime) -> Option { - let value: HeaderValue = mime.into(); + pub fn set_content_type(&mut self, mime: Mime) -> Option { + let value: FieldValue = mime.into(); // A Mime instance is guaranteed to be valid header name. self.insert_header(CONTENT_TYPE, value).unwrap() @@ -627,14 +631,14 @@ impl AsyncBufRead for Response { } } -impl AsRef for Response { - fn as_ref(&self) -> &Headers { +impl AsRef for Response { + fn as_ref(&self) -> &Fields { &self.headers } } -impl AsMut for Response { - fn as_mut(&mut self) -> &mut Headers { +impl AsMut for Response { + fn as_mut(&mut self) -> &mut Fields { &mut self.headers } } @@ -644,8 +648,8 @@ impl From<()> for Response { Response::new(StatusCode::NoContent) } } -impl Index for Response { - type Output = HeaderValues; +impl Index for Response { + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -653,13 +657,13 @@ impl Index for Response { /// /// Panics if the name is not present in `Response`. #[inline] - fn index(&self, name: HeaderName) -> &HeaderValues { + fn index(&self, name: FieldName) -> &FieldValues { self.headers.index(name) } } impl Index<&str> for Response { - type Output = HeaderValues; + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -667,7 +671,7 @@ impl Index<&str> for Response { /// /// Panics if the name is not present in `Response`. #[inline] - fn index(&self, name: &str) -> &HeaderValues { + fn index(&self, name: &str) -> &FieldValues { self.headers.index(name) } } @@ -691,7 +695,7 @@ where } impl IntoIterator for Response { - type Item = (HeaderName, HeaderValues); + type Item = (FieldName, FieldValues); type IntoIter = headers::IntoIter; /// Returns a iterator of references over the remaining items. @@ -702,7 +706,7 @@ impl IntoIterator for Response { } impl<'a> IntoIterator for &'a Response { - type Item = (&'a HeaderName, &'a HeaderValues); + type Item = (&'a FieldName, &'a FieldValues); type IntoIter = headers::Iter<'a>; #[inline] @@ -712,7 +716,7 @@ impl<'a> IntoIterator for &'a Response { } impl<'a> IntoIterator for &'a mut Response { - type Item = (&'a HeaderName, &'a mut HeaderValues); + type Item = (&'a FieldName, &'a mut FieldValues); type IntoIter = headers::IterMut<'a>; #[inline] diff --git a/src/security/csp.rs b/src/security/csp.rs index 9478f34a..cc38a991 100644 --- a/src/security/csp.rs +++ b/src/security/csp.rs @@ -1,4 +1,4 @@ -use crate::headers::Headers; +use crate::headers::Fields; use std::collections::HashMap; use std::fmt; @@ -355,7 +355,7 @@ impl ContentSecurityPolicy { } /// Sets the `Content-Security-Policy` (CSP) HTTP header to prevent cross-site injections - pub fn apply(&mut self, mut headers: impl AsMut) { + pub fn apply(&mut self, mut headers: impl AsMut) { let name = if self.report_only_flag { "Content-Security-Policy-Report-Only" } else { diff --git a/src/security/mod.rs b/src/security/mod.rs index 2cb9b6a1..c581b1da 100644 --- a/src/security/mod.rs +++ b/src/security/mod.rs @@ -15,7 +15,7 @@ // //! assert_eq!(res["X-XSS-Protection"], "1; mode=block"); //! ``` -use crate::headers::{HeaderName, HeaderValue, Headers}; +use crate::headers::{FieldName, FieldValue, Fields}; mod csp; mod strict_transport_security; @@ -41,7 +41,7 @@ pub use timing_allow_origin::TimingAllowOrigin; // /// assert_eq!(headers["X-Content-Type-Options"], "nosniff"); // /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block"); // /// ``` -pub fn default(mut headers: impl AsMut) { +pub fn default(mut headers: impl AsMut) { dns_prefetch_control(&mut headers); nosniff(&mut headers); frameguard(&mut headers, None); @@ -63,7 +63,7 @@ pub fn default(mut headers: impl AsMut) { // /// assert_eq!(headers["X-DNS-Prefetch-Control"], "on"); // /// ``` #[inline] -pub fn dns_prefetch_control(mut headers: impl AsMut) { +pub fn dns_prefetch_control(mut headers: impl AsMut) { // This will never fail, could use an unsafe version of insert. headers .as_mut() @@ -93,7 +93,7 @@ pub enum FrameOptions { // /// assert_eq!(headers["X-Frame-Options"], "sameorigin"); // /// ``` #[inline] -pub fn frameguard(mut headers: impl AsMut, guard: Option) { +pub fn frameguard(mut headers: impl AsMut, guard: Option) { let kind = match guard { None | Some(FrameOptions::SameOrigin) => "sameorigin", Some(FrameOptions::Deny) => "deny", @@ -117,8 +117,8 @@ pub fn frameguard(mut headers: impl AsMut, guard: Option) // /// assert_eq!(headers.get("X-Powered-By"), None); // /// ``` #[inline] -pub fn powered_by(mut headers: impl AsMut, value: Option) { - let name = HeaderName::from_lowercase_str("X-Powered-By"); +pub fn powered_by(mut headers: impl AsMut, value: Option) { + let name = FieldName::from_lowercase_str("X-Powered-By"); match value { Some(value) => { // Can never fail as value is already a HeaderValue, could use unsafe version of insert @@ -146,7 +146,7 @@ pub fn powered_by(mut headers: impl AsMut, value: Option) // /// assert_eq!(headers["Strict-Transport-Security"], "max-age=5184000"); // /// ``` #[inline] -pub fn hsts(mut headers: impl AsMut) { +pub fn hsts(mut headers: impl AsMut) { // Never fails, could use unsafe version of insert headers .as_mut() @@ -168,7 +168,7 @@ pub fn hsts(mut headers: impl AsMut) { // /// assert_eq!(headers["X-Content-Type-Options"], "nosniff"); // /// ``` #[inline] -pub fn nosniff(mut headers: impl AsMut) { +pub fn nosniff(mut headers: impl AsMut) { // Never fails, could use unsafe verison of insert. headers .as_mut() @@ -189,7 +189,7 @@ pub fn nosniff(mut headers: impl AsMut) { // /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block"); // /// ``` #[inline] -pub fn xss_filter(mut headers: impl AsMut) { +pub fn xss_filter(mut headers: impl AsMut) { // Never fails, could use unsafe version of insert. headers .as_mut() @@ -236,7 +236,7 @@ pub enum ReferrerOptions { // /// assert_eq!(referrerValues.sort(), vec!("unsafe-url", "no-referrer").sort()); // /// ``` #[inline] -pub fn referrer_policy(mut headers: impl AsMut, referrer: Option) { +pub fn referrer_policy(mut headers: impl AsMut, referrer: Option) { let policy = match referrer { None | Some(ReferrerOptions::NoReferrer) => "no-referrer", Some(ReferrerOptions::NoReferrerDowngrade) => "no-referrer-when-downgrade", diff --git a/src/security/strict_transport_security.rs b/src/security/strict_transport_security.rs index f512fa17..7e1ead7a 100644 --- a/src/security/strict_transport_security.rs +++ b/src/security/strict_transport_security.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers}; +use crate::headers::{Field, FieldName, FieldValue, Fields}; use crate::Status; use crate::headers::STRICT_TRANSPORT_SECURITY; @@ -70,12 +70,10 @@ impl StrictTransportSecurity { } } -impl Header for StrictTransportSecurity { - fn header_name(&self) -> HeaderName { - STRICT_TRANSPORT_SECURITY - } +impl Field for StrictTransportSecurity { + const FIELD_NAME: FieldName = STRICT_TRANSPORT_SECURITY; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let max_age = self.max_age.as_secs(); let mut output = format!("max-age={}", max_age); if self.include_subdomains { @@ -86,14 +84,14 @@ impl Header for StrictTransportSecurity { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } // TODO: move to new header traits impl StrictTransportSecurity { /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(STRICT_TRANSPORT_SECURITY) { Some(headers) => headers, None => return Ok(None), @@ -170,7 +168,7 @@ mod test { let stc = StrictTransportSecurity::new(duration); let mut headers = Response::new(200); - stc.apply_header(&mut headers); + headers.insert_typed_header(stc); let stc = StrictTransportSecurity::from_headers(headers)?.unwrap(); diff --git a/src/security/timing_allow_origin.rs b/src/security/timing_allow_origin.rs index 443427fb..a5524341 100644 --- a/src/security/timing_allow_origin.rs +++ b/src/security/timing_allow_origin.rs @@ -17,7 +17,7 @@ //! origins.push(Url::parse("https://example.com")?); //! //! let mut res = Response::new(200); -//! origins.apply_header(&mut res); +//! res.insert_typed_header(origins); //! //! let origins = TimingAllowOrigin::from_headers(res)?.unwrap(); //! let origin = origins.iter().next().unwrap(); @@ -26,7 +26,7 @@ //! # Ok(()) } //! ``` -use crate::headers::{Header, HeaderName, HeaderValue, Headers, TIMING_ALLOW_ORIGIN}; +use crate::headers::{Field, FieldName, FieldValue, Fields, TIMING_ALLOW_ORIGIN}; use crate::{Status, Url}; use std::fmt::Write; @@ -77,7 +77,7 @@ impl TimingAllowOrigin { /// # Implementation note /// /// A header value of `"null"` is treated the same as if no header was sent. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(TIMING_ALLOW_ORIGIN) { Some(headers) => headers, None => return Ok(None), @@ -131,11 +131,9 @@ impl TimingAllowOrigin { } } -impl Header for TimingAllowOrigin { - fn header_name(&self) -> HeaderName { - TIMING_ALLOW_ORIGIN - } - fn header_value(&self) -> HeaderValue { +impl Field for TimingAllowOrigin { + const FIELD_NAME: FieldName = TIMING_ALLOW_ORIGIN; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, origin) in self.origins.iter().enumerate() { match n { @@ -152,7 +150,7 @@ impl Header for TimingAllowOrigin { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -258,15 +256,15 @@ impl Debug for TimingAllowOrigin { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let mut origins = TimingAllowOrigin::new(); origins.push(Url::parse("https://example.com")?); - let mut headers = Headers::new(); - origins.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(origins); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let origin = origins.iter().next().unwrap(); @@ -280,8 +278,8 @@ mod test { origins.push(Url::parse("https://example.com")?); origins.push(Url::parse("https://mozilla.org/")?); - let mut headers = Headers::new(); - origins.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(origins); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let mut origins = origins.iter(); @@ -295,7 +293,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(TIMING_ALLOW_ORIGIN, "server; ") .unwrap(); @@ -309,8 +307,8 @@ mod test { origins.push(Url::parse("https://example.com")?); origins.set_wildcard(true); - let mut headers = Headers::new(); - origins.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(origins); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); assert!(origins.wildcard()); diff --git a/src/server/allow.rs b/src/server/allow.rs index 9377a3f6..0f670140 100644 --- a/src/server/allow.rs +++ b/src/server/allow.rs @@ -1,6 +1,6 @@ //! List the set of methods supported by a resource. -use crate::headers::{Header, HeaderName, HeaderValue, Headers, ALLOW}; +use crate::headers::{Field, FieldName, FieldValue, Fields, ALLOW}; use crate::Method; use std::collections::{hash_set, HashSet}; @@ -49,7 +49,7 @@ impl Allow { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = HashSet::new(); let headers = match headers.as_ref().get(ALLOW) { Some(headers) => headers, @@ -84,11 +84,9 @@ impl Allow { } } -impl Header for Allow { - fn header_name(&self) -> HeaderName { - ALLOW - } - fn header_value(&self) -> HeaderValue { +impl Field for Allow { + const FIELD_NAME: FieldName = ALLOW; + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, method) in self.entries.iter().enumerate() { match n { @@ -98,7 +96,7 @@ impl Header for Allow { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -175,7 +173,7 @@ impl Debug for Allow { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { @@ -183,8 +181,8 @@ mod test { allow.insert(Method::Put); allow.insert(Method::Post); - let mut headers = Headers::new(); - allow.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(allow); let allow = Allow::from_headers(headers)?.unwrap(); assert!(allow.contains(Method::Put)); diff --git a/src/status_code.rs b/src/status_code.rs index a86d8ac6..aa42ab7a 100644 --- a/src/status_code.rs +++ b/src/status_code.rs @@ -582,7 +582,6 @@ mod serde { where E: DeError, { - use std::convert::TryFrom; match StatusCode::try_from(v) { Ok(status_code) => Ok(status_code), Err(_) => Err(DeError::invalid_value( diff --git a/src/trace/server_timing/metric.rs b/src/trace/server_timing/metric.rs index ed55c189..407c8b95 100644 --- a/src/trace/server_timing/metric.rs +++ b/src/trace/server_timing/metric.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use crate::headers::HeaderValue; +use crate::headers::FieldValue; /// An individual entry into `ServerTiming`. // @@ -52,8 +52,8 @@ impl Metric { } } -impl From for HeaderValue { - fn from(entry: Metric) -> HeaderValue { +impl From for FieldValue { + fn from(entry: Metric) -> FieldValue { let mut string = entry.name; // Format a `Duration` into the format that the spec expects. @@ -69,14 +69,14 @@ impl From for HeaderValue { }; // SAFETY: we validate that the values are valid ASCII on creation. - unsafe { HeaderValue::from_bytes_unchecked(string.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(string.into_bytes()) } } } #[cfg(test)] mod test { use super::*; - use crate::headers::HeaderValue; + use crate::headers::FieldValue; use std::time::Duration; #[test] @@ -86,16 +86,16 @@ mod test { let dur = Duration::from_secs(1); let desc = String::from("A server timing"); - let val: HeaderValue = Metric::new(name.clone(), None, None)?.into(); + let val: FieldValue = Metric::new(name.clone(), None, None)?.into(); assert_eq!(val, "Server"); - let val: HeaderValue = Metric::new(name.clone(), Some(dur), None)?.into(); + let val: FieldValue = Metric::new(name.clone(), Some(dur), None)?.into(); assert_eq!(val, "Server; dur=1000"); - let val: HeaderValue = Metric::new(name.clone(), None, Some(desc.clone()))?.into(); + let val: FieldValue = Metric::new(name.clone(), None, Some(desc.clone()))?.into(); assert_eq!(val, r#"Server; desc="A server timing""#); - let val: HeaderValue = Metric::new(name.clone(), Some(dur), Some(desc.clone()))?.into(); + let val: FieldValue = Metric::new(name.clone(), Some(dur), Some(desc.clone()))?.into(); assert_eq!(val, r#"Server; dur=1000; desc="A server timing""#); Ok(()) } diff --git a/src/trace/server_timing/mod.rs b/src/trace/server_timing/mod.rs index ddd6c79a..50f60b1f 100644 --- a/src/trace/server_timing/mod.rs +++ b/src/trace/server_timing/mod.rs @@ -32,7 +32,7 @@ use std::iter::Iterator; use std::slice; -use crate::headers::{Header, HeaderName, HeaderValue, Headers, SERVER_TIMING}; +use crate::headers::{Field, FieldName, FieldValue, Fields, SERVER_TIMING}; /// Metrics and descriptions for the given request-response cycle. /// @@ -72,7 +72,7 @@ impl ServerTiming { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut timings = vec![]; let headers = match headers.as_ref().get(SERVER_TIMING) { Some(headers) => headers, @@ -105,15 +105,13 @@ impl ServerTiming { } } -impl Header for ServerTiming { - fn header_name(&self) -> HeaderName { - SERVER_TIMING - } +impl Field for ServerTiming { + const FIELD_NAME: FieldName = SERVER_TIMING; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, timing) in self.timings.iter().enumerate() { - let timing: HeaderValue = timing.clone().into(); + let timing: FieldValue = timing.clone().into(); match n { 0 => write!(output, "{}", timing).unwrap(), _ => write!(output, ", {}", timing).unwrap(), @@ -121,7 +119,7 @@ impl Header for ServerTiming { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -217,15 +215,15 @@ impl<'a> Iterator for IterMut<'a> { #[cfg(test)] mod test { use super::*; - use crate::headers::Headers; + use crate::headers::Fields; #[test] fn smoke() -> crate::Result<()> { let mut timings = ServerTiming::new(); timings.push(Metric::new("server".to_owned(), None, None)?); - let mut headers = Headers::new(); - timings.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(timings); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); @@ -238,8 +236,8 @@ mod test { let mut timings = ServerTiming::new(); timings.push(Metric::new("server".to_owned(), None, None)?); - let mut headers = Headers::new(); - timings.apply_header(&mut headers); + let mut headers = Fields::new(); + headers.insert_typed(timings); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); @@ -249,7 +247,7 @@ mod test { #[test] fn bad_request_on_parse_error() { - let mut headers = Headers::new(); + let mut headers = Fields::new(); headers .insert(SERVER_TIMING, "server; ") .unwrap(); diff --git a/src/trace/trace_context.rs b/src/trace/trace_context.rs index 3bc062f7..c887aa0c 100644 --- a/src/trace/trace_context.rs +++ b/src/trace/trace_context.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::headers::{Header, HeaderName, HeaderValue, Headers, TRACEPARENT}; +use crate::headers::{Field, FieldName, FieldValue, Fields, TRACEPARENT}; use crate::Status; /// Extract and apply [Trace-Context](https://w3c.github.io/trace-context/) headers. @@ -99,7 +99,7 @@ impl TraceContext { /// # /// # Ok(()) } /// ``` - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = headers.as_ref(); let traceparent = match headers.get(TRACEPARENT) { @@ -195,14 +195,12 @@ impl TraceContext { } } -impl Header for TraceContext { - fn header_name(&self) -> HeaderName { - TRACEPARENT - } +impl Field for TraceContext { + const FIELD_NAME: FieldName = TRACEPARENT; - fn header_value(&self) -> HeaderValue { + fn field_value(&self) -> FieldValue { let output = format!("{}", self); - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } } @@ -222,7 +220,7 @@ mod test { #[test] fn default() -> crate::Result<()> { - let mut headers = crate::headers::Headers::new(); + let mut headers = crate::headers::Fields::new(); headers.insert(TRACEPARENT, "00-01-deadbeef-00").unwrap(); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert_eq!(context.version(), 0); @@ -244,7 +242,7 @@ mod test { #[test] fn not_sampled() -> crate::Result<()> { - let mut headers = crate::headers::Headers::new(); + let mut headers = crate::headers::Fields::new(); headers.insert(TRACEPARENT, "00-01-02-00").unwrap(); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert!(!context.sampled()); @@ -253,7 +251,7 @@ mod test { #[test] fn sampled() -> crate::Result<()> { - let mut headers = crate::headers::Headers::new(); + let mut headers = crate::headers::Fields::new(); headers.insert(TRACEPARENT, "00-01-02-01").unwrap(); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert!(context.sampled()); diff --git a/src/trailers.rs b/src/trailers.rs index c65801c6..b662b922 100644 --- a/src/trailers.rs +++ b/src/trailers.rs @@ -48,9 +48,7 @@ //! - [MDN HTTP Headers: Trailer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer) //! - [HTTP/2 spec: HTTP Sequence](https://http2.github.io/http2-spec/#HttpSequence) -use crate::headers::{ - HeaderName, HeaderValues, Headers, Iter, IterMut, Names, ToHeaderValues, Values, -}; +use crate::headers::{FieldName, FieldValues, Fields, Iter, IterMut, Names, ToFieldValues, Values}; use futures_lite::Stream; use std::convert::Into; @@ -62,14 +60,14 @@ use std::task::{Context, Poll}; /// A collection of trailing HTTP headers. #[derive(Debug)] pub struct Trailers { - headers: Headers, + headers: Fields, } impl Trailers { /// Create a new instance of `Trailers`. pub fn new() -> Self { Self { - headers: Headers::new(), + headers: Fields::new(), } } @@ -89,9 +87,9 @@ impl Trailers { /// ``` pub fn insert( &mut self, - name: impl Into, - values: impl ToHeaderValues, - ) -> crate::Result> { + name: impl Into, + values: impl ToFieldValues, + ) -> crate::Result> { self.headers.insert(name, values) } @@ -114,25 +112,25 @@ impl Trailers { /// ``` pub fn append( &mut self, - name: impl Into, - values: impl ToHeaderValues, + name: impl Into, + values: impl ToFieldValues, ) -> crate::Result<()> { self.headers.append(name, values) } /// Get a reference to a header. - pub fn get(&self, name: impl Into) -> Option<&HeaderValues> { - self.headers.get(name) + pub fn get(&self, name: impl Into) -> Option<&FieldValues> { + self.headers.get(name.into()) } /// Get a mutable reference to a header. - pub fn get_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { - self.headers.get_mut(name) + pub fn get_mut(&mut self, name: impl Into) -> Option<&mut FieldValues> { + self.headers.get_mut(name.into()) } /// Remove a header. - pub fn remove(&mut self, name: impl Into) -> Option { - self.headers.remove(name) + pub fn remove(&mut self, name: impl Into) -> Option { + self.headers.remove(name.into()) } /// An iterator visiting all header pairs in arbitrary order. @@ -160,7 +158,7 @@ impl Trailers { impl Clone for Trailers { fn clone(&self) -> Self { Self { - headers: Headers { + headers: Fields { headers: self.headers.headers.clone(), }, } @@ -168,7 +166,7 @@ impl Clone for Trailers { } impl Deref for Trailers { - type Target = Headers; + type Target = Fields; fn deref(&self) -> &Self::Target { &self.headers @@ -181,8 +179,8 @@ impl DerefMut for Trailers { } } -impl Index for Trailers { - type Output = HeaderValues; +impl Index for Trailers { + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -190,13 +188,13 @@ impl Index for Trailers { /// /// Panics if the name is not present in `Trailers`. #[inline] - fn index(&self, name: HeaderName) -> &HeaderValues { + fn index(&self, name: FieldName) -> &FieldValues { self.headers.index(name) } } impl Index<&str> for Trailers { - type Output = HeaderValues; + type Output = FieldValues; /// Returns a reference to the value corresponding to the supplied name. /// @@ -204,7 +202,7 @@ impl Index<&str> for Trailers { /// /// Panics if the name is not present in `Trailers`. #[inline] - fn index(&self, name: &str) -> &HeaderValues { + fn index(&self, name: &str) -> &FieldValues { self.headers.index(name) } } diff --git a/src/transfer/encoding.rs b/src/transfer/encoding.rs index c64b3a45..e0bb220e 100644 --- a/src/transfer/encoding.rs +++ b/src/transfer/encoding.rs @@ -1,4 +1,4 @@ -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use std::fmt::{self, Display}; /// Available compression algorithms. @@ -56,9 +56,9 @@ impl Display for Encoding { } } -impl From for HeaderValue { +impl From for FieldValue { fn from(directive: Encoding) -> Self { let s = directive.to_string(); - unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) } } } diff --git a/src/transfer/encoding_proposal.rs b/src/transfer/encoding_proposal.rs index 72f083d5..710d8ed3 100644 --- a/src/transfer/encoding_proposal.rs +++ b/src/transfer/encoding_proposal.rs @@ -1,5 +1,5 @@ use crate::ensure; -use crate::headers::HeaderValue; +use crate::headers::FieldValue; use crate::transfer::Encoding; use crate::utils::parse_weight; @@ -110,13 +110,13 @@ impl PartialOrd for EncodingProposal { } } -impl From for HeaderValue { - fn from(entry: EncodingProposal) -> HeaderValue { +impl From for FieldValue { + fn from(entry: EncodingProposal) -> FieldValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.encoding, weight), None => entry.encoding.to_string(), }; - unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } + unsafe { FieldValue::from_bytes_unchecked(s.into_bytes()) } } } diff --git a/src/transfer/te.rs b/src/transfer/te.rs index acd1af80..b07f1876 100644 --- a/src/transfer/te.rs +++ b/src/transfer/te.rs @@ -1,4 +1,4 @@ -use crate::headers::{self, Header, HeaderName, HeaderValue, Headers}; +use crate::headers::{self, Field, FieldName, FieldValue, Fields}; use crate::transfer::{Encoding, EncodingProposal, TransferEncoding}; use crate::utils::sort_by_weight; use crate::{Error, StatusCode}; @@ -52,7 +52,7 @@ impl TE { } /// Create an instance of `TE` from a `Headers` instance. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(headers::TE) { Some(headers) => headers, @@ -151,15 +151,11 @@ impl TE { } } -impl Header for TE { - fn header_name(&self) -> HeaderName { - headers::TE - } - - fn header_value(&self) -> HeaderValue { +impl Field for TE { + fn field_value(&self) -> FieldValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { - let directive: HeaderValue = (*directive).into(); + let directive: FieldValue = (*directive).into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), @@ -174,8 +170,10 @@ impl Header for TE { } // SAFETY: the internal string is validated to be ASCII. - unsafe { HeaderValue::from_bytes_unchecked(output.into()) } + unsafe { FieldValue::from_bytes_unchecked(output.into()) } } + + const FIELD_NAME: FieldName = headers::TE; } impl IntoIterator for TE { @@ -289,7 +287,7 @@ mod test { accept.push(Encoding::Gzip); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = TE::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); @@ -302,7 +300,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -316,7 +314,7 @@ mod test { accept.set_wildcard(true); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); @@ -331,7 +329,7 @@ mod test { accept.push(Encoding::Brotli); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let accept = TE::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); @@ -348,7 +346,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); - accept.apply_header(&mut headers); + headers.insert_typed_header(accept); let mut accept = TE::from_headers(headers)?.unwrap(); accept.sort(); @@ -367,7 +365,7 @@ mod test { accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); - accept.apply_header(&mut res); + res.insert_typed_header(accept); 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 7fede4aa..eb0d6d87 100644 --- a/src/transfer/transfer_encoding.rs +++ b/src/transfer/transfer_encoding.rs @@ -1,4 +1,4 @@ -use crate::headers::{Header, HeaderName, HeaderValue, Headers, TRANSFER_ENCODING}; +use crate::headers::{Field, FieldName, FieldValue, Fields, TRANSFER_ENCODING}; use crate::transfer::{Encoding, EncodingProposal}; use std::fmt::{self, Debug}; @@ -40,7 +40,7 @@ impl TransferEncoding { } /// Create a new instance from headers. - pub fn from_headers(headers: impl AsRef) -> crate::Result> { + pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(TRANSFER_ENCODING) { Some(headers) => headers, None => return Ok(None), @@ -64,11 +64,9 @@ impl TransferEncoding { } } -impl Header for TransferEncoding { - fn header_name(&self) -> HeaderName { - TRANSFER_ENCODING - } - fn header_value(&self) -> HeaderValue { +impl Field for TransferEncoding { + const FIELD_NAME: FieldName = TRANSFER_ENCODING; + fn field_value(&self) -> FieldValue { self.inner.into() } }