requiem_http/header/common/
if_range.rs

1use std::fmt::{self, Display, Write};
2
3use crate::error::ParseError;
4use crate::header::{
5    self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
6    IntoHeaderValue, InvalidHeaderValue, Writer,
7};
8use crate::httpmessage::HttpMessage;
9
10/// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2)
11///
12/// If a client has a partial copy of a representation and wishes to have
13/// an up-to-date copy of the entire representation, it could use the
14/// Range header field with a conditional GET (using either or both of
15/// If-Unmodified-Since and If-Match.)  However, if the precondition
16/// fails because the representation has been modified, the client would
17/// then have to make a second request to obtain the entire current
18/// representation.
19///
20/// The `If-Range` header field allows a client to \"short-circuit\" the
21/// second request.  Informally, its meaning is as follows: if the
22/// representation is unchanged, send me the part(s) that I am requesting
23/// in Range; otherwise, send me the entire representation.
24///
25/// # ABNF
26///
27/// ```text
28/// If-Range = entity-tag / HTTP-date
29/// ```
30///
31/// # Example values
32///
33/// * `Sat, 29 Oct 1994 19:43:31 GMT`
34/// * `\"xyzzy\"`
35///
36/// # Examples
37///
38/// ```rust
39/// use requiem_http::Response;
40/// use requiem_http::http::header::{EntityTag, IfRange};
41///
42/// let mut builder = Response::Ok();
43/// builder.set(IfRange::EntityTag(EntityTag::new(
44///     false,
45///     "xyzzy".to_owned(),
46/// )));
47/// ```
48///
49/// ```rust
50/// use requiem_http::Response;
51/// use requiem_http::http::header::IfRange;
52/// use std::time::{Duration, SystemTime};
53///
54/// let mut builder = Response::Ok();
55/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
56/// builder.set(IfRange::Date(fetched.into()));
57/// ```
58#[derive(Clone, Debug, PartialEq)]
59pub enum IfRange {
60    /// The entity-tag the client has of the resource
61    EntityTag(EntityTag),
62    /// The date when the client retrieved the resource
63    Date(HttpDate),
64}
65
66impl Header for IfRange {
67    fn name() -> HeaderName {
68        header::IF_RANGE
69    }
70    #[inline]
71    fn parse<T>(msg: &T) -> Result<Self, ParseError>
72    where
73        T: HttpMessage,
74    {
75        let etag: Result<EntityTag, _> =
76            from_one_raw_str(msg.headers().get(&header::IF_RANGE));
77        if let Ok(etag) = etag {
78            return Ok(IfRange::EntityTag(etag));
79        }
80        let date: Result<HttpDate, _> =
81            from_one_raw_str(msg.headers().get(&header::IF_RANGE));
82        if let Ok(date) = date {
83            return Ok(IfRange::Date(date));
84        }
85        Err(ParseError::Header)
86    }
87}
88
89impl Display for IfRange {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match *self {
92            IfRange::EntityTag(ref x) => Display::fmt(x, f),
93            IfRange::Date(ref x) => Display::fmt(x, f),
94        }
95    }
96}
97
98impl IntoHeaderValue for IfRange {
99    type Error = InvalidHeaderValue;
100
101    fn try_into(self) -> Result<HeaderValue, Self::Error> {
102        let mut writer = Writer::new();
103        let _ = write!(&mut writer, "{}", self);
104        HeaderValue::from_maybe_shared(writer.take())
105    }
106}
107
108#[cfg(test)]
109mod test_if_range {
110    use super::IfRange as HeaderField;
111    use crate::header::*;
112    use std::str;
113    test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);
114    test_header!(test2, vec![b"\"xyzzy\""]);
115    test_header!(test3, vec![b"this-is-invalid"], None::<IfRange>);
116}