use http::header::HeaderValue;
use smallvec::SmallVec;
use std::cmp;
use std::ops::Range;
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum ResolvedRanges {
None,
NotSatisfiable,
Satisfiable(SmallVec<[Range<u64>; 1]>),
}
pub(crate) fn parse(range: Option<&HeaderValue>, len: u64) -> ResolvedRanges {
let range = match range.and_then(|v| v.to_str().ok()) {
None => return ResolvedRanges::None,
Some(r) => r,
};
if !range.starts_with("bytes=") {
return ResolvedRanges::None;
}
let mut ranges: SmallVec<[Range<u64>; 1]> = SmallVec::new();
for r in range[6..].split(',') {
let r = r.trim_start_matches(|c| c == ' ' || c == '\t');
let hyphen = match r.find('-') {
None => return ResolvedRanges::None, Some(h) => h,
};
if hyphen == 0 {
let last = match u64::from_str(&r[1..]) {
Err(_) => return ResolvedRanges::None, Ok(l) => l,
};
if last >= len {
continue; }
ranges.push((len - last)..len);
} else {
let first = match u64::from_str(&r[0..hyphen]) {
Err(_) => return ResolvedRanges::None, Ok(f) => f,
};
let end = if r.len() > hyphen + 1 {
cmp::min(
match u64::from_str(&r[hyphen + 1..]) {
Err(_) => return ResolvedRanges::None, Ok(l) => l,
} + 1,
len,
)
} else {
len };
if first >= end {
continue; }
ranges.push(first..end);
}
}
if !ranges.is_empty() {
return ResolvedRanges::Satisfiable(ranges);
}
ResolvedRanges::NotSatisfiable
}
#[cfg(test)]
mod tests {
use super::{parse, ResolvedRanges};
use http::header::HeaderValue;
use smallvec::SmallVec;
#[test]
fn test_resolve_ranges_rfc() {
let mut v = SmallVec::new();
v.push(0..500);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=0-499")), 10000)
);
v.clear();
v.push(500..1000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=500-999")), 10000)
);
v.clear();
v.push(9500..10000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=-500")), 10000)
);
v.clear();
v.push(9500..10000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=9500-")), 10000)
);
v.clear();
v.push(0..1);
v.push(9999..10000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=0-0,-1")), 10000)
);
v.clear();
v.push(500..601);
v.push(601..1000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(
Some(&HeaderValue::from_static("bytes=500-600, 601-999")),
10000
)
);
v.clear();
v.push(500..701);
v.push(601..1000);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(
Some(&HeaderValue::from_static("bytes=500-700, 601-999")),
10000
)
);
}
#[test]
fn test_resolve_ranges_satisfiability() {
assert_eq!(
ResolvedRanges::NotSatisfiable,
parse(Some(&HeaderValue::from_static("bytes=10000-")), 10000)
);
let mut v = SmallVec::new();
v.push(0..500);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=0-499,10000-")), 10000)
);
assert_eq!(
ResolvedRanges::NotSatisfiable,
parse(Some(&HeaderValue::from_static("bytes=-1")), 0)
);
assert_eq!(
ResolvedRanges::NotSatisfiable,
parse(Some(&HeaderValue::from_static("bytes=0-0")), 0)
);
assert_eq!(
ResolvedRanges::NotSatisfiable,
parse(Some(&HeaderValue::from_static("bytes=0-")), 0)
);
v.clear();
v.push(0..1);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=0-0")), 1)
);
v.clear();
v.push(0..500);
assert_eq!(
ResolvedRanges::Satisfiable(v.clone()),
parse(Some(&HeaderValue::from_static("bytes=0-10000")), 500)
);
}
#[test]
fn test_resolve_ranges_absent_or_invalid() {
assert_eq!(ResolvedRanges::None, parse(None, 10000));
}
#[test]
fn test_nonascii() {
assert_eq!(
ResolvedRanges::None,
parse(Some(&HeaderValue::from_bytes(b"\xff").unwrap()), 10000)
);
}
}