refactor(headers): Use header!() macro for 3 headers with a "*" value
`If-Match`, `If-None-Match` and `Vary` headers are either a "*" value meaning that the header matches every possible item or a list of items, one of them must be matched to fulfil the condition. BREAKING CHANGE: `If-Match`, `If-None-Match` and `Vary` item variant name changed to `Items`
This commit is contained in:
@@ -1,51 +1,31 @@
|
|||||||
use header::{EntityTag, Header, HeaderFormat};
|
use header::EntityTag;
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
/// The `If-Match` header
|
header! {
|
||||||
///
|
#[doc="`If-Match` header, defined in"]
|
||||||
/// The `If-Match` request-header field is used with a method to make
|
#[doc="[RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)"]
|
||||||
/// it conditional. The client provides a list of entity tags, and
|
#[doc=""]
|
||||||
/// the request is only executed if one of those tags matches the
|
#[doc="The `If-Match` header field makes the request method conditional on"]
|
||||||
/// current entity.
|
#[doc="the recipient origin server either having at least one current"]
|
||||||
///
|
#[doc="representation of the target resource, when the field-value is \"*\","]
|
||||||
/// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
|
#[doc="or having a current representation of the target resource that has an"]
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[doc="entity-tag matching a member of the list of entity-tags provided in"]
|
||||||
pub enum IfMatch {
|
#[doc="the field-value."]
|
||||||
/// This corresponds to '*'.
|
#[doc=""]
|
||||||
Any,
|
#[doc="An origin server MUST use the strong comparison function when"]
|
||||||
/// The header field names which will influence the response representation.
|
#[doc="comparing entity-tags for `If-Match`, since the client"]
|
||||||
EntityTags(Vec<EntityTag>)
|
#[doc="intends this precondition to prevent the method from being applied if"]
|
||||||
}
|
#[doc="there have been any changes to the representation data."]
|
||||||
|
#[doc=""]
|
||||||
impl Header for IfMatch {
|
#[doc="# ABNF"]
|
||||||
fn header_name() -> &'static str {
|
#[doc="```plain"]
|
||||||
"If-Match"
|
#[doc="If-Match = \"*\" / 1#entity-tag"]
|
||||||
}
|
#[doc="```"]
|
||||||
|
(IfMatch, "If-Match") => {Any / (EntityTag)+}
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> Option<IfMatch> {
|
|
||||||
from_one_raw_str(raw).and_then(|s: String| {
|
|
||||||
let slice = &s[..];
|
|
||||||
match slice {
|
|
||||||
"" => None,
|
|
||||||
"*" => Some(IfMatch::Any),
|
|
||||||
_ => from_comma_delimited(raw).map(IfMatch::EntityTags),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HeaderFormat for IfMatch {
|
|
||||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
IfMatch::Any => write!(fmt, "*"),
|
|
||||||
IfMatch::EntityTags(ref fields) => fmt_comma_delimited(fmt, &fields[..])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_header() {
|
fn test_parse_header() {
|
||||||
|
use header::Header;
|
||||||
{
|
{
|
||||||
let a: IfMatch = Header::parse_header(
|
let a: IfMatch = Header::parse_header(
|
||||||
[b"*".to_vec()].as_ref()).unwrap();
|
[b"*".to_vec()].as_ref()).unwrap();
|
||||||
@@ -54,7 +34,7 @@ fn test_parse_header() {
|
|||||||
{
|
{
|
||||||
let a: IfMatch = Header::parse_header(
|
let a: IfMatch = Header::parse_header(
|
||||||
[b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"".to_vec()].as_ref()).unwrap();
|
[b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"".to_vec()].as_ref()).unwrap();
|
||||||
let b = IfMatch::EntityTags(
|
let b = IfMatch::Items(
|
||||||
vec![EntityTag::new(false, "xyzzy".to_string()),
|
vec![EntityTag::new(false, "xyzzy".to_string()),
|
||||||
EntityTag::new(false, "r2d2xxxx".to_string()),
|
EntityTag::new(false, "r2d2xxxx".to_string()),
|
||||||
EntityTag::new(false, "c3piozzzz".to_string())]);
|
EntityTag::new(false, "c3piozzzz".to_string())]);
|
||||||
|
|||||||
@@ -1,8 +1,28 @@
|
|||||||
use header::{Header, HeaderFormat, EntityTag};
|
use header::EntityTag;
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str};
|
|
||||||
use std::fmt::{self};
|
|
||||||
|
|
||||||
/// The `If-None-Match` header defined by HTTP/1.1.
|
header! {
|
||||||
|
#[doc="`If-None-Match` header, defined in"]
|
||||||
|
#[doc="[RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2)"]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc="The `If-None-Match` header field makes the request method conditional"]
|
||||||
|
#[doc="on a recipient cache or origin server either not having any current"]
|
||||||
|
#[doc="representation of the target resource, when the field-value is \"*\","]
|
||||||
|
#[doc="or having a selected representation with an entity-tag that does not"]
|
||||||
|
#[doc="match any of those listed in the field-value."]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc="A recipient MUST use the weak comparison function when comparing"]
|
||||||
|
#[doc="entity-tags for If-None-Match (Section 2.3.2), since weak entity-tags"]
|
||||||
|
#[doc="can be used for cache validation even if there have been changes to"]
|
||||||
|
#[doc="the representation data."]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc="# ABNF"]
|
||||||
|
#[doc="```plain"]
|
||||||
|
#[doc="If-None-Match = \"*\" / 1#entity-tag"]
|
||||||
|
#[doc="```"]
|
||||||
|
(IfNoneMatch, "If-None-Match") => {Any / (EntityTag)+}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// The `If-None-Match` header defined by HTTP/1.1.
|
||||||
///
|
///
|
||||||
/// The "If-None-Match" header field makes the request method conditional
|
/// The "If-None-Match" header field makes the request method conditional
|
||||||
/// on a recipient cache or origin server either not having any current
|
/// on a recipient cache or origin server either not having any current
|
||||||
@@ -50,7 +70,7 @@ impl HeaderFormat for IfNoneMatch {
|
|||||||
IfNoneMatch::EntityTags(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
|
IfNoneMatch::EntityTags(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -71,7 +91,7 @@ mod tests {
|
|||||||
let weak_etag = EntityTag::new(true, "weak-etag".to_string());
|
let weak_etag = EntityTag::new(true, "weak-etag".to_string());
|
||||||
entities.push(foobar_etag);
|
entities.push(foobar_etag);
|
||||||
entities.push(weak_etag);
|
entities.push(weak_etag);
|
||||||
assert_eq!(if_none_match, Some(IfNoneMatch::EntityTags(entities)));
|
assert_eq!(if_none_match, Some(IfNoneMatch::Items(entities)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,47 @@ macro_rules! header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// List header, one or more items with "*" option
|
||||||
|
($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+}) => {
|
||||||
|
$(#[$a])*
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum $id {
|
||||||
|
/// Any value is a match
|
||||||
|
Any,
|
||||||
|
/// Only the listed items are a match
|
||||||
|
Items(Vec<$item>),
|
||||||
|
}
|
||||||
|
impl $crate::header::Header for $id {
|
||||||
|
fn header_name() -> &'static str {
|
||||||
|
$n
|
||||||
|
}
|
||||||
|
fn parse_header(raw: &[Vec<u8>]) -> Option<Self> {
|
||||||
|
// FIXME: Return None if no item is in $id::Only
|
||||||
|
if raw.len() == 1 {
|
||||||
|
if raw[0] == b"*" {
|
||||||
|
return Some($id::Any)
|
||||||
|
} else if raw[0] == b"" {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$crate::header::parsing::from_comma_delimited(raw).map(|vec| $id::Items(vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl $crate::header::HeaderFormat for $id {
|
||||||
|
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
$id::Any => write!(f, "*"),
|
||||||
|
$id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited(f, &fields[..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ::std::fmt::Display for $id {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
use $crate::header::HeaderFormat;
|
||||||
|
self.fmt_header(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mod access_control;
|
mod access_control;
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
use header::{Header, HeaderFormat};
|
|
||||||
use std::fmt::{self};
|
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str};
|
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
|
|
||||||
/// The `Allow` header.
|
header! {
|
||||||
|
#[doc="`Vary` header, defined in [RFC7231](https://tools.ietf.org/html/rfc7231#section-7.1.4)"]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc="The \"Vary\" header field in a response describes what parts of a"]
|
||||||
|
#[doc="request message, aside from the method, Host header field, and"]
|
||||||
|
#[doc="request target, might influence the origin server's process for"]
|
||||||
|
#[doc="selecting and representing this response. The value consists of"]
|
||||||
|
#[doc="either a single asterisk (\"*\") or a list of header field names"]
|
||||||
|
#[doc="(case-insensitive)."]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc="# ABNF"]
|
||||||
|
#[doc="```plain"]
|
||||||
|
#[doc="Vary = \"*\" / 1#field-name"]
|
||||||
|
#[doc="```"]
|
||||||
|
(Vary, "Vary") => {Any / (UniCase<String>)+}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// The `Allow` header.
|
||||||
/// See also https://tools.ietf.org/html/rfc7231#section-7.1.4
|
/// See also https://tools.ietf.org/html/rfc7231#section-7.1.4
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
@@ -38,7 +52,7 @@ impl HeaderFormat for Vary {
|
|||||||
Vary::Headers(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
|
Vary::Headers(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -53,8 +67,8 @@ mod tests {
|
|||||||
assert_eq!(vary, Some(Vary::Any));
|
assert_eq!(vary, Some(Vary::Any));
|
||||||
|
|
||||||
vary = Header::parse_header([b"etag,cookie,allow".to_vec()].as_ref());
|
vary = Header::parse_header([b"etag,cookie,allow".to_vec()].as_ref());
|
||||||
assert_eq!(vary, Some(Vary::Headers(vec!["eTag".parse().unwrap(),
|
assert_eq!(vary, Some(Vary::Items(vec!["eTag".parse().unwrap(),
|
||||||
"cookIE".parse().unwrap(),
|
"cookIE".parse().unwrap(),
|
||||||
"AlLOw".parse().unwrap(),])));
|
"AlLOw".parse().unwrap(),])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user