mco_http/header/common/if_range.rs
1use std::fmt::{self, Display};
2use crate::header::{self, Header, HeaderFormat, EntityTag, HttpDate};
3
4/// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2)
5///
6/// If a client has a partial copy of a representation and wishes to have
7/// an up-to-date copy of the entire representation, it could use the
8/// Range header field with a conditional GET (using either or both of
9/// If-Unmodified-Since and If-Match.) However, if the precondition
10/// fails because the representation has been modified, the client would
11/// then have to make a second request to obtain the entire current
12/// representation.
13///
14/// The `If-Range` header field allows a client to \"short-circuit\" the
15/// second request. Informally, its meaning is as follows: if the
16/// representation is unchanged, send me the part(s) that I am requesting
17/// in Range; otherwise, send me the entire representation.
18///
19/// # ABNF
20/// ```plain
21/// If-Range = entity-tag / HTTP-date
22/// ```
23///
24/// # Example values
25/// * `Sat, 29 Oct 1994 19:43:31 GMT`
26/// * `\"xyzzy\"`
27///
28/// # Examples
29/// ```
30/// use mco_http::header::{Headers, IfRange, EntityTag};
31///
32/// let mut headers = Headers::new();
33/// headers.set(IfRange::EntityTag(EntityTag::new(false, "xyzzy".to_owned())));
34/// ```
35/// ```
36/// # extern crate mco_http;
37/// # extern crate time;
38/// # fn main() {
39/// // extern crate time;
40///
41/// use mco_http::header::{Headers, IfRange, HttpDate};
42/// use time::{self, Duration};
43///
44/// let mut headers = Headers::new();
45/// headers.set(IfRange::Date(HttpDate(time::now() - Duration::days(1))));
46/// # }
47/// ```
48#[derive(Clone, Debug, PartialEq)]
49pub enum IfRange {
50 /// The entity-tag the client has of the resource
51 EntityTag(EntityTag),
52 /// The date when the client retrieved the resource
53 Date(HttpDate),
54}
55
56impl Header for IfRange {
57 fn header_name() -> &'static str {
58 "If-Range"
59 }
60 fn parse_header(raw: &[Vec<u8>]) -> crate::Result<IfRange> {
61 let etag: crate::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
62 if etag.is_ok() {
63 return Ok(IfRange::EntityTag(etag.unwrap()));
64 }
65 let date: crate::Result<HttpDate> = header::parsing::from_one_raw_str(raw);
66 if date.is_ok() {
67 return Ok(IfRange::Date(date.unwrap()));
68 }
69 Err(crate::Error::Header)
70 }
71}
72
73impl HeaderFormat for IfRange {
74 fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
75 match *self {
76 IfRange::EntityTag(ref x) => Display::fmt(x, f),
77 IfRange::Date(ref x) => Display::fmt(x, f),
78 }
79 }
80}
81
82impl Display for IfRange {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 self.fmt_header(f)
85 }
86}
87
88#[cfg(test)]
89mod test_if_range {
90 use std::str;
91 use crate::header::*;
92 use super::IfRange as HeaderField;
93 test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);
94 test_header!(test2, vec![b"\"xyzzy\""]);
95 test_header!(test3, vec![b"this-is-invalid"], None::<IfRange>);
96}