diff --git a/src/headers/content_range.rs b/src/headers/content_range.rs index 02e7b31..6f852c4 100644 --- a/src/headers/content_range.rs +++ b/src/headers/content_range.rs @@ -30,9 +30,10 @@ impl HttpContentRange { ( HttpRange::Range(OrderedRange { start, end }), HttpContentRange::Bound(Bound { range, .. }), - ) => start == range.start() && end == range.end(), + ) => start == range.start() && end >= range.end(), (HttpRange::Suffix(suffix), HttpContentRange::Bound(Bound { range, size })) => { - let length_matches = (range.end() - range.start()).checked_add(1) == Some(suffix); + let length = (range.end() - range.start()).checked_add(1); + let length_matches = length.is_some_and(|len| len <= suffix); let ends_at_boundary = size.is_none_or(|size| range.end() + 1 == size); length_matches && ends_at_boundary } @@ -44,10 +45,9 @@ impl HttpContentRange { HttpRange::Range(OrderedRange { end: n, .. }), HttpContentRange::Unsatisfiable(Unsatisfiable { size }), ) => n >= *size, - ( - HttpRange::Suffix(suffix), - HttpContentRange::Unsatisfiable(Unsatisfiable { size }), - ) => suffix > *size, + (HttpRange::Suffix(suffix), HttpContentRange::Unsatisfiable(Unsatisfiable { .. })) => { + suffix == 0 + } } } } diff --git a/src/headers/tests.rs b/src/headers/tests.rs index 11ac67c..1778a2e 100644 --- a/src/headers/tests.rs +++ b/src/headers/tests.rs @@ -146,12 +146,52 @@ mod content_range { } #[test] - fn unsuccessful_range_suffix_range_content_bound() { + fn unsuccessful_range_suffix_zero_content_unsatisfiable() { + let range = HttpRange::Suffix(0); + let content_range = HttpContentRange::Unsatisfiable(Unsatisfiable::new(20)); + + assert!(content_range.matches_requested_range(range)); + } + + #[test] + fn suffix_exceeding_size_is_not_unsatisfiable() { let range = HttpRange::Suffix(50); let content_range = HttpContentRange::Unsatisfiable(Unsatisfiable::new(20)); + assert!(!content_range.matches_requested_range(range)); + } + + #[test] + fn range_with_clamped_end_matches() { + let range = HttpRange::Range(OrderedRange::new(0..=999).unwrap()); + let content_range = HttpContentRange::Bound(Bound::new(0..=49, Some(50)).unwrap()); + assert!(content_range.matches_requested_range(range)); } + + #[test] + fn suffix_clamped_to_file_size_matches() { + let range = HttpRange::Suffix(999); + let content_range = HttpContentRange::Bound(Bound::new(0..=49, Some(50)).unwrap()); + + assert!(content_range.matches_requested_range(range)); + } + + #[test] + fn suffix_not_at_boundary_does_not_match() { + let range = HttpRange::Suffix(5); + let content_range = HttpContentRange::Bound(Bound::new(0..=4, Some(20)).unwrap()); + + assert!(!content_range.matches_requested_range(range)); + } + + #[test] + fn suffix_length_mismatch_does_not_match() { + let range = HttpRange::Suffix(3); + let content_range = HttpContentRange::Bound(Bound::new(10..=19, Some(20)).unwrap()); + + assert!(!content_range.matches_requested_range(range)); + } } } @@ -428,15 +468,29 @@ mod file_range { } #[test] - fn range_end_at_size() { + fn range_end_at_size_is_clamped() { let range = HttpRange::Range(OrderedRange::new(0..=10).unwrap()); + let result = file_range(size(10), Some(range)).unwrap(); + assert_eq!(result.range(), &(0..=9)); + } + + #[test] + fn range_beyond_size_is_clamped() { + let range = HttpRange::Range(OrderedRange::new(0..=50).unwrap()); + let result = file_range(size(10), Some(range)).unwrap(); + assert_eq!(result.range(), &(0..=9)); + } + + #[test] + fn range_start_at_size_is_unsatisfiable() { + let range = HttpRange::Range(OrderedRange::new(10..=20).unwrap()); let result = file_range(size(10), Some(range)); assert!(result.is_err()); } #[test] - fn range_beyond_size() { - let range = HttpRange::Range(OrderedRange::new(0..=50).unwrap()); + fn range_start_beyond_size_is_unsatisfiable() { + let range = HttpRange::Range(OrderedRange::new(50..=100).unwrap()); let result = file_range(size(10), Some(range)); assert!(result.is_err()); } @@ -466,9 +520,9 @@ mod file_range { } #[test] - fn suffix_exceeds_size() { - let result = file_range(size(10), Some(HttpRange::Suffix(11))); - assert!(result.is_err()); + fn suffix_exceeds_size_is_clamped() { + let result = file_range(size(10), Some(HttpRange::Suffix(11))).unwrap(); + assert_eq!(result.range(), &(0..=9)); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 8a84a5f..7c3b081 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,8 +55,10 @@ pub fn file_range( let range = match http_range { HttpRange::StartingPoint(start) if start < size => start..=size - 1, - HttpRange::Range(range) if range.end() < size => range.start()..=range.end(), - HttpRange::Suffix(suffix) if suffix > 0 && suffix <= size => size - suffix..=size - 1, + HttpRange::Range(range) if range.start() < size => { + range.start()..=range.end().min(size - 1) + } + HttpRange::Suffix(suffix) if suffix > 0 => size.saturating_sub(suffix)..=size - 1, _ => { let content_range = HttpContentRange::Unsatisfiable(Unsatisfiable::new(size)); return Err(UnsatisfiableRange(content_range));