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}