Compare commits

...

7 Commits

Author SHA1 Message Date
Yoshua Wuyts eb1f011341 finish adding the `Field` trait 2022-06-19 03:56:50 +02:00
Yoshua Wuyts b0c50be2db add typed insert methods 2022-06-19 03:40:46 +02:00
Yoshua Wuyts 7a1a518179 rename stuffs 2022-06-19 03:26:50 +02:00
Yoshua Wuyts fe27416dd6 undo some of the header access changes 2022-06-19 03:01:00 +02:00
Yoshua Wuyts 04953ce909 use `const FIELD_NAME` 2022-06-19 02:30:33 +02:00
Yoshua Wuyts a9fc3590c3 update more header stuff 2022-06-19 02:23:22 +02:00
Yoshua Wuyts d9120102b2 Replace the use of "header" terminology with "field"
From https://www.rfc-editor.org/rfc/rfc9110.html#section-6.3:

> 6.3. Header Fields
> Fields (Section 5) that are sent or received before the content are referred to as  "header fields" (or just "headers", colloquially).

> The "header section" of a message consists of a sequence of header field lines. Each header field might modify or extend message semantics, describe the sender, define the content, or provide additional context.

> Note: We refer to named fields specifically as a "header field" when they are only allowed to be sent in the header section.

This means that our use of "header" is currently wrong. Because "fields" can be sent in both the headers and trailers section, solely referring to them as "headers" is wrong. We can work around confusion by linking to "headers" using a doc alias.
2022-06-18 15:39:13 +02:00
63 changed files with 807 additions and 872 deletions

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(authz);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(authz);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(authz);
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, "<nori ate the tag. yum.>")
.unwrap();

22
src/cache/age.rs vendored
View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(age);
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, "<nori ate the tag. yum.>").unwrap();
let err = Age::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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()) }
}
}

View File

@ -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<CacheDirective> for HeaderValue {
impl From<CacheDirective> 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()),

View File

@ -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();
headers.insert(entries);
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);

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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());

22
src/cache/expires.rs vendored
View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(expires);
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, "<nori ate the tag. yum.>").unwrap();
let err = Expires::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(etag);
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();
headers.insert(etag);
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, "<nori ate the tag. yum.>").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);

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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());

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(expires);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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());

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(expires);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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_headers.insert(modified);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<HeaderName>,
entries: Vec<FieldName>,
wildcard: bool,
}
@ -51,7 +51,7 @@ impl Vary {
}
/// Create a new instance from headers.
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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<HeaderName>) -> crate::Result<()> {
pub fn push(&mut self, directive: impl Into<FieldName>) -> 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<HeaderName>,
inner: std::vec::IntoIter<FieldName>,
}
impl Iterator for IntoIter {
type Item = HeaderName;
type Item = FieldName;
fn next(&mut self) -> Option<Self::Item> {
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::Item> {
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::Item> {
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());

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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()
}
}

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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_headers.insert(len);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<U>(base_url: U, headers: impl AsRef<Headers>) -> crate::Result<Option<Self>>
pub fn from_headers<U>(base_url: U, headers: impl AsRef<Fields>) -> crate::Result<Option<Self>>
where
U: TryInto<Url>,
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_headers.insert(location);
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://<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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<Mime> 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();
headers.insert(ct);
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, "<nori ate the tag. yum.>")
.unwrap();

View File

@ -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<Encoding> for HeaderValue {
impl From<Encoding> 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()) }
}
}

View File

@ -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<EncodingProposal> for HeaderValue {
fn from(entry: EncodingProposal) -> HeaderValue {
impl From<EncodingProposal> 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()) }
}
}

View File

@ -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<MediaTypeProposal> for HeaderValue {
fn from(entry: MediaTypeProposal) -> HeaderValue {
impl From<MediaTypeProposal> 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()) }
}
}

View File

@ -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");

View File

