feat(headers): add Range header
				
					
				
			This commit is contained in:
		| @@ -40,6 +40,7 @@ pub use self::if_range::IfRange; | |||||||
| pub use self::last_modified::LastModified; | pub use self::last_modified::LastModified; | ||||||
| pub use self::location::Location; | pub use self::location::Location; | ||||||
| pub use self::pragma::Pragma; | pub use self::pragma::Pragma; | ||||||
|  | pub use self::range::{Range, RangeSpec}; | ||||||
| pub use self::referer::Referer; | pub use self::referer::Referer; | ||||||
| pub use self::server::Server; | pub use self::server::Server; | ||||||
| pub use self::set_cookie::SetCookie; | pub use self::set_cookie::SetCookie; | ||||||
| @@ -349,6 +350,7 @@ mod if_unmodified_since; | |||||||
| mod last_modified; | mod last_modified; | ||||||
| mod location; | mod location; | ||||||
| mod pragma; | mod pragma; | ||||||
|  | mod range; | ||||||
| mod referer; | mod referer; | ||||||
| mod server; | mod server; | ||||||
| mod set_cookie; | mod set_cookie; | ||||||
|   | |||||||
							
								
								
									
										231
									
								
								src/header/common/range.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/header/common/range.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | |||||||
|  | use std::fmt::{self, Display}; | ||||||
|  | use std::str::FromStr; | ||||||
|  |  | ||||||
|  | use header::{Header, HeaderFormat, RangeUnit}; | ||||||
|  | use header::parsing::{from_one_raw_str, from_one_comma_delimited}; | ||||||
|  |  | ||||||
|  | /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) | ||||||
|  | /// | ||||||
|  | /// The "Range" header field on a GET request modifies the method | ||||||
|  | /// semantics to request transfer of only one or more subranges of the | ||||||
|  | /// selected representation data, rather than the entire selected | ||||||
|  | /// representation data. | ||||||
|  | /// | ||||||
|  | /// # ABNF | ||||||
|  | /// ```plain | ||||||
|  | /// Range =	byte-ranges-specifier / other-ranges-specifier | ||||||
|  | /// other-ranges-specifier = other-range-unit "=" other-range-set | ||||||
|  | /// other-range-set = 1*VCHAR | ||||||
|  | /// ``` | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `bytes=1000-` | ||||||
|  | /// * `bytes=-2000` | ||||||
|  | /// * `bytes=0-1,30-40` | ||||||
|  | /// * `custom_unit=0-123,-200` | ||||||
|  | /// | ||||||
|  | /// # Examples | ||||||
|  | /// ``` | ||||||
|  | /// use hyper::header::{Headers, Range, RangeSpec, RangeUnit}; | ||||||
|  | /// | ||||||
|  | /// let mut headers = Headers::new(); | ||||||
|  | /// | ||||||
|  | /// headers.set(Range { | ||||||
|  | ///     unit: RangeUnit::Bytes, | ||||||
|  | ///     ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::AllFrom(200)] | ||||||
|  | /// }); | ||||||
|  | /// ``` | ||||||
|  | /// ``` | ||||||
|  | /// use hyper::header::{Headers, Range}; | ||||||
|  | /// | ||||||
|  | /// let mut headers = Headers::new(); | ||||||
|  | /// headers.set(Range::bytes(1, 100)); | ||||||
|  | /// ``` | ||||||
|  | #[derive(PartialEq, Clone, Debug)] | ||||||
|  | pub struct Range { | ||||||
|  |     /// Unit of the Range i.e. bytes | ||||||
|  |     pub unit: RangeUnit, | ||||||
|  |     /// Set of ranges as defined in the HTTP spec | ||||||
|  |     pub ranges: Vec<RangeSpec>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Each 'Range' header can contain one or more RangeSpecs. | ||||||
|  | /// Each RangeSpec defines a range of units to fetch | ||||||
|  | #[derive(PartialEq, Clone, Debug)] | ||||||
|  | pub enum RangeSpec { | ||||||
|  |     /// Get all bytes between x and y ("x-y") | ||||||
|  |     FromTo(u64, u64), | ||||||
|  |     /// Get all bytes starting from x ("x-") | ||||||
|  |     AllFrom(u64), | ||||||
|  |     /// Get last x bytes ("-x") | ||||||
|  |     Last(u64) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Range { | ||||||
|  |     /// Get the most common byte range header ("bytes=from-to") | ||||||
|  |     pub fn bytes(from: u64, to: u64) -> Range { | ||||||
|  |         Range { | ||||||
|  |             unit: RangeUnit::Bytes, | ||||||
|  |             ranges: vec![RangeSpec::FromTo(from, to)], | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | impl fmt::Display for RangeSpec { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         match *self { | ||||||
|  |             RangeSpec::FromTo(from, to) => write!(f, "{}-{}", from, to), | ||||||
|  |             RangeSpec::Last(pos) => write!(f, "-{}", pos), | ||||||
|  |             RangeSpec::AllFrom(pos) => write!(f, "{}-", pos), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | impl fmt::Display for Range { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         try!(write!(f, "{}=", self.unit)); | ||||||
|  |  | ||||||
|  |         for (i, range) in self.ranges.iter().enumerate() { | ||||||
|  |             if i != 0 { | ||||||
|  |                 try!(f.write_str(",")); | ||||||
|  |             } | ||||||
|  |             try!(Display::fmt(range, f)); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FromStr for Range { | ||||||
|  |     type Err = ::Error; | ||||||
|  |  | ||||||
|  |     fn from_str(s: &str) -> ::Result<Range> { | ||||||
|  |         let mut iter = s.splitn(2, "="); | ||||||
|  |  | ||||||
|  |         match (iter.next(), iter.next()) { | ||||||
|  |             (Some(unit), Some(ranges)) => { | ||||||
|  |                 match (RangeUnit::from_str(unit), from_one_comma_delimited(ranges.as_bytes())) { | ||||||
|  |                     (Ok(unit), Ok(ranges)) => { | ||||||
|  |                         if ranges.is_empty() { | ||||||
|  |                             return Err(::Error::Header); | ||||||
|  |                         } | ||||||
|  |                         Ok(Range{unit: unit, ranges: ranges}) | ||||||
|  |                     }, | ||||||
|  |                     _ => Err(::Error::Header) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ => Err(::Error::Header) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FromStr for RangeSpec { | ||||||
|  |     type Err = ::Error; | ||||||
|  |  | ||||||
|  |     fn from_str(s: &str) -> ::Result<RangeSpec> { | ||||||
|  |         let mut parts = s.splitn(2, "-"); | ||||||
|  |  | ||||||
|  |         match (parts.next(), parts.next()) { | ||||||
|  |             (Some(""), Some(end)) => { | ||||||
|  |                 end.parse().or(Err(::Error::Header)).map(|end| RangeSpec::Last(end)) | ||||||
|  |             }, | ||||||
|  |             (Some(start), Some("")) => { | ||||||
|  |                 start.parse().or(Err(::Error::Header)).map(|start| RangeSpec::AllFrom(start)) | ||||||
|  |             }, | ||||||
|  |             (Some(start), Some(end)) => { | ||||||
|  |                 match (start.parse(), end.parse()) { | ||||||
|  |                     (Ok(start), Ok(end)) if start <= end => Ok(RangeSpec::FromTo(start, end)), | ||||||
|  |                     _ => Err(::Error::Header) | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             _ => Err(::Error::Header) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Header for Range { | ||||||
|  |  | ||||||
|  |     fn header_name() -> &'static str { | ||||||
|  |         "Range" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> { | ||||||
|  |         from_one_raw_str(raw) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl HeaderFormat for Range { | ||||||
|  |  | ||||||
|  |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         Display::fmt(self, f) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_parse_valid() { | ||||||
|  |     let r: Range = Header::parse_header(&[b"bytes=1-100".to_vec()]).unwrap(); | ||||||
|  |     let r2: Range = Header::parse_header(&[b"bytes=1-100,-".to_vec()]).unwrap(); | ||||||
|  |     let r3 =  Range::bytes(1, 100); | ||||||
|  |     assert_eq!(r, r2); | ||||||
|  |     assert_eq!(r2, r3); | ||||||
|  |  | ||||||
|  |     let r: Range = Header::parse_header(&[b"bytes=1-100,200-".to_vec()]).unwrap(); | ||||||
|  |     let r2: Range = Header::parse_header(&[b"bytes= 1-100 , 101-xxx,  200- ".to_vec()]).unwrap(); | ||||||
|  |     let r3 =  Range { | ||||||
|  |         unit: RangeUnit::Bytes, | ||||||
|  |         ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::AllFrom(200)] | ||||||
|  |     }; | ||||||
|  |     assert_eq!(r, r2); | ||||||
|  |     assert_eq!(r2, r3); | ||||||
|  |  | ||||||
|  |     let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap(); | ||||||
|  |     let r2: Range = Header::parse_header(&[b"custom=1-100, ,,-100".to_vec()]).unwrap(); | ||||||
|  |     let r3 =  Range { | ||||||
|  |         unit: RangeUnit::Unregistered("custom".to_owned()), | ||||||
|  |         ranges: vec![RangeSpec::FromTo(1, 100), RangeSpec::Last(100)] | ||||||
|  |     }; | ||||||
|  |     assert_eq!(r, r2); | ||||||
|  |     assert_eq!(r2, r3); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_parse_invalid() { | ||||||
|  |     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-a,-".to_vec()]); | ||||||
|  |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|  |     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-2-3".to_vec()]); | ||||||
|  |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|  |     let r: ::Result<Range> = Header::parse_header(&[b"abc".to_vec()]); | ||||||
|  |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|  |     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-100=".to_vec()]); | ||||||
|  |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|  |     let r: ::Result<Range> = Header::parse_header(&[b"bytes=".to_vec()]); | ||||||
|  |     assert_eq!(r.ok(), None); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_fmt() { | ||||||
|  |     use header::Headers; | ||||||
|  |  | ||||||
|  |     let range_header = Range { | ||||||
|  |         unit: RangeUnit::Bytes, | ||||||
|  |         ranges: vec![RangeSpec::FromTo(0, 1000), RangeSpec::AllFrom(2000)], | ||||||
|  |     }; | ||||||
|  |     let mut headers = Headers::new(); | ||||||
|  |     headers.set(range_header); | ||||||
|  |  | ||||||
|  |     assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); | ||||||
|  |  | ||||||
|  |     headers.clear(); | ||||||
|  |     headers.set(Range {unit: RangeUnit::Bytes, ranges: vec![]}); | ||||||
|  |  | ||||||
|  |     assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bench_header!(bytes_multi, Range, { vec![b"bytes=1-1001,2001-3001,10001-".to_vec()]}); | ||||||
|  | bench_header!(custom_unit, Range, { vec![b"custom_unit=0-100000".to_vec()]}); | ||||||
		Reference in New Issue
	
	Block a user