From 89f59044b7ed7f975be66a1d931c549319312e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Thu, 14 Oct 2021 16:31:22 +0200 Subject: [PATCH] make `Range` headers strongly typed (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From what I can see, the old, good implementation was commented out back in 2018 during some large refactoring of the whole crate, and 4b42e4fc reenabled the range module, while replacing the old, strongly typed implementation with an implmentation that moves parsing of ranges out of the decoding, into an iterator function. While the implementation that 4b42e4fc brought was certainly better than no implementation at all, the old implementation should've been fixed instead, considering that it was mostly spec compliant and supported more variants than the back then new implementation, and it even had tests, which the other one had not. Sadly, the I had only found the old implementation after writing a mostly feature complete new implementation, so this wasted a lot of effort right here. With the old implementation found, and the new and improved implementation at hand, a consolidated implementation did arise, now with more documentation, stronger type guarantees and more test cases then ever. Signed-off-by: Jan Christian Grünhage --- src/common/mod.rs | 2 + src/common/range.rs | 715 +++++++++++++++++++++++++++----------------- 2 files changed, 443 insertions(+), 274 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index 2237ae8e..b9b82b61 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -52,6 +52,8 @@ pub use self::pragma::Pragma; //pub use self::prefer::{Prefer, Preference}; //pub use self::preference_applied::PreferenceApplied; pub use self::proxy_authorization::ProxyAuthorization; +pub use self::range::ByteRangeBuilder; +pub use self::range::ByteRangeSpec; pub use self::range::Range; pub use self::referer::Referer; pub use self::referrer_policy::ReferrerPolicy; diff --git a/src/common/range.rs b/src/common/range.rs index 29cc79d3..b227bc17 100644 --- a/src/common/range.rs +++ b/src/common/range.rs @@ -1,4 +1,8 @@ -use std::ops::{Bound, RangeBounds}; +use std::{ + num::NonZeroU64, + ops::{Bound, RangeBounds}, + str::FromStr, +}; /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// @@ -10,17 +14,21 @@ use std::ops::{Bound, RangeBounds}; /// # ABNF /// /// ```text -/// Range = byte-ranges-specifier / other-ranges-specifier +/// Range = byte-ranges-specifier / other-ranges-specifier /// other-ranges-specifier = other-range-unit "=" other-range-set /// other-range-set = 1*VCHAR /// -/// bytes-unit = "bytes" -/// /// byte-ranges-specifier = bytes-unit "=" byte-range-set /// byte-range-set = 1#(byte-range-spec / suffix-byte-range-spec) +/// +/// bytes-unit = "bytes" +/// /// byte-range-spec = first-byte-pos "-" [last-byte-pos] /// first-byte-pos = 1*DIGIT /// last-byte-pos = 1*DIGIT +/// +/// suffix-byte-range-spec = "-" suffix-length +/// suffix-length = 1*DIGIT /// ``` /// /// # Example values @@ -34,96 +42,95 @@ use std::ops::{Bound, RangeBounds}; /// /// ``` /// # extern crate headers; -/// use headers::Range; -/// +/// use headers::{Range, ByteRangeBuilder}; /// -/// let range = Range::bytes(0..1234).unwrap(); +/// let range : Range = ByteRangeBuilder::new() +/// .range(0..20).unwrap() +/// .range(1000..1700).unwrap() +/// .suffix(300).unwrap() +/// .finish().unwrap(); /// ``` #[derive(Clone, Debug, PartialEq)] -pub struct Range(::HeaderValue); - -error_type!(InvalidRange); - -impl Range { - /// Creates a `Range` header from bounds. - pub fn bytes(bounds: impl RangeBounds) -> Result { - let v = match (bounds.start_bound(), bounds.end_bound()) { - (Bound::Unbounded, Bound::Included(end)) => format!("bytes=-{}", end), - (Bound::Unbounded, Bound::Excluded(&end)) => format!("bytes=-{}", end - 1), - (Bound::Included(start), Bound::Included(end)) => format!("bytes={}-{}", start, end), - (Bound::Included(start), Bound::Excluded(&end)) => { - format!("bytes={}-{}", start, end - 1) - } - (Bound::Included(start), Bound::Unbounded) => format!("bytes={}-", start), - _ => return Err(InvalidRange { _inner: () }), - }; - - Ok(Range(::HeaderValue::from_str(&v).unwrap())) - } - - /// Iterate the range sets as a tuple of bounds. - pub fn iter<'a>(&'a self) -> impl Iterator, Bound)> + 'a { - let s = self - .0 - .to_str() - .expect("valid string checked in Header::decode()"); - - s["bytes=".len()..].split(',').filter_map(|spec| { - let mut iter = spec.trim().splitn(2, '-'); - Some((parse_bound(iter.next()?)?, parse_bound(iter.next()?)?)) - }) - } +pub enum Range { + /// Since representation data is transferred in payloads as a sequence of + /// octets, a byte range is a meaningful substructure for any + /// representation transferable over HTTP (Section 3 of [RFC7231]). The + /// "bytes" range unit is defined for expressing subranges of the data's + /// octet sequence. + /// + /// These byte ranges are exactly as they came from the client. You may + /// wish to coalesce them in case of overlaps, as described in [section + /// 4.1 of RFC7233](https://tools.ietf.org/html/rfc7233#section-4.1). + Bytes(Vec), + /// Range units are intended to be extensible. New range units ought to be + /// registered with IANA, as defined in [Section 5.1 of RFC 7233](2). + /// + /// `bytes` is the only [registered](1) unit at the moment, so all other + /// units will end up in this variant, `Unregistered`. For those other + /// units, no concrete format for the `other-ranges-set` is given in RFC + /// 7233, so additional parsing has to be done by the consumer of the + /// unregistered units. + /// + /// [1]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#range-units + /// [2]: https://datatracker.ietf.org/doc/html/rfc7233#section-5.1 + Unregistered { + /// The unit for other ranges. This has to be a `token`, as defined in + /// [section 3.2.6 of RFC 7230](1). + /// + /// [1]: https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 + unit: String, + /// Representation of the range set for the custom, unregistered unit. + /// This can be a string made up out of any printable ASCII character. + set: String, + }, } -fn parse_bound(s: &str) -> Option> { - if s.is_empty() { - return Some(Bound::Unbounded); - } - - s.parse().ok().map(Bound::Included) +/// A single byte range, which can either be a range or a suffix. +#[derive(Clone, Debug, PartialEq)] +pub enum ByteRangeSpec { + /// For a range, indices of the first and last byte are given. + FromTo(u64, u64), + /// Remainder of the resource, starting from the byte index. + AllFrom(u64), + /// For a suffix, only the length of the suffix is given, and the server is prompted to return + /// the last bytes only. + Last(NonZeroU64), } -impl ::Header for Range { - fn name() -> &'static ::HeaderName { - &::http::header::RANGE - } - - fn decode<'i, I: Iterator>(values: &mut I) -> Result { - values - .next() - .and_then(|val| { - if val.to_str().ok()?.starts_with("bytes=") { - Some(Range(val.clone())) - } else { - None - } - }) - .ok_or_else(::Error::invalid) - } - - fn encode>(&self, values: &mut E) { - values.extend(::std::iter::once(self.0.clone())); +impl Range { + /// Convenience method to convert into [RangeBounds], if this is a set of end-inclusive satisfiable byte ranges. + /// If it isn't, then this will return `Err`. + pub fn to_satisfiable_range_bounds( + &self, + len: u64, + ) -> Result>, ::Error> { + if let Self::Bytes(ranges) = self { + ranges + .iter() + .map(|spec| spec.to_satisfiable_range_bounds(len)) + .collect() + } else { + Err(::Error::invalid()) + } } } -/* - impl ByteRangeSpec { - /// Given the full length of the entity, attempt to normalize the byte range - /// into an satisfiable end-inclusive (from, to) range. + /// Given the full length of the entity, attempt to normalize the byte + /// range into an satisfiable end-inclusive (from, to) range. /// - /// The resulting range is guaranteed to be a satisfiable range within the bounds - /// of `0 <= from <= to < full_length`. + /// The resulting range is guaranteed to be a satisfiable range within + /// the bounds of `0 <= from <= to < full_length`. /// - /// If the byte range is deemed unsatisfiable, `None` is returned. - /// An unsatisfiable range is generally cause for a server to either reject - /// the client request with a `416 Range Not Satisfiable` status code, or to - /// simply ignore the range header and serve the full entity using a `200 OK` - /// status code. + /// If the byte range is deemed unsatisfiable, `Err` is returned. An + /// unsatisfiable range is generally cause for a server to either reject + /// the client request with a `416 Range Not Satisfiable` status code, + /// or to simply ignore the range header and serve the full entity using + /// a `200 OK` status code. /// /// This function closely follows [RFC 7233][1] section 2.1. - /// As such, it considers ranges to be satisfiable if they meet the following - /// conditions: + /// As such, it considers ranges to be satisfiable if they meet the + /// following conditions: /// /// > If a valid byte-range-set includes at least one byte-range-spec with /// a first-byte-pos that is less than the current length of the @@ -141,278 +148,438 @@ impl ByteRangeSpec { /// length of the selected representation). /// /// [1]: https://tools.ietf.org/html/rfc7233 - pub fn to_satisfiable_range(&self, full_length: u64) -> Option<(u64, u64)> { + pub fn to_satisfiable_range_bounds(&self, len: u64) -> Result, ::Error> { // If the full length is zero, there is no satisfiable end-inclusive range. - if full_length == 0 { - return None; + if len == 0 { + return Err(::Error::invalid()); } match self { - &ByteRangeSpec::FromTo(from, to) => { - if from < full_length && from <= to { - Some((from, ::std::cmp::min(to, full_length - 1))) - } else { - None - } - }, - &ByteRangeSpec::AllFrom(from) => { - if from < full_length { - Some((from, full_length - 1)) + ByteRangeSpec::FromTo(first, last) => { + // If the index of the first byte is beyond the end, or after + // the index of the first byte, the range is not satisfiable + if *first >= len || first > last { + Err(::Error::invalid()) } else { - None + // Clamp index of the last requested by to the last byte in + // the resource + Ok(( + Bound::Included(*first), + Bound::Included(u64::min(*last, len - 1)), + )) } - }, - &ByteRangeSpec::Last(last) => { - if last > 0 { - // From the RFC: If the selected representation is shorter - // than the specified suffix-length, - // the entire representation is used. - if last > full_length { - Some((0, full_length - 1)) - } else { - Some((full_length - last, full_length - 1)) - } - } else { - None + } + ByteRangeSpec::AllFrom(first) => { + if *first >= len { + return Err(::Error::invalid()); } + Ok((Bound::Included(*first), Bound::Excluded(len))) + } + ByteRangeSpec::Last(suf_len) => { + // If the requested suffix length is longer than the resource, set the starting + // bound to the start of the resource + Ok(( + Bound::Included(len.saturating_sub(suf_len.get())), + Bound::Excluded(len), + )) } } } } -impl Range { - /// Get the most common byte range header ("bytes=from-to") - pub fn bytes(from: u64, to: u64) -> Range { - Range::Bytes(vec![ByteRangeSpec::FromTo(from, to)]) +impl ::Header for Range { + fn name() -> &'static ::HeaderName { + &::http::header::RANGE + } + + fn decode<'i, I: Iterator>(values: &mut I) -> Result { + values + .next() + .and_then(|val| val.to_str().ok()?.parse().ok()) + .ok_or_else(::Error::invalid) } - /// Get byte range header with multiple subranges - /// ("bytes=from1-to1,from2-to2,fromX-toX") - pub fn bytes_multi(ranges: Vec<(u64, u64)>) -> Range { - Range::Bytes(ranges.iter().map(|r| ByteRangeSpec::FromTo(r.0, r.1)).collect()) + fn encode>(&self, values: &mut E) { + let value = ::HeaderValue::from_str(&format!("{}", self)).unwrap(); + values.extend(::std::iter::once(value)); } } +/// Builder for byte range headers +#[derive(Clone, Debug)] +pub struct ByteRangeBuilder { + inner: Vec, +} -impl fmt::Display for ByteRangeSpec { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ByteRangeSpec::FromTo(from, to) => write!(f, "{}-{}", from, to), - ByteRangeSpec::Last(pos) => write!(f, "-{}", pos), - ByteRangeSpec::AllFrom(pos) => write!(f, "{}-", pos), - } +impl Default for ByteRangeBuilder { + fn default() -> Self { + Self::new() } } +impl ByteRangeBuilder { + /// Create a new byte range builder + pub fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Add a range to the header. Fails if the end bound of the range + /// is before the start bound. + pub fn range(mut self, bounds: impl RangeBounds) -> Result { + match reduce_bounds(bounds) { + (first, Some(last)) => { + if first > last { + return Err(err()); + } + self.inner.push(ByteRangeSpec::FromTo(first, last)); + } + (first, None) => { + self.inner.push(ByteRangeSpec::AllFrom(first)); + } + }; + Ok(self) + } -impl fmt::Display for Range { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Range::Bytes(ref ranges) => { - try!(write!(f, "bytes=")); + /// Add a suffix to the header. Fails if the suffix length is zero. + pub fn suffix(mut self, len: u64) -> Result { + let len = match NonZeroU64::new(len) { + Some(len) => len, + None => return Err(err()), + }; + + self.inner.push(ByteRangeSpec::Last(len)); + Ok(self) + } + + /// Finish up the header. Fails if no ranges or suffixes were added. + pub fn finish(self) -> Result { + if self.inner.is_empty() { + return Err(err()); + } + Ok(Range::Bytes(self.inner)) + } +} +impl std::fmt::Display for Range { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Range::Bytes(ranges) => { + write!(f, "bytes=")?; for (i, range) in ranges.iter().enumerate() { if i != 0 { - try!(f.write_str(",")); + f.write_str(",")?; } - try!(Display::fmt(range, f)); + std::fmt::Display::fmt(range, f)?; } Ok(()) - }, - Range::Unregistered(ref unit, ref range_str) => { - write!(f, "{}={}", unit, range_str) - }, + } + Range::Unregistered { unit, set } => write!(f, "{}={}", unit, set), } } } impl FromStr for Range { - type Err = ::Error; + type Err = InvalidRange; - fn from_str(s: &str) -> ::Result { + fn from_str(s: &str) -> Result { let mut iter = s.splitn(2, '='); match (iter.next(), iter.next()) { - (Some("bytes"), Some(ranges)) => { - let ranges = from_comma_delimited(ranges); - if ranges.is_empty() { - return Err(::Error::Header); + (Some("bytes"), Some(set)) => { + let specs: Result, Self::Err> = + set.split(',').map(|spec| spec.parse()).collect(); + match specs { + Ok(specs) if !specs.is_empty() => Ok(Self::Bytes(specs)), + _ => Err(err()), } - Ok(Range::Bytes(ranges)) } - (Some(unit), Some(range_str)) if unit != "" && range_str != "" => { - Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())) + (Some(unit), Some(set)) if !unit.is_empty() && !set.is_empty() => { + Ok(Self::Unregistered { + unit: unit.to_string(), + set: set.to_string(), + }) + } + _ => Err(err()), + } + } +} - }, - _ => Err(::Error::Header) +impl std::fmt::Display for ByteRangeSpec { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self { + ByteRangeSpec::FromTo(first, last) => write!(f, "{}-{}", first, last), + ByteRangeSpec::Last(len) => write!(f, "-{}", len), + ByteRangeSpec::AllFrom(first) => write!(f, "{}-", first), } } } impl FromStr for ByteRangeSpec { - type Err = ::Error; + type Err = InvalidRange; - fn from_str(s: &str) -> ::Result { + fn from_str(s: &str) -> Result { let mut parts = s.splitn(2, '-'); match (parts.next(), parts.next()) { - (Some(""), Some(end)) => { - end.parse().or(Err(::Error::Header)).map(ByteRangeSpec::Last) - }, - (Some(start), Some("")) => { - start.parse().or(Err(::Error::Header)).map(ByteRangeSpec::AllFrom) + (Some(""), Some(end)) => end.parse().or(Err(err())).map(ByteRangeSpec::Last), + (Some(start), Some("")) => start.parse().or(Err(err())).map(ByteRangeSpec::AllFrom), + (Some(start), Some(end)) => match (start.parse(), end.parse()) { + (Ok(start), Ok(end)) if start <= end => Ok(ByteRangeSpec::FromTo(start, end)), + _ => Err(err()), }, - (Some(start), Some(end)) => { - match (start.parse(), end.parse()) { - (Ok(start), Ok(end)) if start <= end => Ok(ByteRangeSpec::FromTo(start, end)), - _ => Err(::Error::Header) - } - }, - _ => Err(::Error::Header) + _ => Err(err()), } } } +error_type!(InvalidRange); -fn from_comma_delimited(s: &str) -> Vec { - s.split(',') - .filter_map(|x| match x.trim() { - "" => None, - y => Some(y) - }) - .filter_map(|x| x.parse().ok()) - .collect() +fn err() -> InvalidRange { + InvalidRange { _inner: () } } -impl Header for Range { +fn reduce_bounds(bounds: impl RangeBounds) -> (u64, Option) { + ( + match bounds.start_bound() { + Bound::Included(n) => *n, + Bound::Excluded(n) => n + 1, + Bound::Unbounded => 0, + }, + match bounds.end_bound() { + Bound::Included(n) => Some(*n), + Bound::Excluded(n) => Some(n - 1), + Bound::Unbounded => None, + }, + ) +} - fn header_name() -> &'static str { - static NAME: &'static str = "Range"; - NAME - } +#[cfg(test)] +mod test { + use std::num::NonZeroU64; - fn parse_header(raw: &Raw) -> ::Result { - from_one_raw_str(raw) - } + use crate::common::test_decode; + use crate::{ByteRangeBuilder, ByteRangeSpec, Range}; - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) - } + use super::reduce_bounds; -} + #[test] + fn test_from_str_byte_range_spec() { + let r: ByteRangeSpec = "1-100".parse().unwrap(); + let r2 = ByteRangeSpec::FromTo(1, 100); + assert_eq!(r, r2); -#[test] -fn test_parse_bytes_range_valid() { - let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); - let r3 = Range::bytes(1, 100); - assert_eq!(r, r2); - assert_eq!(r2, r3); - - let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap(); - let r3 = Range::Bytes( - vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)] - ); - assert_eq!(r, r2); - assert_eq!(r2, r3); - - let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); - let r3 = Range::Bytes( - vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)] - ); - assert_eq!(r, r2); - assert_eq!(r2, r3); - - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); + let r: ByteRangeSpec = "200-".parse().unwrap(); + let r2 = ByteRangeSpec::AllFrom(200); + assert_eq!(r, r2); -} + let r: ByteRangeSpec = "-100".parse().unwrap(); + let r2 = ByteRangeSpec::Last(NonZeroU64::new(100).unwrap()); + assert_eq!(r, r2); + } -#[test] -fn test_parse_unregistered_range_valid() { - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); + #[test] + fn test_from_str_range() { + let r: Range = "bytes=1-100".parse().unwrap(); + let r2 = ByteRangeBuilder::new() + .range(1..=100) + .unwrap() + .finish() + .unwrap(); + assert_eq!(r, r2); + + assert!("bytes=".parse::().is_err()) + } - let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned()); - assert_eq!(r, r2); + #[test] + fn test_parse_bytes_range_valid() { + let r: Range = test_decode(&["bytes=1-100"]).unwrap(); + let r2 = ByteRangeBuilder::new() + .range(1..=100) + .unwrap() + .finish() + .unwrap(); + assert_eq!(r, r2); + + let r: Range = test_decode(&["bytes=1-100,200-"]).unwrap(); + let r2 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::AllFrom(200), + ]); + assert_eq!(r, r2); + + let r: Range = test_decode(&["bytes=1-100,-100"]).unwrap(); + let r2 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::Last(NonZeroU64::new(100).unwrap()), + ]); + assert_eq!(r, r2); + + let r: Range = test_decode(&["custom=1-100,-100"]).unwrap(); + let r2 = Range::Unregistered { + unit: "custom".to_owned(), + set: "1-100,-100".to_owned(), + }; + assert_eq!(r, r2); + } - let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); - assert_eq!(r, r2); -} + #[test] + fn test_parse_unregistered_range_valid() { + let r: Range = test_decode(&["custom=1-100,-100"]).unwrap(); + let r2 = Range::Unregistered { + unit: "custom".to_owned(), + set: "1-100,-100".to_owned(), + }; + assert_eq!(r, r2); -#[test] -fn test_parse_invalid() { - let r: ::Result = Header::parse_header(&"bytes=1-a,-".into()); - assert_eq!(r.ok(), None); + let r: Range = test_decode(&["custom=abcd"]).unwrap(); + let r2 = Range::Unregistered { + unit: "custom".to_owned(), + set: "abcd".to_owned(), + }; + assert_eq!(r, r2); - let r: ::Result = Header::parse_header(&"bytes=1-2-3".into()); - assert_eq!(r.ok(), None); + let r: Range = test_decode(&["custom=xxx-yyy"]).unwrap(); + let r2 = Range::Unregistered { + unit: "custom".to_owned(), + set: "xxx-yyy".to_owned(), + }; + assert_eq!(r, r2); + } - let r: ::Result = Header::parse_header(&"abc".into()); - assert_eq!(r.ok(), None); + #[test] + fn test_parse_invalid() { + let r: Option = test_decode(&["bytes=1-a,-"]); + assert_eq!(r, None); - let r: ::Result = Header::parse_header(&"bytes=1-100=".into()); - assert_eq!(r.ok(), None); + let r: Option = test_decode(&["bytes=1-2-3"]); + assert_eq!(r, None); - let r: ::Result = Header::parse_header(&"bytes=".into()); - assert_eq!(r.ok(), None); + let r: Option = test_decode(&["abc"]); + assert_eq!(r, None); - let r: ::Result = Header::parse_header(&"custom=".into()); - assert_eq!(r.ok(), None); + let r: Option = test_decode(&["bytes=1-100="]); + assert_eq!(r, None); - let r: ::Result = Header::parse_header(&"=1-100".into()); - assert_eq!(r.ok(), None); -} + let r: Option = test_decode(&["bytes="]); + assert_eq!(r, None); -#[test] -fn test_fmt() { - use Headers; + let r: Option = test_decode(&["custom="]); + assert_eq!(r, None); - let mut headers = Headers::new(); + let r: Option = test_decode(&["=1-100"]); + assert_eq!(r, None); + } - headers.set( - Range::Bytes( - vec![ByteRangeSpec::FromTo(0, 1000), ByteRangeSpec::AllFrom(2000)] - )); - assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); + #[test] + fn test_fmt() { + use crate::HeaderMap as Headers; + use crate::HeaderMapExt; - headers.clear(); - headers.set(Range::Bytes(vec![])); + let mut headers = Headers::new(); - assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); + headers.typed_insert(Range::Bytes(vec![ + ByteRangeSpec::FromTo(0, 1000), + ByteRangeSpec::AllFrom(2000), + ])); + assert_eq!(headers["Range"], "bytes=0-1000,2000-"); - headers.clear(); - headers.set(Range::Unregistered("custom".to_owned(), "1-xxx".to_owned())); + headers.typed_insert(Range::Unregistered { + unit: "custom".to_owned(), + set: "1-xxx".to_owned(), + }); - assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n"); -} + assert_eq!(headers["Range"], "custom=1-xxx"); + } -#[test] -fn test_byte_range_spec_to_satisfiable_range() { - assert_eq!(Some((0, 0)), ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3)); - assert_eq!(Some((1, 2)), ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3)); - assert_eq!(Some((1, 2)), ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0)); - - assert_eq!(Some((0, 2)), ByteRangeSpec::AllFrom(0).to_satisfiable_range(3)); - assert_eq!(Some((2, 2)), ByteRangeSpec::AllFrom(2).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::AllFrom(3).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::AllFrom(5).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::AllFrom(0).to_satisfiable_range(0)); - - assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3)); - assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3)); - assert_eq!(Some((0, 2)), ByteRangeSpec::Last(5).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0)); + #[test] + fn test_byte_range_spec_to_satisfiable_range() { + assert_eq!( + (0, Some(0)), + reduce_bounds( + ByteRangeSpec::FromTo(0, 0) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + assert_eq!( + (1, Some(2)), + reduce_bounds( + ByteRangeSpec::FromTo(1, 2) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + assert_eq!( + (1, Some(2)), + reduce_bounds( + ByteRangeSpec::FromTo(1, 5) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + + assert!(ByteRangeSpec::FromTo(3, 3) + .to_satisfiable_range_bounds(3) + .is_err()); + assert!(ByteRangeSpec::FromTo(2, 1) + .to_satisfiable_range_bounds(3) + .is_err()); + assert!(ByteRangeSpec::FromTo(0, 0) + .to_satisfiable_range_bounds(0) + .is_err()); + + assert_eq!( + (0, Some(2)), + reduce_bounds( + ByteRangeSpec::AllFrom(0) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + assert_eq!( + (2, Some(2)), + reduce_bounds( + ByteRangeSpec::AllFrom(2) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + + assert!(ByteRangeSpec::AllFrom(3) + .to_satisfiable_range_bounds(3) + .is_err()); + assert!(ByteRangeSpec::AllFrom(5) + .to_satisfiable_range_bounds(3) + .is_err()); + assert!(ByteRangeSpec::AllFrom(0) + .to_satisfiable_range_bounds(0) + .is_err()); + + assert_eq!( + (1, Some(2)), + reduce_bounds( + ByteRangeSpec::Last(NonZeroU64::new(2).unwrap()) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + assert_eq!( + (2, Some(2)), + reduce_bounds( + ByteRangeSpec::Last(NonZeroU64::new(1).unwrap()) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + assert_eq!( + (0, Some(2)), + reduce_bounds( + ByteRangeSpec::Last(NonZeroU64::new(5).unwrap()) + .to_satisfiable_range_bounds(3) + .unwrap() + ) + ); + + assert!(ByteRangeSpec::Last(NonZeroU64::new(2).unwrap()) + .to_satisfiable_range_bounds(0) + .is_err()); + } } - -bench_header!(bytes_multi, Range, { vec![b"bytes=1-1001,2001-3001,10001-".to_vec()]}); -bench_header!(custom_unit, Range, { vec![b"other=0-100000".to_vec()]}); -*/