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