use http::header;
use crate::request_filter::RequestFilter;
use std::str::FromStr;
use pingora::proxy::Session;
use crate::metadata::Metadata;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Range {
Valid(u64, u64),
OutOfBounds,
}
impl Range {
pub fn parse(range: &str, file_size: u64) -> Option<Self> {
let (units, range) = range.split_once('=')?;
if units != "bytes" {
return None;
}
let (start, end) = range.trim().split_once('-')?;
let (start, end) = if start.is_empty() {
let len = u64::from_str(end.trim()).ok()?;
if len > file_size {
return Some(Self::OutOfBounds);
}
(file_size - len, file_size - 1)
} else if end.is_empty() {
(u64::from_str(start.trim()).ok()?, file_size - 1)
} else {
(
u64::from_str(start.trim()).ok()?,
u64::from_str(end.trim()).ok()?,
)
};
if end >= file_size || start > end {
Some(Self::OutOfBounds)
} else {
Some(Self::Valid(start, end))
}
}
}
pub fn extract_range(session: &Session, meta: &Metadata) -> Option<Range> {
let headers = &session.req_header().headers;
if let Some(value) = headers
.get(header::IF_RANGE)
.and_then(|value| value.to_str().ok())
{
if value != meta.etag
&& !meta
.modified
.as_ref()
.is_some_and(|modified| modified == value)
{
return None;
}
}
let value = headers.get(header::RANGE)?;
let value = value.to_str().ok()?;
Range::parse(value, meta.size)
}
#[cfg(test)]
mod tests {
use super::*;
}