diff --git a/src/common/range.rs b/src/common/range.rs index 29cc79d3..3ce64f8c 100644 --- a/src/common/range.rs +++ b/src/common/range.rs @@ -48,29 +48,50 @@ 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), + // These do not directly translate. + //(Bound::Unbounded, Bound::Included(end)) => format!("bytes=-{}", end), + //(Bound::Unbounded, Bound::Excluded(&end)) => format!("bytes=-{}", end - 1), + _ => 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 { + /// Iterate the range sets as a tuple of bounds, if valid with length. + /// + /// The length of the content is passed as an argument, and all ranges + /// that can be satisfied will be iterated. + pub fn satisfiable_ranges<'a>(&'a self, len: u64) -> impl Iterator, Bound)> + 'a { let s = self .0 .to_str() .expect("valid string checked in Header::decode()"); - s["bytes=".len()..].split(',').filter_map(|spec| { + s["bytes=".len()..].split(',').filter_map(move |spec| { let mut iter = spec.trim().splitn(2, '-'); - Some((parse_bound(iter.next()?)?, parse_bound(iter.next()?)?)) + let start = parse_bound(iter.next()?)?; + let end = parse_bound(iter.next()?)?; + + // Unbounded ranges in HTTP are actually a suffix + // For example, `-100` means the last 100 bytes. + if let Bound::Unbounded = start { + if let Bound::Included(end) = end { + if len < end { + // Last N bytes is larger than available! + return None; + } + return Some((Bound::Included(len - end), Bound::Unbounded)); + } + // else fall through + } + + Some((start, end)) }) } } @@ -416,3 +437,11 @@ fn test_byte_range_spec_to_satisfiable_range() { 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()]}); */ + +#[test] +fn test_to_satisfiable_range_suffix() { + let range = super::test_decode::(&["bytes=-100"]).unwrap(); + let bounds = range.satisfiable_ranges(350).next().unwrap(); + assert_eq!(bounds, (Bound::Included(250), Bound::Unbounded)); +} +