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