Skip to content

Commit

Permalink
feat(headers): Add If-None-Match header field
Browse files Browse the repository at this point in the history
Add the HTTP/1.1 `If-None-Match` header field makes the request method conditional
on a recipient cache or origin server either not having any current
representation of the target resource, when the field-value is "*",
or having a selected representation with an entity-tag that does not
match any of those listed in the field-value.

Closes #238
  • Loading branch information
fmendez committed Feb 7, 2015
1 parent f836ee8 commit 318b067
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
84 changes: 84 additions & 0 deletions src/header/common/if_none_match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use header::{Header, HeaderFormat, 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.
///
/// The "If-None-Match" header field makes the request method conditional
/// on a recipient cache or origin server either not having any current
/// representation of the target resource, when the field-value is "*",
/// or having a selected representation with an entity-tag that does not
/// match any of those listed in the field-value.
///
/// A recipient MUST use the weak comparison function when comparing
/// entity-tags for If-None-Match (Section 2.3.2), since weak entity-tags
/// can be used for cache validation even if there have been changes to
/// the representation data.
///
/// Spec: https://tools.ietf.org/html/rfc7232#section-3.2

/// The `If-None-Match` header field.
#[derive(Clone, PartialEq, Debug)]
pub enum IfNoneMatch {
/// This corresponds to '*'.
Any,
/// The header field names which will influence the response representation.
EntityTags(Vec<EntityTag>)
}

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

fn parse_header(raw: &[Vec<u8>]) -> Option<IfNoneMatch> {
from_one_raw_str(raw).and_then(|s: String| {
let slice = &s[];
match slice {
"" => None,
"*" => Some(IfNoneMatch::Any),
_ => from_comma_delimited(raw).map(|vec| IfNoneMatch::EntityTags(vec)),
}
})
}
}

impl HeaderFormat for IfNoneMatch {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
IfNoneMatch::Any => { write!(fmt, "*") }
IfNoneMatch::EntityTags(ref fields) => { fmt_comma_delimited(fmt, &fields[]) }
}
}
}

#[cfg(test)]
mod tests {
use super::IfNoneMatch;
use header::Header;
use header::EntityTag;

#[test]
fn test_if_none_match() {
let mut if_none_match: Option<IfNoneMatch>;

if_none_match = Header::parse_header([b"*".to_vec()].as_slice());
assert_eq!(if_none_match, Some(IfNoneMatch::Any));

if_none_match = Header::parse_header([b"\"foobar\", W/\"weak-etag\"".to_vec()].as_slice());
let mut entities: Vec<EntityTag> = Vec::new();
let foobar_etag = EntityTag {
weak: false,
tag: "foobar".to_string()
};
let weak_etag = EntityTag {
weak: true,
tag: "weak-etag".to_string()
};
entities.push(foobar_etag);
entities.push(weak_etag);
assert_eq!(if_none_match, Some(IfNoneMatch::EntityTags(entities)));
}
}

bench_header!(bench, IfNoneMatch, { vec![b"W/\"nonemptytag\"".to_vec()] });
2 changes: 2 additions & 0 deletions src/header/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub use self::etag::Etag;
pub use self::expires::Expires;
pub use self::host::Host;
pub use self::if_modified_since::IfModifiedSince;
pub use self::if_none_match::IfNoneMatch;
pub use self::if_unmodified_since::IfUnmodifiedSince;
pub use self::last_modified::LastModified;
pub use self::location::Location;
Expand Down Expand Up @@ -158,6 +159,7 @@ mod expires;
mod host;
mod last_modified;
mod if_modified_since;
mod if_none_match;
mod if_unmodified_since;
mod location;
mod pragma;
Expand Down

0 comments on commit 318b067

Please sign in to comment.