1#![warn(clippy::pedantic)]
2#![allow(unknown_lints)]
4#![allow(clippy::uninlined_format_args, clippy::single_range_in_vec_init)]
5use core::fmt::{Debug, Display, Formatter};
6use core::ops::RangeInclusive;
7use std::num::{IntErrorKind, NonZeroUsize};
8
9const UNIT_SEP: &str = "bytes=";
10#[allow(clippy::missing_panics_doc)]
135pub fn parse_range_header(
136 range_header_value: &str,
137) -> Result<ParsedRanges, RangeUnsatisfiableError> {
138 const NZ_100: NonZeroUsize = match NonZeroUsize::new(100) {
140 None => {
141 unreachable!();
142 }
143 Some(nz) => nz,
144 };
145 parse_range_header_with_max_ranges(range_header_value, NZ_100)
148}
149
150pub fn parse_range_header_with_max_ranges(
155 range_header_value: &str,
156 max_ranges: NonZeroUsize,
157) -> Result<ParsedRanges, RangeUnsatisfiableError> {
158 const COMMA: char = ',';
159 let indicated_range = match range_header_value.get(..UNIT_SEP.len()) {
160 Some(r) => {
161 if !r.eq_ignore_ascii_case(UNIT_SEP) {
162 return Err(RangeUnsatisfiableError::Invalid(
163 RangeInvalid::DoesNotStartWithToken,
164 ));
165 }
166 match range_header_value.get(UNIT_SEP.len()..) {
167 Some(r) => r,
168 None => {
169 return Err(RangeUnsatisfiableError::Invalid(
171 RangeInvalid::DoesNotStartWithToken,
172 ));
173 }
174 }
175 }
176 None => {
177 return Err(RangeUnsatisfiableError::Invalid(
178 RangeInvalid::DoesNotStartWithToken,
179 ));
180 }
181 };
182 let mut last_err = None;
183 let mut num_empty = 0;
184 let mut ranges = Vec::new();
185 for range in indicated_range.split(COMMA) {
186 if num_empty >= MAX_EMPTY {
187 return Err(RangeUnsatisfiableError::Rejected(
188 RangeRejected::TooManyUnparseable,
189 ));
190 }
191 let val = if let Some(trimmed) = trim(range) {
192 if trimmed.is_empty() {
193 num_empty += 1;
194 continue;
195 }
196 match parse_inner(trimmed) {
197 Ok(parsed) => parsed,
198 Err(e) => {
199 last_err = Some(e);
200 num_empty += 1;
201 continue;
202 }
203 }
204 } else {
205 last_err = Some(RangeUnsatisfiableError::Invalid(
206 RangeInvalid::IllegalWhitespace,
207 ));
208 num_empty += 1;
209 continue;
210 };
211 ranges.push(val);
212 if ranges.len() > max_ranges.get() {
213 return Err(RangeUnsatisfiableError::Rejected(RangeRejected::TooMany));
214 }
215 }
216 if let Some(last_err) = last_err {
217 return Err(last_err);
218 }
219 if indicated_range.ends_with(space_or_tab) {
220 if !indicated_range
222 .trim_end_matches(space_or_tab)
223 .ends_with(',')
224 {
225 return Err(RangeUnsatisfiableError::Invalid(
226 RangeInvalid::IllegalWhitespace,
227 ));
228 }
229 }
230 if ranges.is_empty() {
231 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::NoNonempty))
232 } else {
233 Ok(ParsedRanges::new(ranges))
234 }
235}
236
237const MAX_EMPTY: usize = 15;
238
239fn trim(s: &str) -> Option<&str> {
240 let trimmed = s.trim_matches(space_or_tab);
241 if trimmed.starts_with(char::is_whitespace) || trimmed.ends_with(char::is_whitespace) {
242 None
243 } else {
244 Some(trimmed)
245 }
246}
247
248fn space_or_tab(ch: char) -> bool {
249 ch == ' ' || ch == '\t'
250}
251
252#[derive(Debug, Copy, Clone, Eq, PartialEq)]
253pub enum SyntacticallyCorrectRange {
254 FromStart(u64),
255 FromLast(u64),
256 Indexed(u64, u64),
258}
259
260#[inline]
261fn parse_inner(range: &str) -> Result<SyntacticallyCorrectRange, RangeUnsatisfiableError> {
262 if let Some((start, end)) = range.split_once('-') {
263 if start.is_empty() {
264 let end = strict_parse_u64(end)?;
265 return Ok(SyntacticallyCorrectRange::FromLast(end));
266 }
267 let start = strict_parse_u64(start)?;
268 if end.is_empty() {
269 return Ok(SyntacticallyCorrectRange::FromStart(start));
270 }
271 let end = strict_parse_u64(end)?;
272 if start > end {
273 return Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Reversed));
274 }
275 return Ok(SyntacticallyCorrectRange::Indexed(start, end));
276 }
277 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable))
278}
279
280fn strict_parse_u64(s: &str) -> Result<u64, RangeUnsatisfiableError> {
281 const MAX_DIGITS: usize = 30;
282 if s.starts_with('+') {
283 return Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable));
284 }
285 if s.len() > MAX_DIGITS {
286 return Err(RangeUnsatisfiableError::Rejected(
288 RangeRejected::TooManyDigits,
289 ));
290 }
291 match s.parse::<u64>() {
292 Ok(o) => Ok(o),
293 Err(e)
294 if e.kind() == &IntErrorKind::PosOverflow
295 && s.chars().all(|ch| ch.is_ascii_digit()) =>
296 {
297 Ok(u64::MAX)
302 }
303 Err(_) => Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
304 }
305}
306
307#[derive(Debug, Clone, Eq, PartialEq)]
308pub struct ParsedRanges {
309 ranges: Vec<SyntacticallyCorrectRange>,
310}
311
312impl ParsedRanges {
313 fn new(ranges: Vec<SyntacticallyCorrectRange>) -> Self {
314 ParsedRanges { ranges }
315 }
316
317 #[must_use]
318 #[allow(clippy::len_without_is_empty)]
319 pub fn len(&self) -> usize {
320 self.ranges.len()
321 }
322
323 #[must_use]
324 pub fn into_ranges(self) -> Vec<SyntacticallyCorrectRange> {
325 self.ranges
326 }
327
328 pub fn validate(
334 &self,
335 file_size_bytes: u64,
336 ) -> Result<Vec<RangeInclusive<u64>>, RangeUnsatisfiableError> {
337 let len = self.ranges.len();
338 if file_size_bytes == 0 {
339 return match self.zero_len_validate() {
340 Some(e) => Err(e),
341 None => Ok(vec![]),
342 };
343 }
344 let mut validated = Vec::with_capacity(len);
345 let mut bad_range = false;
346 for parsed in &self.ranges {
347 let (start, end) = match *parsed {
348 SyntacticallyCorrectRange::FromStart(i) => {
349 if i > file_size_bytes.saturating_sub(1) {
350 bad_range = true;
351 continue;
352 }
353 let end = file_size_bytes.saturating_sub(1);
354 (i, end)
355 }
356 SyntacticallyCorrectRange::FromLast(i) => {
357 if i == 0 {
358 bad_range = true;
359 continue;
360 }
361 (
363 file_size_bytes.saturating_sub(i),
364 file_size_bytes.saturating_sub(1),
365 )
366 }
367 SyntacticallyCorrectRange::Indexed(start, end) => {
368 let end = core::cmp::min(end, file_size_bytes.saturating_sub(1));
369 if start > end {
371 bad_range = true;
372 continue;
373 }
374 (start, end)
375 }
376 };
377 let valid = RangeInclusive::new(start, end);
378 validated.push(valid);
379 }
380
381 if validated.is_empty() && bad_range {
382 return Err(RangeUnsatisfiableError::Unsatisfiable);
383 }
384 match validate_ranges(validated.as_slice()) {
385 RangeValidationResult::Valid => Ok(validated),
386 RangeValidationResult::Overlapping => Err(RangeUnsatisfiableError::Rejected(
387 RangeRejected::TooManyOverlapping,
388 )),
389 }
390 }
391
392 #[inline(never)]
393 fn zero_len_validate(&self) -> Option<RangeUnsatisfiableError> {
394 let mut last_error = None;
395 let mut any_passing = false;
396 for parsed in &self.ranges {
397 match *parsed {
398 SyntacticallyCorrectRange::FromStart(_)
399 | SyntacticallyCorrectRange::Indexed(_, _) => {
400 last_error = Some(RangeUnsatisfiableError::Unsatisfiable);
401 }
402 SyntacticallyCorrectRange::FromLast(i) => {
403 if i == 0 {
404 last_error = Some(RangeUnsatisfiableError::Unsatisfiable);
412 continue;
413 }
414 any_passing = true;
415 }
416 }
417 }
418 match (any_passing, last_error) {
419 (false, Some(e)) => Some(e),
420 _ => None,
421 }
422 }
423}
424
425#[derive(Debug, Copy, Clone, Eq, PartialEq)]
428#[non_exhaustive]
429pub enum RangeUnsatisfiableError {
430 Invalid(RangeInvalid),
431 Rejected(RangeRejected),
432 Unsatisfiable,
434}
435
436#[derive(Debug, Copy, Clone, Eq, PartialEq)]
439#[non_exhaustive]
440pub enum RangeInvalid {
441 Reversed,
442 IllegalWhitespace,
443 DoesNotStartWithToken,
447 NoNonempty,
448 Unparseable,
449}
450
451#[derive(Debug, Copy, Clone, Eq, PartialEq)]
453#[non_exhaustive]
454pub enum RangeRejected {
455 TooManyOverlapping,
458 TooManyUnparseable,
459 TooManyDigits,
460 TooMany,
462}
463
464impl Display for RangeUnsatisfiableError {
465 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
466 match self {
467 RangeUnsatisfiableError::Invalid(r) => match r {
468 RangeInvalid::Reversed => f.write_str("range invalid: reversed"),
469 RangeInvalid::IllegalWhitespace => f.write_str("range invalid: illegal whitespace"),
470 RangeInvalid::DoesNotStartWithToken => f.write_fmt(format_args!(
471 "range invalid: range does not start with token '{UNIT_SEP}'"
472 )),
473 RangeInvalid::NoNonempty => f.write_str("range invalid: no nonempty ranges remain"),
474 RangeInvalid::Unparseable => {
475 f.write_str("range invalid: provided input did not make sense")
476 }
477 },
478 RangeUnsatisfiableError::Rejected(r) => match r {
479 RangeRejected::TooManyOverlapping => {
480 f.write_str("range rejected: too many overlapping ranges")
481 }
482 RangeRejected::TooManyUnparseable => {
483 f.write_str("range rejected: too many unparseable ranges")
484 }
485 RangeRejected::TooMany => f.write_str("range rejected: too many ranges"),
486 RangeRejected::TooManyDigits => {
487 f.write_str("range rejected: too many characters in the range digit-position")
488 }
489 },
490 RangeUnsatisfiableError::Unsatisfiable => f.write_str("range unsatisfiable"),
491 }
492 }
493}
494
495impl std::error::Error for RangeUnsatisfiableError {}
496
497enum RangeValidationResult {
498 Valid,
499 Overlapping,
500}
501
502fn validate_ranges(ranges: &[RangeInclusive<u64>]) -> RangeValidationResult {
503 let mut sorted = true;
504 let mut prev_start = 0;
505 for range in ranges {
506 let start = *range.start();
507 sorted &= prev_start <= start;
508 prev_start = start;
509 }
510 if ranges.len() == 1 {
511 return RangeValidationResult::Valid;
512 }
513 let bounds = ranges.iter().map(|r| (*r.start(), *r.end()));
514 let too_many = if sorted {
515 has_multiple_overlaps(bounds)
516 } else {
517 let mut by_start: Vec<_> = bounds.collect();
518 by_start.sort_unstable_by_key(|&(start, _)| start);
519 has_multiple_overlaps(by_start.into_iter())
520 };
521 if too_many {
522 RangeValidationResult::Overlapping
523 } else {
524 RangeValidationResult::Valid
525 }
526}
527
528fn has_multiple_overlaps(mut by_start: impl Iterator<Item = (u64, u64)>) -> bool {
529 #[allow(clippy::manual_let_else)]
531 let mut max_end = match by_start.next() {
532 Some((_, end)) => end,
533 None => return false,
534 };
535 let mut seen_overlap = false;
536 for (start, end) in by_start {
537 if start <= max_end {
538 if seen_overlap {
539 return true;
540 }
541 seen_overlap = true;
542 }
543 max_end = max_end.max(end);
544 }
545 false
546}
547
548#[cfg(test)]
549mod tests {
550 use crate::{
551 parse_range_header, parse_range_header_with_max_ranges, ParsedRanges, RangeInvalid,
552 RangeRejected, RangeUnsatisfiableError, SyntacticallyCorrectRange,
553 };
554 use core::ops::RangeInclusive;
555 use rand::seq::SliceRandom;
556 use std::fmt::Write;
557 use std::num::NonZeroUsize;
558
559 const TEST_FILE_LENGTH: u64 = 10_000;
560 #[test]
562 fn rfc_7233_standard_test1() {
563 let input = "bytes=0-499";
564 let expect = SyntacticallyCorrectRange::Indexed(0, 499);
565 let actual = parse_range_header(input).unwrap();
566 assert_eq!(single_range(expect), actual);
567 let expect = RangeInclusive::new(0, 499);
568 let actual = actual.validate(TEST_FILE_LENGTH).unwrap()[0].clone();
569 assert_eq!(expect, actual);
570 }
571
572 #[test]
573 fn rfc_7233_standard_test2() {
574 let input = "bytes=500-999";
575 let expect = SyntacticallyCorrectRange::Indexed(500, 999);
576 let actual = parse_range_header(input).unwrap();
577 assert_eq!(single_range(expect), actual);
578 let expect = RangeInclusive::new(500, 999);
579 let actual = actual.validate(TEST_FILE_LENGTH).unwrap()[0].clone();
580 assert_eq!(expect, actual);
581 }
582
583 #[test]
585 fn rfc_7233_suffixed_test() {
586 let input = "bytes=-500";
587 let expect = SyntacticallyCorrectRange::FromLast(500);
588 let actual = parse_range_header(input).unwrap();
589 assert_eq!(single_range(expect), actual);
590 let expect = RangeInclusive::new(9500, 9999);
591 let actual = actual.validate(10_000).unwrap()[0].clone();
592 assert_eq!(expect, actual);
593 }
594
595 #[test]
597 fn rfc_7233_open_range_test() {
598 let input = "bytes=9500-";
599 let expect = SyntacticallyCorrectRange::FromStart(9500);
600 let actual = parse_range_header(input).unwrap();
601 assert_eq!(single_range(expect), actual);
602 let expect = RangeInclusive::new(9500, 9999);
603 let actual = actual.validate(10_000).unwrap()[0].clone();
604 assert_eq!(expect, actual);
605 }
606
607 #[test]
609 fn rfc_7233_first_and_last() {
610 let input = "bytes=0-0, -1";
611 let expect = vec![
612 SyntacticallyCorrectRange::Indexed(0, 0),
613 SyntacticallyCorrectRange::FromLast(1),
614 ];
615 let actual = parse_range_header(input).unwrap();
616 assert_eq!(expect, actual.ranges);
617 let expect = vec![0..=0, 9999..=9999];
618 let actual = actual.validate(10_000).unwrap();
619 assert_eq!(expect, actual);
620 }
621
622 #[test]
623 fn parse_standard_range() {
624 let input = "bytes=0-1023";
625 let expect = SyntacticallyCorrectRange::Indexed(0, 1023);
626 let actual = parse_range_header(input).unwrap();
627 assert_eq!(single_range(expect), actual);
628 let expect = RangeInclusive::new(0, 1023);
629 let actual = actual.validate(TEST_FILE_LENGTH).unwrap()[0].clone();
630 assert_eq!(expect, actual);
631 }
632
633 #[test]
634 fn parse_open_ended_range() {
635 let input = "bytes=0-";
636 let expect = SyntacticallyCorrectRange::FromStart(0);
637 let actual = parse_range_header(input).unwrap();
638 assert_eq!(single_range(expect), actual);
639 let expect = RangeInclusive::new(0, TEST_FILE_LENGTH - 1);
640 let actual = actual.validate(TEST_FILE_LENGTH).unwrap()[0].clone();
641 assert_eq!(expect, actual);
642 }
643
644 #[test]
645 fn parse_suffix_range_edge() {
646 let input = &format!("bytes=-{}", TEST_FILE_LENGTH);
647 let expect = SyntacticallyCorrectRange::FromLast(TEST_FILE_LENGTH);
648 let actual = parse_range_header(input).unwrap();
649 assert_eq!(single_range(expect), actual);
650 let expect = RangeInclusive::new(0, TEST_FILE_LENGTH - 1);
651 let actual = actual.validate(TEST_FILE_LENGTH).unwrap()[0].clone();
652 assert_eq!(expect, actual);
653 }
654
655 #[test]
656 fn parse_empty_as_invalid() {
657 let input = "";
658 let parsed = parse_range_header(input);
659 assert_eq!(
660 parsed,
661 Err(RangeUnsatisfiableError::Invalid(
662 RangeInvalid::DoesNotStartWithToken
663 ))
664 );
665 }
666
667 #[test]
668 fn parse_empty_range_as_invalid() {
669 let input = "bytes=";
670 let parsed = parse_range_header(input);
671 assert_eq!(
672 parsed,
673 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::NoNonempty))
674 );
675 }
676
677 #[test]
678 fn parse_range_starting_with_whitespace_is_valid() {
679 let input = "bytes= 0-15";
680 let parsed = parse_range_header(input);
681 assert_eq!(Ok(vec![0..=15]), parsed.unwrap().validate(TEST_FILE_LENGTH));
682 }
683
684 #[test]
685 fn parse_range_token_starting_with_whitespace_as_invalid() {
686 let input = " bytes=0-15";
687 let parsed = parse_range_header(input);
688 assert_eq!(
689 parsed,
690 Err(RangeUnsatisfiableError::Invalid(
691 RangeInvalid::DoesNotStartWithToken
692 ))
693 );
694 }
695
696 #[test]
697 fn parse_range_strict_parse_numerical() {
698 let input = "bytes=+0-15";
699 let parsed = parse_range_header(input);
700 assert_eq!(
701 parsed,
702 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable))
703 );
704 }
705
706 #[test]
707 fn parse_bad_unit_as_invalid() {
708 let input = "abcde=0-10";
709 let parsed = parse_range_header(input);
710 assert_eq!(
711 parsed,
712 Err(RangeUnsatisfiableError::Invalid(
713 RangeInvalid::DoesNotStartWithToken
714 ))
715 );
716 }
717
718 #[test]
719 fn parse_missing_equals_as_malformed() {
720 let input = "bytes0-10";
721 let parsed = parse_range_header(input);
722 assert_eq!(
723 parsed,
724 Err(RangeUnsatisfiableError::Invalid(
725 RangeInvalid::DoesNotStartWithToken
726 ))
727 );
728 }
729
730 #[test]
731 fn parse_negative_bad_characters_in_range_as_malformed() {
732 let input = "bytes=1-10a";
733 let parsed = parse_range_header(input);
734 assert_eq!(
735 parsed,
736 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable))
737 );
738 }
739
740 #[test]
741 fn parse_negative_numbers_as_malformed() {
742 let input = "bytes=-1-10";
743 let parsed = parse_range_header(input);
744 assert_eq!(
746 parsed,
747 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable))
748 );
749 }
750
751 #[test]
752 fn parse_bad_characters_in_start_of_range() {
753 let input = "bytes=a1-10";
754 let parsed = parse_range_header(input);
755 assert_eq!(
757 parsed,
758 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable))
759 );
760 }
761
762 #[test]
763 fn parse_out_of_bounds_overrun_as_content_length() {
764 let input = &format!("bytes=0-{}", TEST_FILE_LENGTH);
765 let expect = vec![RangeInclusive::new(0, TEST_FILE_LENGTH - 1)];
766 let actual = parse_range_header(input)
767 .unwrap()
768 .validate(TEST_FILE_LENGTH)
769 .unwrap();
770 assert_eq!(expect, actual);
771 }
772
773 #[test]
774 fn parse_suffix_longer_than_file_as_whole_file() {
775 let input = &format!("bytes=-{}", TEST_FILE_LENGTH + 1);
776 let parsed = parse_range_header(input)
777 .unwrap()
778 .validate(TEST_FILE_LENGTH)
779 .unwrap();
780 assert_eq!(parsed, vec![0..=TEST_FILE_LENGTH - 1]);
781 }
782
783 #[test]
784 fn parse_suffix_of_empty_file_as_satisfiable_returns_no_ranges() {
785 let parsed = parse_range_header("bytes=-1").unwrap().validate(0);
786 assert_eq!(parsed, Ok(vec![]));
787 }
788
789 #[test]
790 fn parse_zero_length_suffix_as_unsatisfiable() {
791 let input = "bytes=-0";
792 let parsed = parse_range_header(input).unwrap();
793 assert_eq!(
794 parsed.validate(TEST_FILE_LENGTH),
795 Err(RangeUnsatisfiableError::Unsatisfiable)
796 );
797 }
798
799 #[test]
800 fn parse_zero_length_suffix_retains_satisfiable_ranges() {
801 let input = "bytes=15-20,-0";
802 let parsed = parse_range_header(input).unwrap();
803 assert_eq!(Ok(vec![15..=20]), parsed.validate(TEST_FILE_LENGTH));
804 }
805
806 #[test]
807 fn retains_satisfiable_ranges() {
808 let input = "bytes=10-20,0-0";
809 let parsed = parse_range_header(input).unwrap();
810 assert_eq!(Ok(vec![0..=0]), parsed.validate(10));
811 let parsed = parse_range_header(input).unwrap();
812 assert_eq!(Ok(vec![10..=19, 0..=0]), parsed.validate(20));
813 let parsed = parse_range_header(input).unwrap();
814 assert_eq!(Ok(vec![10..=20, 0..=0]), parsed.validate(21));
815 }
816
817 #[test]
818 fn rejects_invalid_range_even_when_oob() {
819 let input = "bytes=20-10,0-0";
820 assert_eq!(
821 parse_range_header(input),
822 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Reversed)),
823 );
824 }
825
826 #[test]
827 fn retains_satisfiable_ranges_reordered() {
828 let input = "bytes=0-0,10-20";
829 let parsed = parse_range_header(input).unwrap();
830 assert_eq!(Ok(vec![0..=0]), parsed.validate(10));
831 let parsed = parse_range_header(input).unwrap();
832 assert_eq!(Ok(vec![0..=0, 10..=19]), parsed.validate(20));
833 let parsed = parse_range_header(input).unwrap();
834 assert_eq!(Ok(vec![0..=0, 10..=20]), parsed.validate(21));
835 }
836
837 #[test]
838 fn parse_single_reversed_as_invalid() {
839 let input = "bytes=15-0";
840 assert_eq!(
841 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Reversed)),
842 parse_range_header(input),
843 );
844 }
845
846 #[test]
847 fn parse_zero_range_as_invalid() {
848 let input = "bytes=15-";
849 let parsed = parse_range_header(input).unwrap();
850 assert_eq!(
851 parsed.validate(0),
852 Err(RangeUnsatisfiableError::Unsatisfiable)
853 );
854 }
855
856 #[test]
857 fn empty_file_quirks() {
858 let parsed = parse_range_header("bytes=-0").unwrap();
859 assert_eq!(
860 parsed.validate(0),
861 Err(RangeUnsatisfiableError::Unsatisfiable)
862 );
863 let parsed = parse_range_header("bytes=-0,-1").unwrap();
864 assert_eq!(parsed.validate(0), Ok(vec![]));
865 }
866
867 #[test]
868 fn parse_multi_range() {
869 let input = "bytes=0-1023, 2015-3000, 4000-4500, 8000-9999";
870 let expected_ranges = vec![
871 SyntacticallyCorrectRange::Indexed(0, 1023),
872 SyntacticallyCorrectRange::Indexed(2015, 3000),
873 SyntacticallyCorrectRange::Indexed(4000, 4500),
874 SyntacticallyCorrectRange::Indexed(8000, 9999),
875 ];
876 let parsed = parse_range_header(input).unwrap();
877 assert_eq!(expected_ranges, parsed.ranges);
878 let validated = parsed.validate(TEST_FILE_LENGTH).unwrap();
879 assert_eq!(
880 vec![0..=1023, 2015..=3000, 4000..=4500, 8000..=9999],
881 validated
882 );
883 }
884
885 #[test]
886 fn parse_multi_range_with_open() {
887 let input = "bytes=0-1023, 1024-";
888 let expected_ranges = vec![
889 SyntacticallyCorrectRange::Indexed(0, 1023),
890 SyntacticallyCorrectRange::FromStart(1024),
891 ];
892 let parsed = parse_range_header(input).unwrap();
893 assert_eq!(expected_ranges, parsed.ranges);
894 let validated = parsed.validate(TEST_FILE_LENGTH).unwrap();
895 assert_eq!(vec![0..=1023, 1024..=9999], validated);
896 }
897
898 #[test]
899 fn parse_multi_range_with_suffix() {
900 let input = "bytes=0-1023, -1000";
901 let expected_ranges = vec![
902 SyntacticallyCorrectRange::Indexed(0, 1023),
903 SyntacticallyCorrectRange::FromLast(1000),
904 ];
905 let parsed = parse_range_header(input).unwrap();
906 assert_eq!(expected_ranges, parsed.ranges);
907 let validated = parsed.validate(TEST_FILE_LENGTH).unwrap();
908 assert_eq!(vec![0..=1023, 9000..=9999], validated);
909 }
910
911 #[test]
912 fn parse_overlapping_multi_range_as_unsatisfiable_standard() {
913 let prepared = parse_range_header("bytes=500-700,601-999").unwrap();
914 assert_eq!(
915 Ok(vec![500..=700, 601..=999]),
916 prepared.validate(TEST_FILE_LENGTH)
917 );
918 let prepared = parse_range_header("bytes=0-1023, 500-800").unwrap();
919 assert_eq!(
920 Ok(vec![0..=1023, 500..=800]),
921 prepared.validate(TEST_FILE_LENGTH)
922 );
923 let prepared = parse_range_header("bytes=0-0, 0-15").unwrap();
924 assert_eq!(Ok(vec![0..=0, 0..=15]), prepared.validate(TEST_FILE_LENGTH));
925 let prepared = parse_range_header("bytes=0-20, 20-35").unwrap();
926 assert_eq!(
927 Ok(vec![0..=20, 20..=35]),
928 prepared.validate(TEST_FILE_LENGTH)
929 );
930 let input = "bytes=0-1023, 500-800, 450-650";
931 assert_validation_err(
932 input,
933 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
934 );
935 let input = "bytes=0-1023, 500-800, 1000-1001";
936 assert_validation_err(
937 input,
938 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
939 );
940 let input = "bytes=0-1023, 500-800, 1000-";
941 assert_validation_err(
942 input,
943 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
944 );
945 let input = "bytes=0-, 0-0, 0-15";
946 assert_validation_err(
947 input,
948 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
949 );
950 let input = "bytes=0-20, 20-35, 35-";
951 assert_validation_err(
952 input,
953 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
954 );
955 }
956
957 #[test]
958 fn parse_overlapping_multi_range_as_unsatisfiable_open() {
959 let input = "bytes=0-, 1-, 5000-6000";
960 assert_validation_err(
961 input,
962 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
963 );
964 let parsed = parse_range_header("bytes=0-, 5000-6000").unwrap();
965 assert_eq!(
966 Ok(vec![0..=TEST_FILE_LENGTH - 1, 5000..=6000]),
967 parsed.validate(TEST_FILE_LENGTH)
968 );
969 }
970
971 #[test]
972 fn parse_overlapping_multi_range_as_unsatisfiable_suffixed() {
973 let parsed = parse_range_header("bytes=8000-9000, -1001").unwrap();
974 assert_eq!(
975 Ok(vec![8000..=9000, 8999..=TEST_FILE_LENGTH - 1]),
976 parsed.validate(TEST_FILE_LENGTH)
977 );
978 let parsed = parse_range_header("bytes=8000-9000, -1000").unwrap();
979 assert_eq!(
980 Ok(vec![8000..=9000, 9000..=TEST_FILE_LENGTH - 1]),
981 parsed.validate(TEST_FILE_LENGTH)
982 );
983 let input = "bytes=8000-9000, -1001, -500";
984 assert_validation_err(
985 input,
986 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
987 );
988 let input = "bytes=8000-9000, -1000, -400";
989 assert_validation_err(
990 input,
991 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
992 );
993 }
994
995 #[test]
996 fn parse_overlapping_multi_range_as_unsatisfiable_suffixed_open() {
997 let parsed = parse_range_header("bytes=0-, -1").unwrap();
998 assert_eq!(
999 Ok(vec![
1000 0..=TEST_FILE_LENGTH - 1,
1001 TEST_FILE_LENGTH - 1..=TEST_FILE_LENGTH - 1
1002 ]),
1003 parsed.validate(TEST_FILE_LENGTH)
1004 );
1005 let input = "bytes=0-, 5-6, -1";
1006 assert_validation_err(
1007 input,
1008 RangeUnsatisfiableError::Rejected(RangeRejected::TooManyOverlapping),
1009 );
1010 }
1011
1012 #[test]
1013 fn parse_start_boundaries() {
1014 let parsed = parse_range_header("bytes=9999-").unwrap();
1015 assert_eq!(Ok(vec![9999..=9999]), parsed.validate(TEST_FILE_LENGTH));
1016 let parsed = parse_range_header("bytes=10000-").unwrap();
1017 assert_eq!(
1018 Err(RangeUnsatisfiableError::Unsatisfiable),
1019 parsed.validate(TEST_FILE_LENGTH)
1020 );
1021 }
1022
1023 #[test]
1024 fn parse_multi_range_with_a_reversed_as_invalid() {
1025 let input = "bytes=0-15, 30-20";
1026 assert_eq!(
1027 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Reversed)),
1028 parse_range_header(input)
1029 );
1030 }
1031
1032 #[track_caller]
1033 fn assert_validation_err(input: &str, err: RangeUnsatisfiableError) {
1034 let parsed = parse_range_header(input)
1035 .unwrap()
1036 .validate(TEST_FILE_LENGTH);
1037 assert_eq!(Err(err), parsed);
1038 }
1039
1040 #[test]
1041 fn parse_multi_range_rejects_invalid() {
1042 let input = "bytes=0-15, 25, 9, ";
1043 let parsed = parse_range_header(input);
1044 assert!(parsed.is_err());
1045 }
1046
1047 #[quickcheck_macros::quickcheck]
1048 #[allow(clippy::needless_pass_by_value)]
1049 fn never_panics(input: String) -> quickcheck::TestResult {
1050 if let Ok(passed_first_pass) = parse_range_header(&input) {
1051 let _v = passed_first_pass.validate(u64::MAX);
1052 quickcheck::TestResult::passed()
1053 } else {
1054 quickcheck::TestResult::passed()
1055 }
1056 }
1057
1058 #[quickcheck_macros::quickcheck]
1059 #[allow(clippy::needless_pass_by_value)]
1060 fn never_panics_empty_validate(input: String) -> quickcheck::TestResult {
1061 if let Ok(passed_first_pass) = parse_range_header(&input) {
1062 let _v = passed_first_pass.validate(0);
1063 quickcheck::TestResult::passed()
1064 } else {
1065 quickcheck::TestResult::passed()
1066 }
1067 }
1068
1069 #[test]
1070 #[allow(clippy::cast_possible_truncation)]
1071 fn zero_length_file_does_not_return_an_unusable_range_on_suffixed() {
1072 const BYTES: &[u8; 0] = &[];
1073 let parsed = parse_range_header("bytes=-1").unwrap();
1074 let val = parsed.validate(0).unwrap();
1075 for range in val {
1076 for val in range {
1077 let value = BYTES[val as usize];
1078 assert_eq!(15, value);
1080 }
1081 }
1082 }
1083
1084 #[test]
1085 fn zero_length_file_does_not_accept_any_staring_range() {
1086 let parsed = parse_range_header("bytes=0-").unwrap();
1087 assert_eq!(
1088 Err(RangeUnsatisfiableError::Unsatisfiable),
1089 parsed.validate(0)
1090 );
1091 let parsed = parse_range_header("bytes=0-0").unwrap();
1092 assert_eq!(
1093 Err(RangeUnsatisfiableError::Unsatisfiable),
1094 parsed.validate(0)
1095 );
1096 let parsed = parse_range_header("bytes=0-100").unwrap();
1097 assert_eq!(
1098 Err(RangeUnsatisfiableError::Unsatisfiable),
1099 parsed.validate(0)
1100 );
1101 }
1102
1103 #[test]
1104 fn zero_length_file_will_not_give_an_error_if_any_range_is_satisfiable() {
1105 let parsed = parse_range_header("bytes=0-0,-1").unwrap();
1107 assert_eq!(Ok(vec![]), parsed.validate(0));
1108 let parsed = parse_range_header("bytes=1-,-1").unwrap();
1109 assert_eq!(Ok(vec![]), parsed.validate(0));
1110 }
1111
1112 #[test]
1113 fn zero_length_file_will_not_accept_reversed_ranges() {
1114 assert_eq!(
1115 parse_range_header("bytes=15-0,-1"),
1116 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Reversed)),
1117 );
1118 }
1119
1120 #[test]
1121 fn accepts_any_case() {
1122 let _parsed = parse_range_header("bytes=0-0").unwrap();
1123 let _parsed = parse_range_header("Bytes=0-0").unwrap();
1124 let _parsed = parse_range_header("ByTeS=0-0").unwrap();
1125 }
1126
1127 #[test]
1128 fn accepts_leading_zeroes() {
1129 let parsed = parse_range_header("bytes=1-2").unwrap();
1132 assert_eq!(Ok(vec![1..=2]), parsed.validate(TEST_FILE_LENGTH));
1133 let parsed = parse_range_header("bytes=01-2").unwrap();
1134 assert_eq!(Ok(vec![1..=2]), parsed.validate(TEST_FILE_LENGTH));
1135 let parsed = parse_range_header("bytes=001-2").unwrap();
1136 assert_eq!(Ok(vec![1..=2]), parsed.validate(TEST_FILE_LENGTH));
1137 let parsed = parse_range_header("bytes=00001-2").unwrap();
1138 assert_eq!(Ok(vec![1..=2]), parsed.validate(TEST_FILE_LENGTH));
1139 }
1140
1141 #[test]
1142 fn accepts_whitespace_around_separators() {
1143 assert_eq!(
1144 Err(RangeUnsatisfiableError::Invalid(
1145 RangeInvalid::IllegalWhitespace
1146 )),
1147 parse_range_header("bytes=1-2 ")
1148 );
1149 parse_range_header("bytes= , 0-1,3-4").unwrap();
1150 parse_range_header("bytes=0-1,3-4").unwrap();
1151 parse_range_header("bytes=0-1, 3-4").unwrap();
1152 parse_range_header("bytes=0-1, 3-4").unwrap();
1153 parse_range_header("bytes=0-1 , 3-4").unwrap();
1154 parse_range_header("bytes=0-1 ,3-4").unwrap();
1155 parse_range_header("bytes=0-1,\t3-4").unwrap();
1156 parse_range_header("bytes=0-1,\t\t3-4").unwrap();
1157 parse_range_header("bytes=0-1 ,\t3-4").unwrap();
1158 parse_range_header("bytes=0-1 \t,3-4").unwrap();
1159 assert_eq!(
1160 Err(RangeUnsatisfiableError::Invalid(
1161 RangeInvalid::IllegalWhitespace
1162 )),
1163 parse_range_header("bytes=0-1,\n3-4")
1164 );
1165 assert_eq!(
1166 Err(RangeUnsatisfiableError::Invalid(
1167 RangeInvalid::IllegalWhitespace
1168 )),
1169 parse_range_header("bytes=0-1,\n 3-4")
1170 );
1171 assert_eq!(
1172 Err(RangeUnsatisfiableError::Invalid(
1173 RangeInvalid::IllegalWhitespace
1174 )),
1175 parse_range_header("bytes=0-1\n, 3-4")
1176 );
1177 assert_eq!(
1178 Err(RangeUnsatisfiableError::Invalid(
1179 RangeInvalid::IllegalWhitespace
1180 )),
1181 parse_range_header("bytes=0-1,3-4\n")
1182 );
1183 }
1184
1185 #[test]
1186 fn rejects_long_number_even_if_its_not_a_number() {
1187 let expect = ParsedRanges {
1189 ranges: vec![SyntacticallyCorrectRange::Indexed(0, 15)],
1190 };
1191 assert_eq!(
1192 Ok(expect),
1193 parse_range_header("bytes=0-000000000000000000000000000015")
1194 );
1195 assert_eq!(
1196 Err(RangeUnsatisfiableError::Rejected(
1197 RangeRejected::TooManyDigits
1198 )),
1199 parse_range_header("bytes=0-0000000000000000000000000000015")
1200 );
1201 assert_eq!(
1202 Err(RangeUnsatisfiableError::Rejected(
1203 RangeRejected::TooManyDigits
1204 )),
1205 parse_range_header("bytes=0-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
1206 );
1207 }
1208
1209 #[test]
1210 fn accepts_max_u64_value() {
1211 let parsed = parse_range_header("bytes=0-18446744073709551615").unwrap();
1212 assert_eq!(
1213 Ok(vec![0..=TEST_FILE_LENGTH.saturating_sub(1)]),
1214 parsed.validate(TEST_FILE_LENGTH)
1215 );
1216 }
1217 #[test]
1218 fn does_not_accept_a_completely_empty_range() {
1219 assert_eq!(
1220 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::NoNonempty)),
1221 parse_range_header("bytes=")
1222 );
1223 }
1224
1225 #[test]
1226 fn accepts_a_few_empty_elements() {
1227 let parsed = parse_range_header("bytes=0-1,,3-4,").unwrap();
1228 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1229 let parsed = parse_range_header("bytes=,0-1,,3-4").unwrap();
1230 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1231 let parsed = parse_range_header("bytes=0-1,,3-4").unwrap();
1232 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1233 let parsed = parse_range_header("bytes=0-1 , , 3-4").unwrap();
1234 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1235 let parsed = parse_range_header("bytes=0-1 ,, , 3-4").unwrap();
1236 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1237 let parsed = parse_range_header("bytes=0-1 , , , 3-4").unwrap();
1238 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1239 let parsed = parse_range_header("bytes=0-1 ,, ,, 3-4").unwrap();
1240 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1241 let parsed = parse_range_header("bytes=0-1 ,,,,, 3-4").unwrap();
1242 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1243
1244 let parsed = parse_range_header("bytes=0-1,,,,,,,,,,,,,,,3-4").unwrap();
1245 assert_eq!(Ok(vec![0..=1, 3..=4]), parsed.validate(TEST_FILE_LENGTH));
1246 assert_eq!(
1247 Err(RangeUnsatisfiableError::Rejected(
1248 RangeRejected::TooManyUnparseable
1249 )),
1250 parse_range_header("bytes=0-1,,,,,,,,,,,,,,,,3-4")
1251 );
1252 }
1253
1254 #[test]
1255 fn regression_empty_err_on_only_comma() {
1256 let parsed = parse_range_header("bytes=,,");
1257 assert_eq!(
1258 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::NoNonempty)),
1259 parsed
1260 );
1261 let parsed = parse_range_header("bytes=, ,");
1262 assert_eq!(
1263 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::NoNonempty)),
1264 parsed
1265 );
1266 }
1267
1268 #[test]
1269 fn regression_disallowed_valid_white_spacing_allowed_invalid_white_spacing() {
1270 let _parsed = parse_range_header("bytes=0-1 ,").unwrap();
1271 let parsed = parse_range_header("bytes=0-1 ");
1273 assert_eq!(
1274 Err(RangeUnsatisfiableError::Invalid(
1275 RangeInvalid::IllegalWhitespace
1276 )),
1277 parsed
1278 );
1279 let _parsed = parse_range_header("bytes=0-1, ").unwrap();
1281 }
1282
1283 #[test]
1284 fn regression_u64_overflow_clamps_to_end_of_file() {
1285 let parsed = parse_range_header("bytes=0-18446744073709551615000000000").unwrap();
1286 assert_eq!(Ok(vec![0..=9999]), parsed.validate(TEST_FILE_LENGTH));
1287 let parsed = parse_range_header("bytes=-18446744073709551615000000000").unwrap();
1288 assert_eq!(Ok(vec![0..=9999]), parsed.validate(TEST_FILE_LENGTH));
1289 let parsed =
1290 parse_range_header("bytes=18446744073709551615000000000-18446744073709551615000000000")
1291 .unwrap();
1292 assert_eq!(
1293 Err(RangeUnsatisfiableError::Unsatisfiable),
1294 parsed.validate(TEST_FILE_LENGTH)
1295 );
1296 }
1297
1298 #[test]
1299 fn regression_u64_parsing_allows_garbage() {
1300 assert_eq!(
1301 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1302 parse_range_header("bytes=0-99999999999999999999abc")
1303 );
1304 assert_eq!(
1305 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1306 parse_range_header("bytes=0-99999999999999999999-5")
1307 );
1308 assert_eq!(
1309 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1310 parse_range_header("bytes=0-99999999999999999999 5")
1311 );
1312 assert_eq!(
1313 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1314 parse_range_header("bytes=-99999999999999999999abc")
1315 );
1316 assert_eq!(
1317 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1318 parse_range_header("bytes=99999999999999999999abc-,0-1")
1319 );
1320 assert_eq!(
1321 Err(RangeUnsatisfiableError::Invalid(RangeInvalid::Unparseable)),
1322 parse_range_header("bytes=0-18446744073709551615x")
1323 );
1324 }
1325
1326 #[test]
1327 fn regression_char_boundary_check() {
1328 assert_eq!(
1329 Err(RangeUnsatisfiableError::Invalid(
1330 RangeInvalid::DoesNotStartWithToken
1331 )),
1332 parse_range_header("bytesé=0-1")
1333 );
1334 }
1335
1336 #[test]
1337 fn spec_contradiction() {
1338 assert_eq!(
1345 Ok(vec![0..=999, 4500..=5499, 9000..=9999]),
1346 parse_range_header("bytes= 0-999, 4500-5499, -1000")
1347 .unwrap()
1348 .validate(TEST_FILE_LENGTH)
1349 );
1350 }
1351
1352 #[test]
1353 fn default_100_ranges() {
1354 let mut spec = "bytes=".to_string();
1355 let cases = 200;
1356 for i in (0..cases).step_by(2) {
1357 let start = i;
1358 let end = i + 1;
1359 spec.write_fmt(format_args!("{}-{},", start, end)).unwrap();
1360 }
1361 spec.pop();
1362 let parsed = parse_range_header(&spec).unwrap();
1363 parsed.validate(TEST_FILE_LENGTH).unwrap();
1364 let mut spec = "bytes=".to_string();
1365 let cases = 200;
1366 for i in (0..cases).rev().step_by(2) {
1367 let start = i;
1368 let end = i + 1;
1369 spec.write_fmt(format_args!("{}-{},", start, end)).unwrap();
1370 }
1371 spec.pop();
1372 let parsed = parse_range_header(&spec).unwrap();
1373 parsed.validate(TEST_FILE_LENGTH).unwrap();
1374 let mut spec = "bytes=".to_string();
1375 let cases = 201;
1376 for i in (0..cases).rev().step_by(2) {
1377 let start = i;
1378 let end = i + 1;
1379 spec.write_fmt(format_args!("{}-{},", start, end)).unwrap();
1380 }
1381 spec.pop();
1382
1383 assert_eq!(
1384 Err(RangeUnsatisfiableError::Rejected(RangeRejected::TooMany)),
1385 parse_range_header(&spec)
1386 );
1387 }
1388
1389 #[test]
1390 fn random_ranges_overlap_check() {
1391 let mut ranges = Vec::with_capacity(100);
1393 let cases = 200;
1394 for i in (0..cases).rev().step_by(2) {
1395 let start = i;
1396 let end = i + 1;
1397 ranges.push(format!("{}-{},", start, end));
1398 }
1399 let mut rng = rand::rng();
1400 ranges.shuffle(&mut rng);
1401 let mut spec = "bytes=".to_string();
1402 for val in ranges {
1403 spec.push_str(&val);
1404 }
1405 spec.pop();
1406 let parsed = parse_range_header(&spec).unwrap();
1407 parsed.validate(TEST_FILE_LENGTH).unwrap();
1408 }
1409
1410 #[test]
1411 fn configurable_ranges() {
1412 assert!(
1413 parse_range_header_with_max_ranges("bytes=1-2", NonZeroUsize::new(1).unwrap()).is_ok()
1414 );
1415 assert_eq!(
1416 Err(RangeUnsatisfiableError::Rejected(RangeRejected::TooMany)),
1417 parse_range_header_with_max_ranges("bytes=1-2,2-3", NonZeroUsize::new(1).unwrap())
1418 );
1419 }
1420
1421 fn single_range(syntactically_correct: SyntacticallyCorrectRange) -> ParsedRanges {
1422 ParsedRanges::new(vec![syntactically_correct])
1423 }
1424}