Skip to content

refactor(headers): Use header!() macro for 3 headers with a "*" value #421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 24 additions & 44 deletions src/header/common/if_match.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,31 @@
use header::{EntityTag, Header, HeaderFormat};
use header::parsing::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str};
use std::fmt;
use header::EntityTag;

/// The `If-Match` header
///
/// The `If-Match` request-header field is used with a method to make
/// it conditional. The client provides a list of entity tags, and
/// the request is only executed if one of those tags matches the
/// current entity.
///
/// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
#[derive(Clone, PartialEq, Debug)]
pub enum IfMatch {
/// This corresponds to '*'.
Any,
/// The header field names which will influence the response representation.
EntityTags(Vec<EntityTag>)
}

impl Header for IfMatch {
fn header_name() -> &'static str {
"If-Match"
}

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[..])
}
}
header! {
#[doc="`If-Match` header, defined in"]
#[doc="[RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)"]
#[doc=""]
#[doc="The `If-Match` header field makes the request method conditional on"]
#[doc="the recipient origin server either having at least one current"]
#[doc="representation of the target resource, when the field-value is \"*\","]
#[doc="or having a current representation of the target resource that has an"]
#[doc="entity-tag matching a member of the list of entity-tags provided in"]
#[doc="the field-value."]
#[doc=""]
#[doc="An origin server MUST use the strong comparison function when"]
#[doc="comparing entity-tags for `If-Match`, since the client"]
#[doc="intends this precondition to prevent the method from being applied if"]
#[doc="there have been any changes to the representation data."]
#[doc=""]
#[doc="# ABNF"]
#[doc="```plain"]
#[doc="If-Match = \"*\" / 1#entity-tag"]
#[doc="```"]
(IfMatch, "If-Match") => {Any / (EntityTag)+}
}

#[test]
fn test_parse_header() {
use header::Header;
{
let a: IfMatch = Header::parse_header(
[b"*".to_vec()].as_ref()).unwrap();
Expand All @@ -54,7 +34,7 @@ fn test_parse_header() {
{
let a: IfMatch = Header::parse_header(
[b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"".to_vec()].as_ref()).unwrap();
let b = IfMatch::EntityTags(
let b = IfMatch::Items(
vec![EntityTag::new(false, "xyzzy".to_string()),
EntityTag::new(false, "r2d2xxxx".to_string()),
EntityTag::new(false, "c3piozzzz".to_string())]);
Expand Down
32 changes: 26 additions & 6 deletions src/header/common/if_none_match.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
use header::{Header, HeaderFormat, EntityTag};
use header::parsing::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str};
use std::fmt::{self};
use header::EntityTag;

/// 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
/// on a recipient cache or origin server either not having any current
Expand Down Expand Up @@ -50,7 +70,7 @@ impl HeaderFormat for IfNoneMatch {
IfNoneMatch::EntityTags(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
}
}
}
}*/

#[cfg(test)]
mod tests {
Expand All @@ -71,7 +91,7 @@ mod tests {
let weak_etag = EntityTag::new(true, "weak-etag".to_string());
entities.push(foobar_etag);
entities.push(weak_etag);
assert_eq!(if_none_match, Some(IfNoneMatch::EntityTags(entities)));
assert_eq!(if_none_match, Some(IfNoneMatch::Items(entities)));
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/header/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 22 additions & 8 deletions src/header/common/vary.rs
Original file line number Diff line number Diff line change
@@ -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;

/// 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

#[derive(Clone, PartialEq, Debug)]
Expand Down Expand Up @@ -38,7 +52,7 @@ impl HeaderFormat for Vary {
Vary::Headers(ref fields) => { fmt_comma_delimited(fmt, &fields[..]) }
}
}
}
}*/

#[cfg(test)]
mod tests {
Expand All @@ -53,8 +67,8 @@ mod tests {
assert_eq!(vary, Some(Vary::Any));

vary = Header::parse_header([b"etag,cookie,allow".to_vec()].as_ref());
assert_eq!(vary, Some(Vary::Headers(vec!["eTag".parse().unwrap(),
"cookIE".parse().unwrap(),
"AlLOw".parse().unwrap(),])));
assert_eq!(vary, Some(Vary::Items(vec!["eTag".parse().unwrap(),
"cookIE".parse().unwrap(),
"AlLOw".parse().unwrap(),])));
}
}