diff --git a/api/src/main.rs b/api/src/main.rs index 9c2ee8b..92c2c3b 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -9,6 +9,7 @@ mod category; mod video; mod thumbnail; mod common; +mod partial; #[cfg(feature = "admin")] mod admin; diff --git a/api/src/partial.rs b/api/src/partial.rs new file mode 100644 index 0000000..fc67e83 --- /dev/null +++ b/api/src/partial.rs @@ -0,0 +1,65 @@ +use rocket::http::Status; +use rocket::request::{Outcome, Request, FromRequest}; + +// Only single byte ranges are supported right now +pub struct Range { + start: u32, + end: Option +} + +#[derive(Debug)] +pub enum RangeError { + Invalid, + Malformed +} + + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Range { + type Error = RangeError; + async fn from_request(req: &'r Request<'_>) -> Outcome { + // No range basically just means get the whole file + let range_raw = req.headers().get_one("range"); + if range_raw.is_none() { + return Outcome::Success(Self { start: 0, end: None }) + } + let range_raw = range_raw.unwrap(); + + // First make sure that we only ever handle byte ranges + const PREFIX: &'static str = "bytes="; + if !range_raw.starts_with(PREFIX) { + return Outcome::Failure((Status::BadRequest, RangeError::Invalid)) + } + + // Next strip the 'bytes=' out and grab the only everything until either + // ',' + // ! Strip the comma if its there so we end up with a slice that matches + // [0-9]+-[0-9]+ + let range_raw = range_raw + .strip_prefix(PREFIX).unwrap(); + + if let Some(ranges) = range_raw.split_once('-') { + let start = ranges.0.parse::(); + let end = ranges.1.parse::().ok(); + println!("Range collected {:?} -> {:?}", start, end); + match (start, end) { + (Ok(start), Some(end)) => { + if end <= start { + println!("Start end range invalid {}->{}", start, end); + return Outcome::Failure((Status::BadRequest, RangeError::Malformed)) + } + return Outcome::Success(Self { + start, end: Some(end) + }) + } + (Ok(start), None) => { + return Outcome::Success(Self { start, end: None }) + } + _ => Outcome::Failure((Status::BadRequest, RangeError::Invalid)) + } + } else { + return Outcome::Failure((Status::BadRequest, RangeError::Invalid)); + } + } +} +