ptth/crates/ptth_server/src/file_server/range.rs

76 lines
1.6 KiB
Rust

use std::ops::Range;
use regex::Regex;
pub fn parse_range_header (range_str: &str) -> (Option <u64>, Option <u64>) {
use lazy_static::lazy_static;
lazy_static! {
static ref RE: Regex = Regex::new (r"^bytes=(\d*)-(\d*)$").expect ("Couldn't compile regex for Range header");
}
let caps = match RE.captures (range_str) {
Some (x) => x,
None => return (None, None),
};
let start = caps.get (1).map (|x| x.as_str ());
let end = caps.get (2).map (|x| x.as_str ());
let start = start.and_then (|x| u64::from_str_radix (x, 10).ok ());
// HTTP specifies ranges as [start inclusive, end inclusive]
// But that's dumb and [start inclusive, end exclusive) is better
let end = end.and_then (|x| u64::from_str_radix (x, 10).ok ().map (|x| x + 1));
(start, end)
}
#[derive (Debug, PartialEq)]
pub struct ValidParsedRange {
pub range: Range <u64>,
pub range_requested: bool,
}
#[derive (Debug, PartialEq)]
pub enum ParsedRange {
Valid (ValidParsedRange),
RangeNotSatisfiable (u64),
}
pub fn check_range (range_str: Option <&str>, file_len: u64)
-> ParsedRange
{
use ParsedRange::*;
let not_satisfiable = RangeNotSatisfiable (file_len);
let range_str = match range_str {
None => return Valid (ValidParsedRange {
range: 0..file_len,
range_requested: false,
}),
Some (x) => x,
};
let (start, end) = parse_range_header (range_str);
let start = start.unwrap_or (0);
if start >= file_len {
return not_satisfiable;
}
let end = end.unwrap_or (file_len);
if end > file_len {
return not_satisfiable;
}
if end < start {
return not_satisfiable;
}
Valid (ValidParsedRange {
range: start..end,
range_requested: true,
})
}