1use crate::{
24 class,
25 date::HttpDate,
26 error::{ErrorKind, Result, WriteError, err},
27 etag::EntityTag,
28 limits::Limits,
29 list,
30 num::parse_u64,
31 text::{skip_ows, trim_ows},
32 write::{CountSink, Sink, reserve, write_decimal_u64},
33};
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37pub enum RangeSpec {
38 From(u64),
40 FromTo(u64, u64),
42 Suffix(u64),
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
48pub struct ByteRange {
49 pub start: u64,
51 pub end: u64,
53}
54
55impl ByteRange {
56 #[must_use]
65 pub const fn len(&self) -> u64 {
66 if self.end < self.start {
67 0
68 } else {
69 (self.end - self.start).saturating_add(1)
70 }
71 }
72
73 #[must_use]
75 pub const fn is_empty(&self) -> bool {
76 self.end < self.start
77 }
78}
79
80#[non_exhaustive]
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83pub enum RangeOutcome {
84 Ranges(usize),
86 Unsatisfiable,
88 Ignore,
92 NotBytes,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
99pub struct RangeHeader<'a> {
100 unit: &'a [u8],
101 set: list::List<'a>,
102}
103
104impl<'a> RangeHeader<'a> {
105 #[must_use]
107 pub fn is_bytes(&self) -> bool {
108 self.unit.eq_ignore_ascii_case(b"bytes")
109 }
110
111 #[must_use]
113 pub const fn unit(&self) -> &'a [u8] {
114 self.unit
115 }
116
117 pub fn specs(&self) -> impl Iterator<Item = RangeSpec> + use<'a> {
119 let bytes = self.is_bytes();
120 self.set
121 .iter()
122 .filter(move |_| bytes)
123 .filter_map(|e| spec(e).ok())
124 }
125
126 #[must_use]
129 pub const fn len(&self) -> usize {
130 self.set.len()
131 }
132
133 #[must_use]
135 pub const fn is_empty(&self) -> bool {
136 self.set.is_empty()
137 }
138}
139
140fn spec(e: &[u8]) -> Result<RangeSpec> {
142 let Some(dash) = e.iter().position(|&b| b == b'-') else {
143 return err(ErrorKind::InvalidRange, e.len());
144 };
145 let (first, last) = (
146 e.get(..dash).unwrap_or(&[]),
147 e.get(dash + 1..).unwrap_or(&[]),
148 );
149 match (first.is_empty(), last.is_empty()) {
150 (true, true) => err(ErrorKind::InvalidRange, 0),
151 (true, false) => Ok(RangeSpec::Suffix(
152 parse_u64(last).map_err(|x| x.shifted(dash + 1))?,
153 )),
154 (false, true) => Ok(RangeSpec::From(parse_u64(first)?)),
155 (false, false) => {
156 let f = parse_u64(first)?;
157 let l = parse_u64(last).map_err(|x| x.shifted(dash + 1))?;
158 if f > l {
159 return err(ErrorKind::InvalidRange, 0);
160 }
161 Ok(RangeSpec::FromTo(f, l))
162 }
163 }
164}
165
166pub fn parse<'a>(value: &'a [u8], limits: &Limits) -> Result<RangeHeader<'a>> {
190 let lead = skip_ows(value);
191 let v = trim_ows(value);
192 let unit_len = v.iter().take_while(|&&b| class::is_tchar(b)).count();
193 if unit_len == 0 {
194 return err(ErrorKind::NotToken, lead);
195 }
196 if v.get(unit_len) != Some(&b'=') {
197 return err(ErrorKind::InvalidRange, lead + unit_len);
198 }
199 let unit = v.get(..unit_len).unwrap_or(&[]);
200 let set_at = unit_len + 1;
201 let set_text = v.get(set_at..).unwrap_or(&[]);
202 if set_text.first().is_some_and(|&b| crate::text::is_ows(b)) {
203 return err(ErrorKind::InvalidRange, lead + set_at);
204 }
205 let set = list::parse(set_text, limits).map_err(|e| e.shifted(lead + set_at))?;
206 if set.is_empty() {
207 return err(ErrorKind::InvalidRange, lead + set_at);
208 }
209 let bytes = unit.eq_ignore_ascii_case(b"bytes");
210 for e in set.iter() {
211 let at = lead + (e.as_ptr() as usize).saturating_sub(v.as_ptr() as usize);
212 if bytes {
213 spec(e).map(|_| ()).map_err(|x| x.shifted(at))?;
214 } else if let Some(i) = e.iter().position(|&b| b == b',' || !class::is_vchar(b)) {
215 return err(ErrorKind::InvalidRange, at + i);
216 }
217 }
218 Ok(RangeHeader { unit, set })
219}
220
221#[must_use]
245pub fn resolve(
246 h: &RangeHeader<'_>,
247 length: u64,
248 coalesce: bool,
249 limits: &Limits,
250 out: &mut [ByteRange],
251) -> RangeOutcome {
252 if !h.is_bytes() {
253 return RangeOutcome::NotBytes;
254 }
255 if h.len() > usize::from(limits.ranges) {
256 return RangeOutcome::Ignore;
257 }
258 let mut n = 0usize;
259 let mut descents = 0usize;
260 let mut previous_start: Option<u64> = None;
261 for s in h.specs() {
262 let Some(r) = absolute(s, length) else {
263 continue;
264 };
265 if previous_start.is_some_and(|p| r.start < p) {
266 descents += 1;
267 }
268 previous_start = Some(r.start);
269 match out.get_mut(n) {
270 Some(slot) => *slot = r,
271 None => return RangeOutcome::Ignore,
272 }
273 n += 1;
274 }
275 if n == 0 {
276 return RangeOutcome::Unsatisfiable;
277 }
278 let resolved = out.get_mut(..n).unwrap_or(&mut []);
279 let overlapping = resolved
280 .iter()
281 .enumerate()
282 .filter(|&(i, a)| {
283 resolved
284 .iter()
285 .enumerate()
286 .any(|(j, b)| i != j && a.start <= b.end && b.start <= a.end)
287 })
288 .count();
289 if overlapping > 2 || descents > 2 {
290 return RangeOutcome::Ignore;
291 }
292 if coalesce {
293 for i in 1..resolved.len() {
295 let mut j = i;
296 while j > 0 {
297 let (Some(&a), Some(&b)) = (resolved.get(j - 1), resolved.get(j)) else {
298 break;
299 };
300 if a.start <= b.start {
301 break;
302 }
303 resolved.swap(j - 1, j);
304 j -= 1;
305 }
306 }
307 let mut merged = 0usize;
308 for i in 0..resolved.len() {
309 let Some(&r) = resolved.get(i) else { break };
310 match merged.checked_sub(1).and_then(|m| resolved.get_mut(m)) {
311 Some(last) if r.start <= last.end.saturating_add(1) => {
312 last.end = last.end.max(r.end);
313 }
314 _ => {
315 if let Some(slot) = resolved.get_mut(merged) {
316 *slot = r;
317 }
318 merged += 1;
319 }
320 }
321 }
322 n = merged;
323 }
324 RangeOutcome::Ranges(n)
325}
326
327fn absolute(s: RangeSpec, length: u64) -> Option<ByteRange> {
330 let last = length.checked_sub(1)?;
331 match s {
332 RangeSpec::From(f) if f <= last => Some(ByteRange {
333 start: f,
334 end: last,
335 }),
336 RangeSpec::FromTo(f, l) if f <= last => Some(ByteRange {
337 start: f,
338 end: l.min(last),
339 }),
340 RangeSpec::Suffix(n) if n > 0 => Some(ByteRange {
341 start: length - n.min(length),
342 end: last,
343 }),
344 _ => None,
345 }
346}
347
348pub fn byte_ranges(value: &[u8], length: u64, out: &mut [ByteRange]) -> Result<RangeOutcome> {
367 let h = parse(value, &Limits::DEFAULT)?;
368 Ok(resolve(&h, length, false, &Limits::DEFAULT, out))
369}
370
371#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
373pub enum ContentRange {
374 Satisfied {
376 range: ByteRange,
378 complete: Option<u64>,
380 },
381 Unsatisfied {
383 complete: u64,
385 },
386}
387
388pub fn parse_content_range(value: &[u8]) -> Result<ContentRange> {
408 let lead = skip_ows(value);
409 let v = trim_ows(value);
410 inner_content_range(v).map_err(|e| e.shifted(lead))
411}
412
413fn inner_content_range(v: &[u8]) -> Result<ContentRange> {
414 let unit = v.get(..5).unwrap_or(&[]);
415 if !unit.eq_ignore_ascii_case(b"bytes") || v.get(5) != Some(&b' ') {
416 return err(ErrorKind::InvalidRange, 0);
417 }
418 let rest = v.get(6..).unwrap_or(&[]);
419 let Some(slash) = rest.iter().position(|&b| b == b'/') else {
420 return err(ErrorKind::InvalidRange, v.len());
421 };
422 let (range_text, complete_text) = (
423 rest.get(..slash).unwrap_or(&[]),
424 rest.get(slash + 1..).unwrap_or(&[]),
425 );
426 let complete_at = 6 + slash + 1;
427 if range_text == b"*" {
428 let complete = parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?;
429 return Ok(ContentRange::Unsatisfied { complete });
430 }
431 let complete = if complete_text == b"*" {
432 None
433 } else {
434 Some(parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?)
435 };
436 let Some(dash) = range_text.iter().position(|&b| b == b'-') else {
437 return err(ErrorKind::InvalidRange, 6);
438 };
439 let first = parse_u64(range_text.get(..dash).unwrap_or(&[])).map_err(|e| e.shifted(6))?;
440 let last = parse_u64(range_text.get(dash + 1..).unwrap_or(&[]))
441 .map_err(|e| e.shifted(6 + dash + 1))?;
442 if last < first || complete.is_some_and(|c| last >= c) {
443 return err(ErrorKind::InvalidRange, 6);
444 }
445 Ok(ContentRange::Satisfied {
446 range: ByteRange {
447 start: first,
448 end: last,
449 },
450 complete,
451 })
452}
453
454fn put_content_range(
455 r: ByteRange,
456 complete: Option<u64>,
457 out: &mut impl Sink,
458) -> core::result::Result<(), WriteError> {
459 out.put(b"bytes ")?;
460 write_decimal_u64(r.start, out)?;
461 out.put(b"-")?;
462 write_decimal_u64(r.end, out)?;
463 out.put(b"/")?;
464 match complete {
465 Some(c) => write_decimal_u64(c, out)?,
466 None => out.put(b"*")?,
467 }
468 Ok(())
469}
470
471pub fn write_content_range(
479 r: ByteRange,
480 complete: Option<u64>,
481 out: &mut impl Sink,
482) -> core::result::Result<(), WriteError> {
483 if r.end < r.start || complete.is_some_and(|c| r.end >= c) {
484 return Err(WriteError::OutOfRange);
485 }
486 let mut count = CountSink::new();
487 put_content_range(r, complete, &mut count)?;
488 reserve(out, count.count())?;
489 put_content_range(r, complete, out)
490}
491
492pub fn write_unsatisfied(
509 complete: u64,
510 out: &mut impl Sink,
511) -> core::result::Result<(), WriteError> {
512 let mut count = CountSink::new();
513 count.put(b"bytes */")?;
514 write_decimal_u64(complete, &mut count)?;
515 reserve(out, count.count())?;
516 out.put(b"bytes */")?;
517 write_decimal_u64(complete, out)
518}
519
520#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
522pub enum IfRange<'a> {
523 Date(HttpDate),
525 Tag(EntityTag<'a>),
527}
528
529impl IfRange<'_> {
530 #[must_use]
546 pub fn matches(
547 &self,
548 current_tag: Option<&EntityTag<'_>>,
549 last_modified: Option<HttpDate>,
550 ) -> bool {
551 match self {
552 IfRange::Tag(t) => current_tag.is_some_and(|c| t.strong_eq(c)),
553 IfRange::Date(d) => last_modified == Some(*d),
554 }
555 }
556}
557
558pub fn parse_if_range(value: &[u8]) -> Result<IfRange<'_>> {
565 let v = trim_ows(value);
566 if v.starts_with(b"W/") || v.starts_with(b"\"") {
567 EntityTag::parse(value).map(IfRange::Tag)
568 } else {
569 HttpDate::parse_imf_fixdate(value).map(IfRange::Date)
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576
577 fn resolve_str(
578 v: &[u8],
579 length: u64,
580 coalesce: bool,
581 ) -> (RangeOutcome, std::vec::Vec<ByteRange>) {
582 let mut out = [ByteRange { start: 0, end: 0 }; 32];
583 let h = parse(v, &Limits::DEFAULT).unwrap();
584 let o = resolve(&h, length, coalesce, &Limits::DEFAULT, &mut out);
585 let n = if let RangeOutcome::Ranges(n) = o {
586 n
587 } else {
588 0
589 };
590 (o, out[..n].to_vec())
591 }
592
593 #[test]
594 fn test_range_parse_edge_cases() {
595 let l = Limits::DEFAULT;
596 let k = |v: &[u8]| parse(v, &l).unwrap_err().kind();
597 assert!(parse(b"bytes=0-0", &l).is_ok());
598 assert!(parse(b"BYTES=0-1", &l).unwrap().is_bytes());
599 assert_eq!(k(b"bytes=5-4"), ErrorKind::InvalidRange);
600 assert_eq!(k(b"bytes = 0-1"), ErrorKind::InvalidRange);
601 assert_eq!(k(b"bytes= 0-1"), ErrorKind::InvalidRange);
602 assert_eq!(k(b"bytes=0 -1"), ErrorKind::InvalidNumber);
603 assert_eq!(k(b"bytes="), ErrorKind::InvalidRange);
604 assert_eq!(k(b"bytes=-"), ErrorKind::InvalidRange);
605 assert_eq!(
606 k(b"bytes=0-18446744073709551616"),
607 ErrorKind::NumberOverflow
608 );
609 assert!(parse(b"bytes=0-18446744073709551615", &l).is_ok());
610 assert!(parse(b"bytes=0-1,", &l).is_ok());
611 assert_eq!(k(b"=0-1"), ErrorKind::NotToken);
612 assert_eq!(k(b"bytes"), ErrorKind::InvalidRange);
613 assert!(parse(b"items=0-5", &l).is_ok());
614 assert_eq!(k(b"items=a b"), ErrorKind::InvalidRange);
615 assert_eq!(parse(b"items=0-5", &l).unwrap().specs().count(), 0);
616 }
617
618 #[test]
619 fn test_range_resolution() {
620 assert_eq!(
621 resolve_str(b"bytes=0-", 10, false).1,
622 [ByteRange { start: 0, end: 9 }]
623 );
624 assert_eq!(
625 resolve_str(b"bytes=-0", 10, false).0,
626 RangeOutcome::Unsatisfiable
627 );
628 assert_eq!(
629 resolve_str(b"bytes=-1", 0, false).0,
630 RangeOutcome::Unsatisfiable
631 );
632 assert_eq!(
633 resolve_str(b"bytes=-20", 10, false).1,
634 [ByteRange { start: 0, end: 9 }]
635 );
636 assert_eq!(
637 resolve_str(b"bytes=5-100", 10, false).1,
638 [ByteRange { start: 5, end: 9 }]
639 );
640 assert_eq!(
641 resolve_str(b"bytes=10-", 10, false).0,
642 RangeOutcome::Unsatisfiable
643 );
644 assert_eq!(
645 resolve_str(b"bytes=10-, 0-0", 10, false).1,
646 [ByteRange { start: 0, end: 0 }]
647 );
648 assert_eq!(
649 resolve_str(b"bytes=0-18446744073709551615", u64::MAX, false).1[0].len(),
650 u64::MAX
651 );
652 }
653
654 fn many(n: u64) -> std::vec::Vec<u8> {
656 use std::fmt::Write as _;
657 let mut text = std::string::String::from("bytes=");
658 for i in 0..n {
659 let _ = write!(text, "{}-{},", i * 10, i * 10 + 1);
660 }
661 text.into_bytes()
662 }
663
664 #[test]
665 fn test_range_17_ranges_ignored() {
666 assert_eq!(
667 resolve_str(&many(16), 1000, false).0,
668 RangeOutcome::Ranges(16)
669 );
670 assert_eq!(resolve_str(&many(17), 1000, false).0, RangeOutcome::Ignore);
671 }
672
673 #[test]
674 fn test_range_three_overlapping_ignored() {
675 assert_eq!(
676 resolve_str(b"bytes=0-10, 5-15", 100, false).0,
677 RangeOutcome::Ranges(2)
678 );
679 assert_eq!(
680 resolve_str(b"bytes=0-10, 5-15, 8-20", 100, false).0,
681 RangeOutcome::Ignore
682 );
683 assert_eq!(
684 resolve_str(b"bytes=50-60, 40-45, 30-35, 20-25", 100, false).0,
685 RangeOutcome::Ignore
686 );
687 assert_eq!(
688 resolve_str(b"bytes=50-60, 40-45, 30-35", 100, false).0,
689 RangeOutcome::Ranges(3)
690 );
691 }
692
693 #[test]
694 fn test_range_coalesce() {
695 let (o, r) = resolve_str(b"bytes=40-49, 0-10, 11-20, 5-6", 100, true);
696 assert_eq!(o, RangeOutcome::Ranges(2));
697 assert_eq!(
698 r,
699 [
700 ByteRange { start: 0, end: 20 },
701 ByteRange { start: 40, end: 49 }
702 ]
703 );
704 }
705
706 #[test]
707 fn test_content_range_cases() {
708 assert!(parse_content_range(b"bytes 0-0/1").is_ok());
709 assert_eq!(
710 parse_content_range(b"bytes 0-0/*").unwrap(),
711 ContentRange::Satisfied {
712 range: ByteRange { start: 0, end: 0 },
713 complete: None
714 }
715 );
716 assert_eq!(
717 parse_content_range(b"bytes */0").unwrap(),
718 ContentRange::Unsatisfied { complete: 0 }
719 );
720 for bad in [
721 &b"bytes 1-0/5"[..],
722 b"bytes 0-5/5",
723 b"bytes0-1/2",
724 b"items 0-1/2",
725 b"bytes 0-1",
726 b"bytes -1/2",
727 b"bytes */*",
728 ] {
729 assert!(
730 parse_content_range(bad).is_err(),
731 "{}",
732 std::string::String::from_utf8_lossy(bad)
733 );
734 }
735 }
736
737 #[test]
738 fn test_content_range_writers() {
739 let mut buf = [0u8; 64];
740 let mut out = crate::SliceSink::new(&mut buf);
741 assert_eq!(
742 write_content_range(ByteRange { start: 5, end: 4 }, None, &mut out),
743 Err(WriteError::OutOfRange)
744 );
745 assert_eq!(
746 write_content_range(ByteRange { start: 0, end: 5 }, Some(5), &mut out),
747 Err(WriteError::OutOfRange)
748 );
749 write_content_range(ByteRange { start: 0, end: 4 }, None, &mut out).unwrap();
750 assert_eq!(out.written(), b"bytes 0-4/*");
751 assert_eq!(
752 parse_content_range(out.written()).unwrap(),
753 ContentRange::Satisfied {
754 range: ByteRange { start: 0, end: 4 },
755 complete: None
756 }
757 );
758 let mut small = [0u8; 8];
759 let mut out = crate::SliceSink::new(&mut small);
760 assert_eq!(write_unsatisfied(1, &mut out), Err(WriteError::Full));
761 assert!(out.is_empty());
762 }
763
764 #[test]
765 fn test_if_range() {
766 let d = HttpDate::from_unix(784_111_777).unwrap();
767 let date = parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT").unwrap();
768 assert!(date.matches(None, Some(d)));
769 assert!(!date.matches(None, None));
770 assert!(parse_if_range(b"Sunday, 06-Nov-94 08:49:37 GMT").is_err());
771 let t = EntityTag::strong(b"a").unwrap();
772 assert!(!parse_if_range(b"\"b\"").unwrap().matches(Some(&t), Some(d)));
773 }
774}