@ -1,58 +1,23 @@
use std::ops::Deref;
use crate::headers::{HeaderName, HeaderValue};
use crate::headers::{FieldName, FieldValue};
/// A trait representing a [`HeaderName`] and [`HeaderValue`] pair.
pub trait Header
///
/// # 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 name.
fn header_name(&self) -> HeaderName;
/// The header's field name.
const FIELD_NAME: FieldName;
/// Access the header's value.
fn header_value(&self) -> HeaderValue;
fn field_value(&self) -> FieldValue;
/// Create an instance from a header value.
fn from_parts(name: HeaderName, value: HeaderValue) -> crate::Result<Self>;
}
impl Header for (HeaderName, HeaderValue) {
fn header_name(&self) -> HeaderName {
self.0
}
fn header_value(&self) -> HeaderValue {
self.1
}
fn from_parts(name: HeaderName, value: HeaderValue) -> crate::Result<Self> {
Ok((name, value))
}
}
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()
}
fn from_parts(name: HeaderName, value: HeaderValue) -> crate::Result<Self> {
T::from_parts(name, value)
}
}
#[cfg(test)]
mod test {
use super::*;
#[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<Self, ()>;
}

View File

@ -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<u8>) -> 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<Self, Self::Err> {
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<T: Header> From<T> for HeaderName {
fn from(header: T) -> HeaderName {
header.header_name()
impl<T: Field> From<T> 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<str> for HeaderName {
impl PartialEq<str> 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<String> for HeaderName {
impl 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,
}
}
}
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\"");
}
}

View File

