use std::ops::Range;
pub static RANGE_UNIT: &str = "bytes";
#[derive(Debug, PartialEq)]
pub struct HttpRange {
pub ranges: Vec<Range<u64>>,
pub complete_length: Option<CompleteLength>,
}
#[derive(Debug, PartialEq)]
pub enum CompleteLength {
Representation(u64),
Unknown,
}
impl HttpRange {
pub fn from_header(content_range: &str, content_length: u64) -> Option<HttpRange> {
if content_range.is_empty() {
return None;
}
let parts = content_range
.split("=")
.map(|p| p.trim())
.collect::<Vec<_>>();
if parts.is_empty() {
return None;
}
if parts[0] != RANGE_UNIT {
return None;
}
if parts.len() != 2 {
return None;
}
let params = parts[1].split("/").map(|p| p.trim()).collect::<Vec<_>>();
if params.is_empty() {
return None;
}
if params.len() > 2 {
return None;
}
let range_params = params[0].split(",");
let length_param = if params.len() == 2 { params[1] } else { "" };
let mut ranges = Vec::<Range<u64>>::new();
for range_param in range_params {
let values = range_param.split("-").map(|v| v.trim()).collect::<Vec<_>>();
if values.len() != 2 {
return None;
}
let mut range = 0..content_length - 1;
let start = values[0];
let end = values[1];
if !start.is_empty() && !end.is_empty() {
range.start = start.parse::<u64>().unwrap();
range.end = end.parse::<u64>().unwrap();
}
if start.is_empty() && !end.is_empty() {
let count = end.parse::<u64>().unwrap();
range.start = content_length - count;
}
if !start.is_empty() && end.is_empty() {
range.start = start.parse::<u64>().unwrap();
}
ranges.push(range);
}
ranges.sort_by(|a, b| a.start.cmp(&b.start));
let ranges_count = ranges.len();
if ranges_count > 1 {
let mut retain = vec![true; ranges_count];
let mut range_last = ranges[0].clone();
for (index, range) in ranges.iter_mut().enumerate() {
if index != 0 && (range_last.end + 1) >= range.start {
range.start = range_last.start;
retain[index - 1] = false;
}
range_last = range.clone();
}
let mut index = 0;
ranges.retain(|_| {
let keep = retain[index];
index += 1;
keep
});
}
let complete_length = match length_param {
"" => None,
"*" => Some(CompleteLength::Unknown),
_ => Some(CompleteLength::Representation(
length_param.parse::<u64>().unwrap(),
)),
};
let http_range = HttpRange {
ranges,
complete_length,
};
Some(http_range)
}
pub fn to_header(&self) -> String {
if self.ranges.is_empty() {
return "".to_string();
}
let ranges = self
.ranges
.iter()
.map(|r| format!("{}-{}", r.start, r.end))
.collect::<Vec<_>>()
.join(",");
match &self.complete_length {
Some(CompleteLength::Representation(content_length)) => {
format!("{}={}/{}", RANGE_UNIT, ranges, content_length)
}
Some(CompleteLength::Unknown) => format!("{}={}/*", RANGE_UNIT, ranges),
None => format!("{}={}", RANGE_UNIT, ranges),
}
}
pub fn none_satisfiable(&self, content_length: u64) -> bool {
!self.any_satisfiable(content_length)
}
pub fn any_satisfiable(&self, content_length: u64) -> bool {
for range in &self.ranges {
if HttpRange::range_satisfiable(range, content_length) {
return true;
}
}
false
}
pub fn range_satisfiable(range: &Range<u64>, content_length: u64) -> bool {
range.start < content_length
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test1() {
let http_range = HttpRange::from_header("bytes=0-499", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..499],
complete_length: None
}
);
}
#[test]
fn complete_length_unknown() {
let http_range = HttpRange::from_header("bytes=0-499/*", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..499],
complete_length: Some(CompleteLength::Unknown)
}
);
}
#[test]
fn complete_length_test2() {
let http_range = HttpRange::from_header("bytes=0-499/8000", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..499],
complete_length: Some(CompleteLength::Representation(8000))
}
);
}
#[test]
fn test2() {
let http_range = HttpRange::from_header("bytes=500-999", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: None
}
);
}
#[test]
fn test3() {
let http_range = HttpRange::from_header("bytes=-500", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![9500..9999],
complete_length: None
}
);
}
#[test]
fn test4() {
let http_range = HttpRange::from_header("bytes=9500-", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![9500..9999],
complete_length: None
}
);
}
#[test]
fn test5() {
let http_range = HttpRange::from_header("bytes=0-0,-1", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..0, 9999..9999],
complete_length: None
}
);
}
#[test]
fn test6() {
let http_range = HttpRange::from_header("bytes= 0-999, 4500-5499, -1000", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..999, 4500..5499, 9000..9999],
complete_length: None
}
);
}
#[test]
fn combined_merge_test6() {
let http_range = HttpRange::from_header("bytes=500-600,601-999", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: None
}
);
}
#[test]
fn combined_merge_test7() {
let http_range = HttpRange::from_header("bytes=601-999,500-600", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: None
}
);
}
#[test]
fn combined_merge_test8() {
let http_range = HttpRange::from_header("bytes=500-700,601-999", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: None
}
);
}
#[test]
fn combined_merge_test9() {
let http_range = HttpRange::from_header("bytes=601-999,500-700", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: None
}
);
}
#[test]
fn combined_merge_test10() {
let http_range = HttpRange::from_header("bytes=300-400,400-700,601-999", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![300..999],
complete_length: None
}
);
}
#[test]
fn representation_test1() {
let http_range = HttpRange::from_header("bytes=0-499/1234", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![0..499],
complete_length: Some(CompleteLength::Representation(1234))
}
);
}
#[test]
fn representation_test2() {
let http_range = HttpRange::from_header("bytes=500-999/1234", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..999],
complete_length: Some(CompleteLength::Representation(1234))
}
);
}
#[test]
fn representation_test3() {
let http_range = HttpRange::from_header("bytes=500-1233/1234", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![500..1233],
complete_length: Some(CompleteLength::Representation(1234))
}
);
}
#[test]
fn representation_test4() {
let http_range = HttpRange::from_header("bytes=734-1233/1234", 10000).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![734..1233],
complete_length: Some(CompleteLength::Representation(1234))
}
);
}
#[test]
fn to_header_test1() {
let http_range = HttpRange::from_header("bytes=734-1233/1234", 1234).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![734..1233],
complete_length: Some(CompleteLength::Representation(1234))
}
);
assert_eq!(http_range.to_header(), "bytes=734-1233/1234");
}
#[test]
fn to_header_test2() {
let http_range = HttpRange::from_header("bytes=734-1233/*", 1234).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![734..1233],
complete_length: Some(CompleteLength::Unknown)
}
);
assert_eq!(http_range.to_header(), "bytes=734-1233/*");
}
#[test]
fn to_header_test3() {
let http_range = HttpRange::from_header("bytes=734-1233", 1234).unwrap();
assert_eq!(
http_range,
HttpRange {
ranges: vec![734..1233],
complete_length: None
}
);
assert_eq!(http_range.to_header(), "bytes=734-1233");
}
}