1 use std::time::SystemTime; 2 3 use super::{ETag, LastModified}; 4 use util::{EntityTag, HttpDate}; 5 use HeaderValue; 6 7 /// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2) 8 /// 9 /// If a client has a partial copy of a representation and wishes to have 10 /// an up-to-date copy of the entire representation, it could use the 11 /// Range header field with a conditional GET (using either or both of 12 /// If-Unmodified-Since and If-Match.) However, if the precondition 13 /// fails because the representation has been modified, the client would 14 /// then have to make a second request to obtain the entire current 15 /// representation. 16 /// 17 /// The `If-Range` header field allows a client to \"short-circuit\" the 18 /// second request. Informally, its meaning is as follows: if the 19 /// representation is unchanged, send me the part(s) that I am requesting 20 /// in Range; otherwise, send me the entire representation. 21 /// 22 /// # ABNF 23 /// 24 /// ```text 25 /// If-Range = entity-tag / HTTP-date 26 /// ``` 27 /// 28 /// # Example values 29 /// 30 /// * `Sat, 29 Oct 1994 19:43:31 GMT` 31 /// * `\"xyzzy\"` 32 /// 33 /// # Examples 34 /// 35 /// ``` 36 /// # extern crate headers; 37 /// use headers::IfRange; 38 /// use std::time::{SystemTime, Duration}; 39 /// 40 /// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24); 41 /// let if_range = IfRange::date(fetched); 42 /// ``` 43 #[derive(Clone, Debug, PartialEq)] 44 pub struct IfRange(IfRange_); 45 46 derive_header! { 47 IfRange(_), 48 name: IF_RANGE 49 } 50 51 impl IfRange { 52 /// Create an `IfRange` header with an entity tag. etag(tag: ETag) -> IfRange53 pub fn etag(tag: ETag) -> IfRange { 54 IfRange(IfRange_::EntityTag(tag.0)) 55 } 56 57 /// Create an `IfRange` header with a date value. date(time: SystemTime) -> IfRange58 pub fn date(time: SystemTime) -> IfRange { 59 IfRange(IfRange_::Date(time.into())) 60 } 61 62 /// Checks if the resource has been modified, or if the range request 63 /// can be served. is_modified(&self, etag: Option<&ETag>, last_modified: Option<&LastModified>) -> bool64 pub fn is_modified(&self, etag: Option<&ETag>, last_modified: Option<&LastModified>) -> bool { 65 match self.0 { 66 IfRange_::Date(since) => last_modified.map(|time| since < time.0).unwrap_or(true), 67 IfRange_::EntityTag(ref entity) => etag.map(|etag| !etag.0.strong_eq(entity)).unwrap_or(true), 68 } 69 } 70 } 71 72 #[derive(Clone, Debug, PartialEq)] 73 enum IfRange_ { 74 /// The entity-tag the client has of the resource 75 EntityTag(EntityTag), 76 /// The date when the client retrieved the resource 77 Date(HttpDate), 78 } 79 80 impl ::util::TryFromValues for IfRange_ { try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> where I: Iterator<Item = &'i HeaderValue>,81 fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> 82 where 83 I: Iterator<Item = &'i HeaderValue>, 84 { 85 values 86 .next() 87 .and_then(|val| { 88 if let Some(tag) = EntityTag::from_val(val) { 89 return Some(IfRange_::EntityTag(tag)); 90 } 91 92 let date = HttpDate::from_val(val)?; 93 Some(IfRange_::Date(date)) 94 }) 95 .ok_or_else(::Error::invalid) 96 } 97 } 98 99 impl<'a> From<&'a IfRange_> for HeaderValue { from(if_range: &'a IfRange_) -> HeaderValue100 fn from(if_range: &'a IfRange_) -> HeaderValue { 101 match *if_range { 102 IfRange_::EntityTag(ref tag) => tag.into(), 103 IfRange_::Date(ref date) => date.into(), 104 } 105 } 106 } 107 108 /* 109 #[cfg(test)] 110 mod tests { 111 use std::str; 112 use *; 113 use super::IfRange as HeaderField; 114 test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); 115 test_header!(test2, vec![b"\"xyzzy\""]); 116 test_header!(test3, vec![b"this-is-invalid"], None::<IfRange>); 117 } 118 */ 119 120 #[cfg(test)] 121 mod tests { 122 use super::*; 123 124 #[test] test_is_modified_etag()125 fn test_is_modified_etag() { 126 let etag = ETag::from_static("\"xyzzy\""); 127 let if_range = IfRange::etag(etag.clone()); 128 129 assert!(!if_range.is_modified(Some(&etag), None)); 130 131 let etag = ETag::from_static("W/\"xyzzy\""); 132 assert!(if_range.is_modified(Some(&etag), None)); 133 } 134 } 135