From 6ccdb0fdc376e00cb3a5b46a18b9ad4bdf2e82ac Mon Sep 17 00:00:00 2001 From: kieran Date: Thu, 27 Feb 2025 16:01:03 +0000 Subject: [PATCH] fix: range header response --- src/routes/mod.rs | 63 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/routes/mod.rs b/src/routes/mod.rs index a9b6c9a..6b3c61a 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -9,8 +9,10 @@ pub use crate::routes::blossom::blossom_routes; pub use crate::routes::nip96::nip96_routes; use crate::settings::Settings; use crate::void_file::VoidFile; -use anyhow::Error; -use http_range_header::{parse_range_header, EndPosition, StartPosition}; +use anyhow::{Error, Result}; +use http_range_header::{ + parse_range_header, EndPosition, StartPosition, SyntacticallyCorrectRange, +}; use log::{debug, warn}; use nostr::Event; use rocket::fs::NamedFile; @@ -121,6 +123,7 @@ struct RangeBody { poll_complete: bool, } +const MAX_UNBOUNDED_RANGE: u64 = 1024 * 1024; impl RangeBody { pub fn new(file: File, range: Range) -> Self { Self { @@ -131,6 +134,18 @@ impl RangeBody { poll_complete: false, } } + + pub fn get_range(file_size: u64, header: &SyntacticallyCorrectRange) -> Range { + let range_start = match header.start { + StartPosition::Index(i) => i, + StartPosition::FromLast(i) => file_size - i, + }; + let range_end = match header.end { + EndPosition::Index(i) => i, + EndPosition::LastByte => file_size - 1, + }; + range_start..range_end + } } impl AsyncRead for RangeBody { @@ -190,7 +205,6 @@ impl<'r> Responder<'r, 'static> for FilePayload { // handle ranges #[cfg(feature = "ranges")] { - const MAX_UNBOUNDED_RANGE: u64 = 1024 * 1024; // only use range response for files > 1MiB if self.info.size < MAX_UNBOUNDED_RANGE { response.set_sized_body(None, self.file); @@ -205,18 +219,9 @@ impl<'r> Responder<'r, 'static> for FilePayload { response.set_streamed_body(self.file); } else { let single_range = ranges.ranges.first().unwrap(); - let range_start = match single_range.start { - StartPosition::Index(i) => i, - StartPosition::FromLast(i) => self.info.size - i, - }; - let range_end = match single_range.end { - EndPosition::Index(i) => i, - EndPosition::LastByte => { - (range_start + MAX_UNBOUNDED_RANGE).min(self.info.size) - } - }; - let r_len = range_end - range_start; - let r_body = RangeBody::new(self.file, range_start..range_end); + let range = RangeBody::get_range(self.info.size, single_range); + let r_len = range.end.saturating_sub(range.start); + let r_body = RangeBody::new(self.file, range.clone()); response.set_status(Status::PartialContent); response.set_header(Header::new("content-length", r_len.to_string())); @@ -224,8 +229,8 @@ impl<'r> Responder<'r, 'static> for FilePayload { "content-range", format!( "bytes {}-{}/{}", - range_start, - range_end - 1, + range.start, + range.end, self.info.size ), )); @@ -486,3 +491,27 @@ pub struct VoidCatFile { pub status: Status, pub uuid: Header<'static>, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ranges() -> Result<()> { + let size = 16482469; + let req = parse_range_header("bytes=1122304-16482468")?; + let range = req.ranges.first().unwrap(); + + let r = RangeBody::get_range(size, range); + assert_eq!(r.start, 1122304); + assert_eq!(r.end, 16482468); + + let req = parse_range_header("bytes=16482467-")?; + let range = req.ranges.first().unwrap(); + + let r = RangeBody::get_range(size, range); + assert_eq!(r.start, 16482467); + assert_eq!(r.end, 16482468); + Ok(()) + } +} \ No newline at end of file