@ -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<Mime> for HeaderValue {
impl From<Mime> for FieldValue {
fn from(mime: Mime) -> Self {
HeaderValue {
FieldValue {
inner: format!("{}", mime),
}
}
}
#[cfg(feature = "cookies")]
impl From<Cookie<'_>> for HeaderValue {
impl From<Cookie<'_>> 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<Self, Self::Error> {
@ -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<str> for HeaderValue {
impl PartialEq<str> 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<String> for HeaderValue {
impl PartialEq<String> 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<HeaderValues> for HeaderValue {
fn from(mut other: HeaderValues) -> Self {
impl From<FieldValues> 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\"");
}
}

View File

@ -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<HeaderValue>,
pub struct FieldValues {
pub(crate) inner: Vec<FieldValue>,
}
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<I: SliceIndex<[HeaderValue]>> Index<I> for HeaderValues {
impl<I: SliceIndex<[FieldValue]>> Index<I> for FieldValues {
type Output = I::Output;
#[inline]
@ -65,21 +65,21 @@ impl<I: SliceIndex<[HeaderValue]>> Index<I> for HeaderValues {
}
}
impl FromIterator<HeaderValue> for HeaderValues {
fn from_iter<I>(iter: I) -> HeaderValues
impl FromIterator<FieldValue> for FieldValues {
fn from_iter<I>(iter: I) -> FieldValues
where
I: IntoIterator<Item = HeaderValue>,
I: IntoIterator<Item = FieldValue>,
{
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<str> for HeaderValues {
impl PartialEq<str> 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<String> for HeaderValues {
impl PartialEq<String> 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<HeaderValue> for HeaderValues {
fn from(other: HeaderValue) -> Self {
impl From<FieldValue> for FieldValues {
fn from(other: FieldValue) -> Self {
Self { inner: vec![other] }
}
}
impl AsRef<HeaderValue> for HeaderValues {
fn as_ref(&self) -> &HeaderValue {
impl AsRef<FieldValue> for FieldValues {
fn as_ref(&self) -> &FieldValue {
&self.inner[0]
}
}
impl AsMut<HeaderValue> for HeaderValues {
fn as_mut(&mut self) -> &mut HeaderValue {
impl AsMut<FieldValue> 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<Vec<HeaderValue>> for HeaderValues {
fn from(headers: Vec<HeaderValue>) -> Self {
impl From<Vec<FieldValue>> for FieldValues {
fn from(headers: Vec<FieldValue>) -> Self {
Self { inner: headers }
}
}
impl IntoIterator for HeaderValues {
type Item = HeaderValue;
type IntoIter = std::vec::IntoIter<HeaderValue>;
impl IntoIterator for FieldValues {
type Item = FieldValue;
type IntoIter = std::vec::IntoIter<FieldValue>;
#[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"]"#);

View File

@ -8,16 +8,14 @@ use std::ops::Index;
use std::str::FromStr;
use crate::headers::{
Header, HeaderName, HeaderValues, IntoIter, Iter, IterMut, Names, ToHeaderValues, Values,
Field, FieldName, FieldValues, IntoIter, Iter, IterMut, Names, ToFieldValues, Values,
};
use super::HeaderValue;
/// 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<Headers>`
/// and `AsMut<Headers>` 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<Fields>`
/// and `AsMut<Fields>` so functions that want to modify headers can be generic
/// over either of these traits.
///
/// # Examples
@ -30,11 +28,11 @@ use super::HeaderValue;
/// assert_eq!(res["hello"], "foo0");
/// ```
#[derive(Clone)]
pub struct Headers {
pub(crate) headers: HashMap<HeaderName, HeaderValue>,
pub struct Fields {
pub(crate) headers: HashMap<FieldName, FieldValues>,
}
impl Headers {
impl Fields {
/// Create a new instance.
pub(crate) fn new() -> Self {
Self {
@ -45,25 +43,58 @@ impl Headers {
/// Insert a header into the headers.
///
/// Not that this will replace all header values for a given header name.
pub fn insert<H: Header>(&mut self, header: H) -> Option<HeaderValue> {
let name = header.header_name();
let value = header.header_value();
self.headers.insert(name, value)
pub fn insert(
&mut self,
name: impl Into<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<Option<FieldValues>> {
let name = name.into();
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<F: Field>(&mut self, field: F) -> Option<FieldValues> {
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<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<()> {
let name = name.into();
match self.get_mut(&name) {
Some(headers) => {
let mut values: FieldValues = values.to_field_values()?.collect();
headers.append(&mut values);
}
None => {
self.insert(name, values)?;
}
}
Ok(())
}
/// Get a reference to a header.
pub fn get<H: Header>(&self, name: HeaderName) -> Option<H> {
let value = self.headers.get(&name)?;
H::from_parts(name, value.clone()).ok()
pub fn get(&self, name: impl Into<FieldName>) -> Option<&FieldValues> {
self.headers.get(&name.into())
}
/// Get a mutable reference to a header.
pub fn get_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
pub fn get_mut(&mut self, name: impl Into<FieldName>) -> Option<&mut FieldValues> {
self.headers.get_mut(&name.into())
}
/// Remove a header.
pub fn remove<H: Header>(&mut self, name: HeaderName) -> Option<HeaderValue> {
pub fn remove(&mut self, name: impl Into<FieldName>) -> Option<FieldValues> {
self.headers.remove(&name.into())
}
@ -95,37 +126,37 @@ impl Headers {
}
}
impl Index<HeaderName> for Headers {
type Output = HeaderValues;
impl Index<FieldName> 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.
@ -137,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]
@ -147,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]
@ -157,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<Headers> for Headers {
fn as_ref(&self) -> &Headers {
impl AsRef<Fields> for Fields {
fn as_ref(&self) -> &Fields {
self
}
}
impl AsMut<Headers> for Headers {
fn as_mut(&mut self) -> &mut Headers {
impl AsMut<Fields> for Fields {
fn as_mut(&mut self) -> &mut Fields {
self
}
}
@ -180,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();
@ -201,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");
@ -209,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"]}"#);

View File

@ -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<HeaderName, HeaderValues>,
pub(super) inner: hash_map::IntoIter<FieldName, FieldValues>,
}
impl Iterator for IntoIter {
type Item = (HeaderName, HeaderValues);
type Item = (FieldName, FieldValues);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()

View File

@ -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::Item> {
self.inner.next()

View File

@ -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::Item> {
self.inner.next()

View File

@ -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;

View File

@ -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::Item> {
self.inner.next()

View File

@ -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<Item = HeaderValue>;
type Iter: Iterator<Item = FieldValue>;
/// Converts this object to an iterator of resolved `HeaderValues`.
fn to_header_values(&self) -> crate::Result<Self::Iter>;
fn to_field_values(&self) -> crate::Result<Self::Iter>;
}
impl<T: Header> ToHeaderValues for T {
type Iter = option::IntoIter<HeaderValue>;
impl<T: Field> ToFieldValues for T {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
Ok(Some(self.header_value()).into_iter())
fn to_field_values(&self) -> crate::Result<Self::Iter> {
Ok(Some(self.field_value()).into_iter())
}
}
impl ToHeaderValues for HeaderValue {
type Iter = option::IntoIter<HeaderValue>;
impl ToFieldValues for FieldValue {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
fn to_field_values(&self) -> crate::Result<Self::Iter> {
Ok(Some(self.clone()).into_iter())
}
}
impl<'a> ToHeaderValues for &'a HeaderValues {
impl<'a> ToFieldValues for &'a FieldValues {
type Iter = iter::Cloned<Values<'a>>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
fn to_field_values(&self) -> crate::Result<Self::Iter> {
Ok(self.iter().cloned())
}
}
impl<'a> ToHeaderValues for &'a [HeaderValue] {
type Iter = iter::Cloned<slice::Iter<'a, HeaderValue>>;
impl<'a> ToFieldValues for &'a [FieldValue] {
type Iter = iter::Cloned<slice::Iter<'a, FieldValue>>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
fn to_field_values(&self) -> crate::Result<Self::Iter> {
Ok(self.iter().cloned())
}
}
impl<'a> ToHeaderValues for &'a str {
type Iter = option::IntoIter<HeaderValue>;
impl<'a> ToFieldValues for &'a str {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
fn to_field_values(&self) -> crate::Result<Self::Iter> {
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<HeaderValue>;
impl ToFieldValues for String {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
self.as_str().to_header_values()
fn to_field_values(&self) -> crate::Result<Self::Iter> {
self.as_str().to_field_values()
}
}
impl ToHeaderValues for &String {
type Iter = option::IntoIter<HeaderValue>;
impl ToFieldValues for &String {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
self.as_str().to_header_values()
fn to_field_values(&self) -> crate::Result<Self::Iter> {
self.as_str().to_field_values()
}
}
impl ToHeaderValues for Cow<'_, str> {
type Iter = option::IntoIter<HeaderValue>;
impl ToFieldValues for Cow<'_, str> {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
self.as_ref().to_header_values()
fn to_field_values(&self) -> crate::Result<Self::Iter> {
self.as_ref().to_field_values()
}
}

View File

@ -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<hash_map::Values<'a, HeaderName, HeaderValues>>,
slot: Option<&'a HeaderValues>,
pub(super) inner: Option<hash_map::Values<'a, FieldName, FieldValues>>,
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<Self::Item> {
loop {

View File

@ -112,7 +112,7 @@ impl TryFrom<http::HeaderMap> for Headers {
}
}
impl TryFrom<Headers> for http::HeaderMap {
impl TryFrom<Fields> for http::HeaderMap {
type Error = Error;
fn try_from(headers: Headers) -> Result<Self, Self::Error> {
@ -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();

View File

@ -156,7 +156,7 @@ pub use version::Version;
pub use crate::url::Url;
#[doc(inline)]
pub use crate::headers::Header;
pub use crate::headers::Field;
pub mod security;
pub mod trailers;

View File

@ -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<HeaderValue>;
impl ToFieldValues for Mime {
type Iter = option::IntoIter<FieldValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
fn to_field_values(&self) -> crate::Result<Self::Iter> {
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())
}
}

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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<SystemTime> 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();
headers.insert(date);
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, "<nori ate the tag. yum.>").unwrap();
let err = Date::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(expect);
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, "<nori ate the tag. yum.>").unwrap();
let err = Expect::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);

View File

@ -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<U>(base_url: U, headers: impl AsRef<Headers>) -> crate::Result<Option<Self>>
pub fn from_headers<U>(base_url: U, headers: impl AsRef<Fields>) -> crate::Result<Option<Self>>
where
U: TryInto<Url>,
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();
headers.insert(referer);
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://<nori ate the tag. yum.>")
.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();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(retry);
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();
headers.insert(retry);
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.

View File

@ -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<U>(base_url: U, headers: impl AsRef<Headers>) -> crate::Result<Option<Self>>
pub fn from_headers<U>(base_url: U, headers: impl AsRef<Fields>) -> crate::Result<Option<Self>>
where
U: TryInto<Url>,
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_headers.insert(map);
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://<nori ate the tag. yum.>")
.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();

View File

@ -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<Headers>) -> Result<Option<Self>, ParseError> {
pub fn from_headers(headers: &'a impl AsRef<Fields>) -> Result<Option<Self>, 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>,
headers: &'a impl AsRef<Fields>,
) -> Result<Option<Self>, 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<Headers>) -> Result<Option<Self>, ParseError> {
pub fn from_x_headers(headers: &'a impl AsRef<Fields>) -> Result<Option<Self>, ParseError> {
let headers = headers.as_ref();
let forwarded_for: Vec<Cow<'a, str>> = 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);
}

View File

@ -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<Version>,
#[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<HeaderName>) -> Option<&HeaderValues> {
self.headers.get(name)
pub fn header(&self, name: impl Into<FieldName>) -> Option<&FieldValues> {
self.headers.get(name.into())
}
/// Get a mutable reference to a header.
pub fn header_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
pub fn header_mut(&mut self, name: impl Into<FieldName>) -> Option<&mut FieldValues> {
self.headers.get_mut(name.into())
}
/// Remove a header.
pub fn remove_header(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
pub fn remove_header(&mut self, name: impl Into<FieldName>) -> Option<FieldValues> {
self.headers.remove(name.into())
}
@ -438,12 +437,17 @@ impl Request {
/// ```
pub fn insert_header(
&mut self,
name: impl Into<HeaderName>,
values: impl ToHeaderValues,
) -> crate::Result<Option<HeaderValues>> {
name: impl Into<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<Option<FieldValues>> {
self.headers.insert(name, values)
}
/// Set a typed HTTP header.
pub fn insert_typed_header<F: Field>(&mut self, field: F) -> Option<FieldValues> {
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<HeaderName>,
values: impl ToHeaderValues,
name: impl Into<FieldName>,
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<HeaderValues> {
let value: HeaderValue = mime.into();
pub fn set_content_type(&mut self, mime: Mime) -> Option<FieldValues> {
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<Headers> for Request {
fn as_ref(&self) -> &Headers {
impl AsRef<Fields> for Request {
fn as_ref(&self) -> &Fields {
&self.headers
}
}
impl AsMut<Headers> for Request {
fn as_mut(&mut self) -> &mut Headers {
impl AsMut<Fields> for Request {
fn as_mut(&mut self) -> &mut Fields {
&mut self.headers
}
}
@ -945,8 +949,8 @@ impl From<Request> for Body {
}
}
impl Index<HeaderName> for Request {
type Output = HeaderValues;
impl Index<FieldName> for Request {
type Output = FieldValues;
/// Returns a reference to the value corresponding to the supplied name.
///
@ -954,13 +958,13 @@ impl Index<HeaderName> 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]

View File

@ -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<Version>,
has_trailers: bool,
trailers_sender: Option<async_channel::Sender<Trailers>>,
@ -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<HeaderName>) -> Option<&mut HeaderValues> {
pub fn header_mut(&mut self, name: impl Into<FieldName>) -> Option<&mut FieldValues> {
self.headers.get_mut(name.into())
}
/// Get an HTTP header.
pub fn header(&self, name: impl Into<HeaderName>) -> Option<&HeaderValues> {
pub fn header(&self, name: impl Into<FieldName>) -> Option<&FieldValues> {
self.headers.get(name.into())
}
/// Remove a header.
pub fn remove_header(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
pub fn remove_header(&mut self, name: impl Into<FieldName>) -> Option<FieldValues> {
self.headers.remove(name.into())
}
@ -117,12 +116,17 @@ impl Response {
/// ```
pub fn insert_header(
&mut self,
name: impl Into<HeaderName>,
values: impl ToHeaderValues,
) -> crate::Result<Option<HeaderValues>> {
name: impl Into<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<Option<FieldValues>> {
self.headers.insert(name, values)
}
/// Set a typed HTTP header.
pub fn insert_typed_header<F: Field>(&mut self, field: F) -> Option<FieldValues> {
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<HeaderName>,
values: impl ToHeaderValues,
name: impl Into<FieldName>,
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<HeaderValues> {
let value: HeaderValue = mime.into();
pub fn set_content_type(&mut self, mime: Mime) -> Option<FieldValues> {
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<Headers> for Response {
fn as_ref(&self) -> &Headers {
impl AsRef<Fields> for Response {
fn as_ref(&self) -> &Fields {
&self.headers
}
}
impl AsMut<Headers> for Response {
fn as_mut(&mut self) -> &mut Headers {
impl AsMut<Fields> 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<HeaderName> for Response {
type Output = HeaderValues;
impl Index<FieldName> for Response {
type Output = FieldValues;
/// Returns a reference to the value corresponding to the supplied name.
///
@ -653,13 +657,13 @@ impl Index<HeaderName> 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]

View File

@ -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<Headers>) {
pub fn apply(&mut self, mut headers: impl AsMut<Fields>) {
let name = if self.report_only_flag {
"Content-Security-Policy-Report-Only"
} else {

View File

@ -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<Headers>) {
pub fn default(mut headers: impl AsMut<Fields>) {
dns_prefetch_control(&mut headers);
nosniff(&mut headers);
frameguard(&mut headers, None);
@ -63,7 +63,7 @@ pub fn default(mut headers: impl AsMut<Headers>) {
// /// assert_eq!(headers["X-DNS-Prefetch-Control"], "on");
// /// ```
#[inline]
pub fn dns_prefetch_control(mut headers: impl AsMut<Headers>) {
pub fn dns_prefetch_control(mut headers: impl AsMut<Fields>) {
// 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<Headers>, guard: Option<FrameOptions>) {
pub fn frameguard(mut headers: impl AsMut<Fields>, guard: Option<FrameOptions>) {
let kind = match guard {
None | Some(FrameOptions::SameOrigin) => "sameorigin",
Some(FrameOptions::Deny) => "deny",
@ -117,8 +117,8 @@ pub fn frameguard(mut headers: impl AsMut<Headers>, guard: Option<FrameOptions>)
// /// assert_eq!(headers.get("X-Powered-By"), None);
// /// ```
#[inline]
pub fn powered_by(mut headers: impl AsMut<Headers>, value: Option<HeaderValue>) {
let name = HeaderName::from_lowercase_str("X-Powered-By");
pub fn powered_by(mut headers: impl AsMut<Fields>, value: Option<FieldValue>) {
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<Headers>, value: Option<HeaderValue>)
// /// assert_eq!(headers["Strict-Transport-Security"], "max-age=5184000");
// /// ```
#[inline]
pub fn hsts(mut headers: impl AsMut<Headers>) {
pub fn hsts(mut headers: impl AsMut<Fields>) {
// Never fails, could use unsafe version of insert
headers
.as_mut()
@ -168,7 +168,7 @@ pub fn hsts(mut headers: impl AsMut<Headers>) {
// /// assert_eq!(headers["X-Content-Type-Options"], "nosniff");
// /// ```
#[inline]
pub fn nosniff(mut headers: impl AsMut<Headers>) {
pub fn nosniff(mut headers: impl AsMut<Fields>) {
// Never fails, could use unsafe verison of insert.
headers
.as_mut()
@ -189,7 +189,7 @@ pub fn nosniff(mut headers: impl AsMut<Headers>) {
// /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block");
// /// ```
#[inline]
pub fn xss_filter(mut headers: impl AsMut<Headers>) {
pub fn xss_filter(mut headers: impl AsMut<Fields>) {
// 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<Headers>, referrer: Option<ReferrerOptions>) {
pub fn referrer_policy(mut headers: impl AsMut<Fields>, referrer: Option<ReferrerOptions>) {
let policy = match referrer {
None | Some(ReferrerOptions::NoReferrer) => "no-referrer",
Some(ReferrerOptions::NoReferrerDowngrade) => "no-referrer-when-downgrade",

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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);
headers.insert(stc);
headers.insert_typed_header(stc);
let stc = StrictTransportSecurity::from_headers(headers)?.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(origins);
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();
headers.insert(origins);
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; <nori ate your param omnom>")
.unwrap();
@ -309,8 +307,8 @@ mod test {
origins.push(Url::parse("https://example.com")?);
origins.set_wildcard(true);
let mut headers = Headers::new();
headers.insert(origins);
let mut headers = Fields::new();
headers.insert_typed(origins);
let origins = TimingAllowOrigin::from_headers(headers)?.unwrap();
assert!(origins.wildcard());

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(allow);
let mut headers = Fields::new();
headers.insert_typed(allow);
let allow = Allow::from_headers(headers)?.unwrap();
assert!(allow.contains(Method::Put));

View File

@ -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(

View File

@ -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<Metric> for HeaderValue {
fn from(entry: Metric) -> HeaderValue {
impl From<Metric> 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<Metric> 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(())
}

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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();
headers.insert(timings);
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();
headers.insert(timings);
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; <nori ate your param omnom>")
.unwrap();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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());

View File

@ -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<HeaderName>,
values: impl ToHeaderValues,
) -> crate::Result<Option<HeaderValues>> {
name: impl Into<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<Option<FieldValues>> {
self.headers.insert(name, values)
}
@ -114,25 +112,25 @@ impl Trailers {
/// ```
pub fn append(
&mut self,
name: impl Into<HeaderName>,
values: impl ToHeaderValues,
name: impl Into<FieldName>,
values: impl ToFieldValues,
) -> crate::Result<()> {
self.headers.append(name, values)
}
/// Get a reference to a header.
pub fn get(&self, name: impl Into<HeaderName>) -> Option<&HeaderValues> {
self.headers.get(name)
pub fn get(&self, name: impl Into<FieldName>) -> Option<&FieldValues> {
self.headers.get(name.into())
}
/// Get a mutable reference to a header.
pub fn get_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
self.headers.get_mut(name)
pub fn get_mut(&mut self, name: impl Into<FieldName>) -> Option<&mut FieldValues> {
self.headers.get_mut(name.into())
}
/// Remove a header.
pub fn remove(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
self.headers.remove(name)
pub fn remove(&mut self, name: impl Into<FieldName>) -> Option<FieldValues> {
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<HeaderName> for Trailers {
type Output = HeaderValues;
impl Index<FieldName> for Trailers {
type Output = FieldValues;
/// Returns a reference to the value corresponding to the supplied name.
///
@ -190,13 +188,13 @@ impl Index<HeaderName> 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)
}
}

View File

@ -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<Encoding> for HeaderValue {
impl From<Encoding> 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()) }
}
}

View File

@ -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<EncodingProposal> for HeaderValue {
fn from(entry: EncodingProposal) -> HeaderValue {
impl From<EncodingProposal> 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()) }
}
}

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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);
headers.insert(accept);
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();

View File

@ -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<Headers>) -> crate::Result<Option<Self>> {
pub fn from_headers(headers: impl AsRef<Fields>) -> crate::Result<Option<Self>> {
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()
}
}