parse_net/range.rs
1//! `Range`, `Content-Range`, and `If-Range` (RFC 9110 section 14), with
2//! resolution against a representation length and budgets against abusive
3//! range sets.
4//!
5//! # Examples
6//!
7//! ```
8//! use parse_net::{self as pn, ByteRange, RangeOutcome, range};
9//!
10//! let mut out = [ByteRange { start: 0, end: 0 }; 8];
11//! assert_eq!(pn::byte_ranges(b"bytes=0-99, -100", 1000, &mut out)?, RangeOutcome::Ranges(2));
12//! assert_eq!(out[1], ByteRange { start: 900, end: 999 });
13//! assert_eq!(pn::byte_ranges(b"bytes=2000-", 1000, &mut out)?, RangeOutcome::Unsatisfiable);
14//! assert_eq!(pn::byte_ranges(b"items=0-5", 1000, &mut out)?, RangeOutcome::NotBytes);
15//!
16//! let mut buf = [0u8; 32];
17//! let mut sink = pn::SliceSink::new(&mut buf);
18//! range::write_content_range(out[0], Some(1000), &mut sink).expect("room");
19//! assert_eq!(sink.written(), b"bytes 0-99/1000");
20//! # Ok::<(), pn::ParseError>(())
21//! ```
22
23use crate::{
24 class,
25 date::HttpDate,
26 error::{ErrorKind, Result, WriteError, err},
27 etag::EntityTag,
28 limits::Limits,
29 num::parse_u64,
30 text::{skip_ows, trim_ows},
31 write::{CountSink, Sink, reserve, write_decimal_u64},
32};
33
34/// One `range-spec` of a `bytes` range set.
35///
36/// # Examples
37///
38/// ```
39/// use parse_net::{range, Limits, RangeSpec};
40///
41/// let h = range::parse(b"bytes=0-9, 50-, -5", &Limits::DEFAULT)?;
42/// let specs: Vec<_> = h.specs().collect();
43/// assert_eq!(specs, [RangeSpec::FromTo(0, 9), RangeSpec::From(50), RangeSpec::Suffix(5)]);
44/// # Ok::<(), parse_net::ParseError>(())
45/// ```
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub enum RangeSpec {
48 /// `first-` : from `first` to the end.
49 From(u64),
50 /// `first-last`, both inclusive.
51 FromTo(u64, u64),
52 /// `-length`: the last `length` bytes.
53 Suffix(u64),
54}
55
56/// A resolved byte range, both ends inclusive.
57///
58/// # Examples
59///
60/// ```
61/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
62///
63/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
64/// assert_eq!(byte_ranges(b"bytes=-10", 100, &mut out)?, RangeOutcome::Ranges(1));
65/// assert_eq!((out[0], out[0].len()), (ByteRange { start: 90, end: 99 }, 10));
66/// # Ok::<(), parse_net::ParseError>(())
67/// ```
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69pub struct ByteRange {
70 /// The first byte.
71 pub start: u64,
72 /// The last byte (inclusive).
73 pub end: u64,
74}
75
76impl ByteRange {
77 /// The number of bytes, `end - start + 1`, saturating at `u64::MAX`; zero
78 /// when `end < start`.
79 ///
80 /// # Examples
81 ///
82 /// ```
83 /// assert_eq!(parse_net::ByteRange { start: 0, end: 499 }.len(), 500);
84 /// ```
85 #[must_use]
86 pub const fn len(&self) -> u64 {
87 if self.end < self.start {
88 0
89 } else {
90 (self.end - self.start).saturating_add(1)
91 }
92 }
93
94 /// Whether the range holds no bytes (only possible when `end < start`).
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use parse_net::ByteRange;
100 ///
101 /// assert!(ByteRange { start: 5, end: 4 }.is_empty());
102 /// assert!(!ByteRange { start: 5, end: 5 }.is_empty());
103 /// ```
104 #[must_use]
105 pub const fn is_empty(&self) -> bool {
106 self.end < self.start
107 }
108}
109
110/// What to do with a `Range` request.
111///
112/// # Examples
113///
114/// ```
115/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
116///
117/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
118/// assert_eq!(byte_ranges(b"bytes=500-", 1000, &mut out)?, RangeOutcome::Ranges(1));
119/// assert_eq!(byte_ranges(b"bytes=1000-", 1000, &mut out)?, RangeOutcome::Unsatisfiable);
120/// # Ok::<(), parse_net::ParseError>(())
121/// ```
122#[non_exhaustive]
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
124pub enum RangeOutcome {
125 /// Serve these ranges (206): the first `n` entries of the output.
126 Ranges(usize),
127 /// No range is satisfiable: answer 416 with `Content-Range: bytes */length`.
128 Unsatisfiable,
129 /// Ignore the field and serve the whole representation (200): the set is
130 /// abusive (too many ranges, too many overlapping, too much out of order)
131 /// or does not fit the output.
132 Ignore,
133 /// Another range unit: ignore the field and serve the whole
134 /// representation.
135 NotBytes,
136}
137
138/// A validated `Range` value.
139///
140/// # Examples
141///
142/// ```
143/// use parse_net::{range, Limits};
144///
145/// let h = range::parse(b"bytes=0-99, 200-299", &Limits::DEFAULT)?;
146/// assert!(h.is_bytes());
147/// assert_eq!(h.specs().count(), 2);
148/// # Ok::<(), parse_net::ParseError>(())
149/// ```
150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
151pub struct RangeHeader<'a> {
152 unit: &'a [u8],
153 /// The validated range set.
154 set: &'a [u8],
155 count: usize,
156}
157
158impl<'a> RangeHeader<'a> {
159 /// Whether the unit is `bytes` (case-insensitive).
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use parse_net::{range, Limits};
165 ///
166 /// assert!(range::parse(b"BYTES=0-1", &Limits::DEFAULT)?.is_bytes());
167 /// assert!(!range::parse(b"items=0-1", &Limits::DEFAULT)?.is_bytes());
168 /// # Ok::<(), parse_net::ParseError>(())
169 /// ```
170 #[must_use]
171 pub fn is_bytes(&self) -> bool {
172 self.unit.eq_ignore_ascii_case(b"bytes")
173 }
174
175 /// The range unit, as sent.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// use parse_net::{range, Limits};
181 ///
182 /// assert_eq!(range::parse(b"items=0-5", &Limits::DEFAULT)?.unit(), b"items");
183 /// # Ok::<(), parse_net::ParseError>(())
184 /// ```
185 #[must_use]
186 pub const fn unit(&self) -> &'a [u8] {
187 self.unit
188 }
189
190 /// The range specs, in request order; empty unless the unit is `bytes`.
191 ///
192 /// # Examples
193 ///
194 /// ```
195 /// use parse_net::{range, Limits, RangeSpec};
196 ///
197 /// assert_eq!(range::parse(b"bytes=-1", &Limits::DEFAULT)?.specs().next(), Some(RangeSpec::Suffix(1)));
198 /// assert_eq!(range::parse(b"items=0-5", &Limits::DEFAULT)?.specs().count(), 0);
199 /// # Ok::<(), parse_net::ParseError>(())
200 /// ```
201 pub fn specs(&self) -> impl Iterator<Item = RangeSpec> + use<'a> {
202 let set = if self.is_bytes() { self.set } else { &[] };
203 Elements { s: set }.filter_map(|e| spec(e).ok())
204 }
205
206 /// The number of members in the range set, whatever the unit (so it can
207 /// be nonzero when [`specs`](Self::specs) is empty).
208 ///
209 /// # Examples
210 ///
211 /// ```
212 /// use parse_net::{range, Limits};
213 ///
214 /// let h = range::parse(b"items=0-5, 9-10", &Limits::DEFAULT)?;
215 /// assert_eq!((h.len(), h.specs().count()), (2, 0));
216 /// # Ok::<(), parse_net::ParseError>(())
217 /// ```
218 #[must_use]
219 pub const fn len(&self) -> usize {
220 self.count
221 }
222
223 /// Whether the set is empty (never true for a parsed value).
224 ///
225 /// # Examples
226 ///
227 /// ```
228 /// use parse_net::{range, Limits};
229 ///
230 /// assert!(!range::parse(b"bytes=0-0", &Limits::DEFAULT)?.is_empty());
231 /// # Ok::<(), parse_net::ParseError>(())
232 /// ```
233 #[must_use]
234 pub const fn is_empty(&self) -> bool {
235 self.count == 0
236 }
237}
238
239/// The members of a range set: the text between commas, OWS trimmed, empty
240/// members skipped. The set is validated before it is walked.
241struct Elements<'a> {
242 s: &'a [u8],
243}
244
245impl<'a> Iterator for Elements<'a> {
246 type Item = &'a [u8];
247
248 fn next(&mut self) -> Option<&'a [u8]> {
249 loop {
250 if self.s.is_empty() {
251 return None;
252 }
253 let end = crate::scan::find_byte(b',', self.s).unwrap_or(self.s.len());
254 let e = trim_ows(self.s.get(..end).unwrap_or(&[]));
255 self.s = self.s.get(end + 1..).unwrap_or(&[]);
256 if !e.is_empty() {
257 return Some(e);
258 }
259 }
260 }
261}
262
263/// The error for byte `at` of a value, where a control byte keeps its kind.
264fn refused(v: &[u8], at: usize, kind: ErrorKind) -> crate::ParseError {
265 match v.get(at) {
266 Some(&b) if crate::scan::is_control(b) => {
267 crate::ParseError::new(crate::scan::control_kind(b), at)
268 }
269 _ => crate::ParseError::new(kind, at),
270 }
271}
272
273/// A byte position of a range set. A numeral past `u64::MAX` saturates: RFC
274/// 9110 section 14.1.2 tells recipients to read large numerals without
275/// failing, and `u64::MAX` is past the end of any representation.
276fn position(digits: &[u8]) -> Result<u64> {
277 let mut n = 0u64;
278 for (i, &b) in digits.iter().enumerate() {
279 if !b.is_ascii_digit() {
280 return Err(refused(digits, i, ErrorKind::InvalidNumber));
281 }
282 n = n.saturating_mul(10).saturating_add(u64::from(b - b'0'));
283 }
284 Ok(n)
285}
286
287/// Whether the numeral `a` is above `b`, whatever their size.
288fn numeral_above(a: &[u8], b: &[u8]) -> bool {
289 let strip = |s: &'_ [u8]| -> usize { s.iter().take_while(|&&d| d == b'0').count() };
290 let a = a.get(strip(a)..).unwrap_or(&[]);
291 let b = b.get(strip(b)..).unwrap_or(&[]);
292 a.len() > b.len() || (a.len() == b.len() && a > b)
293}
294
295/// Parses one `int-range / suffix-range`.
296fn spec(e: &[u8]) -> Result<RangeSpec> {
297 let Some(dash) = e.iter().position(|&b| b == b'-') else {
298 let at = e.iter().position(|&b| crate::scan::is_control(b));
299 return Err(refused(e, at.unwrap_or(e.len()), ErrorKind::InvalidRange));
300 };
301 let (first, last) = (
302 e.get(..dash).unwrap_or(&[]),
303 e.get(dash + 1..).unwrap_or(&[]),
304 );
305 match (first.is_empty(), last.is_empty()) {
306 (true, true) => err(ErrorKind::InvalidRange, 0),
307 (true, false) => Ok(RangeSpec::Suffix(
308 position(last).map_err(|x| x.shifted(dash + 1))?,
309 )),
310 (false, true) => Ok(RangeSpec::From(position(first)?)),
311 (false, false) => {
312 let f = position(first)?;
313 let l = position(last).map_err(|x| x.shifted(dash + 1))?;
314 if numeral_above(first, last) {
315 return err(ErrorKind::InvalidRange, 0);
316 }
317 Ok(RangeSpec::FromTo(f, l))
318 }
319 }
320}
321
322/// Parses `range-unit "=" range-set` (RFC 9110 section 14.1.2).
323///
324/// The unit compares case-insensitively. A `bytes` set must hold only
325/// `first-last`, `first-`, and `-length` specs; another unit's set is
326/// checked as `other-range` bytes and not interpreted.
327///
328/// # Errors
329///
330/// [`ErrorKind::NotToken`] for the unit; [`ErrorKind::InvalidRange`] for a
331/// missing `=`, whitespace around it, an empty set, `first > last`, or a
332/// malformed spec; [`ErrorKind::InvalidNumber`]; a control kind for a control
333/// byte; the list errors. A position past `u64::MAX` is not an error: it
334/// saturates (RFC 9110 section 14.1.2), which is past the end of any
335/// representation.
336///
337/// # Examples
338///
339/// ```
340/// use parse_net::{range, Limits, RangeSpec};
341///
342/// let h = range::parse(b"bytes=0-0, -5, 10-", &Limits::DEFAULT)?;
343/// assert_eq!(h.specs().collect::<Vec<_>>(), [RangeSpec::FromTo(0, 0), RangeSpec::Suffix(5), RangeSpec::From(10)]);
344/// assert!(range::parse(b"bytes=5-4", &Limits::DEFAULT).is_err());
345/// # Ok::<(), parse_net::ParseError>(())
346/// ```
347pub fn parse<'a>(value: &'a [u8], limits: &Limits) -> Result<RangeHeader<'a>> {
348 let lead = skip_ows(value);
349 let v = trim_ows(value);
350 let unit_len = v.iter().take_while(|&&b| class::is_tchar(b)).count();
351 if unit_len == 0 {
352 return Err(refused(v, 0, ErrorKind::NotToken).shifted(lead));
353 }
354 if v.get(unit_len) != Some(&b'=') {
355 return Err(refused(v, unit_len, ErrorKind::InvalidRange).shifted(lead));
356 }
357 let unit = v.get(..unit_len).unwrap_or(&[]);
358 let set_at = unit_len + 1;
359 let set_text = v.get(set_at..).unwrap_or(&[]);
360 if set_text.first().is_some_and(|&b| crate::text::is_ows(b)) {
361 return err(ErrorKind::InvalidRange, lead + set_at);
362 }
363 // One pass over the set: each member is found by its comma, trimmed,
364 // and checked; empty members are skipped up to the limit.
365 let bytes = unit.eq_ignore_ascii_case(b"bytes");
366 if set_text.is_empty() {
367 return err(ErrorKind::InvalidRange, lead + set_at);
368 }
369 let (mut i, mut count, mut empty) = (0usize, 0usize, 0usize);
370 while i <= set_text.len() {
371 let rest = set_text.get(i..).unwrap_or(&[]);
372 let end = crate::scan::find_byte(b',', rest).unwrap_or(rest.len());
373 let raw = rest.get(..end).unwrap_or(&[]);
374 let e = trim_ows(raw);
375 let at = lead + set_at + i + skip_ows(raw);
376 if e.is_empty() {
377 empty += 1;
378 if empty > usize::from(limits.empty_elements) {
379 return err(ErrorKind::TooManyEmptyElements, at);
380 }
381 } else {
382 count += 1;
383 if count > usize::from(limits.list_members) {
384 return err(ErrorKind::TooManyMembers, at);
385 }
386 if bytes {
387 spec(e).map(|_| ()).map_err(|x| x.shifted(at))?;
388 } else if let Some(j) = e.iter().position(|&b| !class::is_vchar(b)) {
389 return Err(refused(e, j, ErrorKind::InvalidRange).shifted(at));
390 }
391 }
392 i += end + 1;
393 }
394 if count == 0 {
395 return err(ErrorKind::InvalidRange, lead + set_at);
396 }
397 Ok(RangeHeader {
398 unit,
399 set: set_text,
400 count,
401 })
402}
403
404/// Resolves a parsed `Range` against a representation of `length` bytes.
405///
406/// Suffix ranges become absolute; a last position beyond the end is clamped;
407/// a range starting at or after the end, and a zero-length suffix, are
408/// dropped. More than [`Limits::max_ranges`] ranges, more than two ranges
409/// that overlap another, or more than two ranges that start before the one
410/// before them give [`RangeOutcome::Ignore`] (RFC 9110 section 14.2 allows
411/// ignoring abusive sets). A non-zero suffix on an empty representation also
412/// gives `Ignore`: it is satisfiable, and a [`ByteRange`] cannot hold an empty
413/// range. With `coalesce`, overlapping and adjacent ranges
414/// are merged into ascending order (this changes the shape of a multipart
415/// response, so it is the caller's choice). An `out` too small for the
416/// ranges gives `Ignore`: the set is never silently truncated.
417///
418/// # Examples
419///
420/// ```
421/// use parse_net::{range, ByteRange, Limits, RangeOutcome};
422///
423/// let h = range::parse(b"bytes=0-10, 5-20, 40-49", &Limits::DEFAULT)?;
424/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
425/// assert_eq!(range::resolve(&h, 100, true, &Limits::DEFAULT, &mut out), RangeOutcome::Ranges(2));
426/// assert_eq!(out[..2], [ByteRange { start: 0, end: 20 }, ByteRange { start: 40, end: 49 }]);
427/// # Ok::<(), parse_net::ParseError>(())
428/// ```
429#[must_use]
430pub fn resolve(
431 h: &RangeHeader<'_>,
432 length: u64,
433 coalesce: bool,
434 limits: &Limits,
435 out: &mut [ByteRange],
436) -> RangeOutcome {
437 if !h.is_bytes() {
438 return RangeOutcome::NotBytes;
439 }
440 if h.len() > usize::from(limits.ranges) {
441 return RangeOutcome::Ignore;
442 }
443 let mut n = 0usize;
444 let mut descents = 0usize;
445 let mut previous_start: Option<u64> = None;
446 for s in h.specs() {
447 let Some(r) = absolute(s, length) else {
448 continue;
449 };
450 if previous_start.is_some_and(|p| r.start < p) {
451 descents += 1;
452 }
453 previous_start = Some(r.start);
454 match out.get_mut(n) {
455 Some(slot) => *slot = r,
456 None => return RangeOutcome::Ignore,
457 }
458 n += 1;
459 }
460 if n == 0 {
461 // A non-zero suffix is satisfiable on an empty representation (RFC
462 // 9110 section 14.1.2), and a `ByteRange` cannot hold an empty range,
463 // so the whole, empty, representation is served.
464 let empty_suffix = |s| matches!(s, RangeSpec::Suffix(1..));
465 if length == 0 && h.specs().any(empty_suffix) {
466 return RangeOutcome::Ignore;
467 }
468 return RangeOutcome::Unsatisfiable;
469 }
470 let resolved = out.get_mut(..n).unwrap_or(&mut []);
471 let overlapping = resolved
472 .iter()
473 .enumerate()
474 .filter(|&(i, a)| {
475 resolved
476 .iter()
477 .enumerate()
478 .any(|(j, b)| i != j && a.start <= b.end && b.start <= a.end)
479 })
480 .count();
481 if overlapping > 2 || descents > 2 {
482 return RangeOutcome::Ignore;
483 }
484 if coalesce {
485 // Insertion sort: at most `max_ranges` entries.
486 for i in 1..resolved.len() {
487 let mut j = i;
488 while j > 0 {
489 let (Some(&a), Some(&b)) = (resolved.get(j - 1), resolved.get(j)) else {
490 break;
491 };
492 if a.start <= b.start {
493 break;
494 }
495 resolved.swap(j - 1, j);
496 j -= 1;
497 }
498 }
499 let mut merged = 0usize;
500 for i in 0..resolved.len() {
501 let Some(&r) = resolved.get(i) else { break };
502 match merged.checked_sub(1).and_then(|m| resolved.get_mut(m)) {
503 Some(last) if r.start <= last.end.saturating_add(1) => {
504 last.end = last.end.max(r.end);
505 }
506 _ => {
507 if let Some(slot) = resolved.get_mut(merged) {
508 *slot = r;
509 }
510 merged += 1;
511 }
512 }
513 }
514 n = merged;
515 }
516 RangeOutcome::Ranges(n)
517}
518
519/// The absolute range of `s` in a representation of `length` bytes, or
520/// `None` when it is unsatisfiable.
521fn absolute(s: RangeSpec, length: u64) -> Option<ByteRange> {
522 let last = length.checked_sub(1)?;
523 match s {
524 RangeSpec::From(f) if f <= last => Some(ByteRange {
525 start: f,
526 end: last,
527 }),
528 RangeSpec::FromTo(f, l) if f <= last => Some(ByteRange {
529 start: f,
530 end: l.min(last),
531 }),
532 RangeSpec::Suffix(n) if n > 0 => Some(ByteRange {
533 start: length - n.min(length),
534 end: last,
535 }),
536 _ => None,
537 }
538}
539
540/// The run of digits at the front of `s`, at most 19 (which cannot overflow a
541/// `u64`), and what follows it. A longer run leaves a digit in the rest.
542fn digits(s: &[u8]) -> (Option<u64>, &[u8]) {
543 let (mut n, mut rest, mut len) = (0u64, s, 0usize);
544 while let [d @ b'0'..=b'9', tail @ ..] = rest {
545 if len == 19 {
546 break;
547 }
548 n = n * 10 + u64::from(*d - b'0');
549 rest = tail;
550 len += 1;
551 }
552 ((len > 0).then_some(n), rest)
553}
554
555/// `bytes=first-last`, `bytes=first-`, or `bytes=-length`, read by position:
556/// the one shape nearly every request sends. `None` for anything else, or
557/// any failed check; the general path then gives the answer or the error
558/// (D-074).
559fn one_spec(value: &[u8]) -> Option<RangeSpec> {
560 let (first, rest) = digits(value.strip_prefix(b"bytes=")?);
561 let [b'-', rest @ ..] = rest else {
562 return None;
563 };
564 let (last, rest) = digits(rest);
565 if !rest.is_empty() {
566 return None;
567 }
568 match (first, last) {
569 (Some(f), Some(l)) if f <= l => Some(RangeSpec::FromTo(f, l)),
570 (Some(f), None) => Some(RangeSpec::From(f)),
571 (None, Some(l)) => Some(RangeSpec::Suffix(l)),
572 _ => None,
573 }
574}
575
576/// Parses and resolves `Range` against `length` with the default limits and
577/// no coalescing.
578///
579/// # Errors
580///
581/// As [`parse`]. RFC 9110 lets a server ignore a `Range` it cannot read:
582/// treat an error as [`RangeOutcome::Ignore`] unless the request should be
583/// refused.
584///
585/// # Examples
586///
587/// ```
588/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
589///
590/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
591/// assert_eq!(byte_ranges(b"bytes=0-0, 2-2", 10, &mut out)?, RangeOutcome::Ignore); // `out` too small
592/// # Ok::<(), parse_net::ParseError>(())
593/// ```
594pub fn byte_ranges(value: &[u8], length: u64, out: &mut [ByteRange]) -> Result<RangeOutcome> {
595 if let Some(s) = one_spec(value) {
596 return Ok(match (absolute(s, length), out.first_mut()) {
597 (None, _) if length == 0 && matches!(s, RangeSpec::Suffix(1..)) => RangeOutcome::Ignore,
598 (None, _) => RangeOutcome::Unsatisfiable,
599 (Some(r), Some(slot)) => {
600 *slot = r;
601 RangeOutcome::Ranges(1)
602 }
603 (Some(_), None) => RangeOutcome::Ignore,
604 });
605 }
606 let h = parse(value, &Limits::DEFAULT)?;
607 Ok(resolve(&h, length, false, &Limits::DEFAULT, out))
608}
609
610/// A parsed `Content-Range` value.
611///
612/// # Examples
613///
614/// ```
615/// use parse_net::{range::parse_content_range, ContentRange};
616///
617/// let ContentRange::Satisfied { range, complete } = parse_content_range(b"bytes 42-1233/*")? else { unreachable!() };
618/// assert_eq!((range.len(), complete), (1192, None));
619/// # Ok::<(), parse_net::ParseError>(())
620/// ```
621#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
622pub enum ContentRange {
623 /// `bytes first-last/complete` or `bytes first-last/*`.
624 Satisfied {
625 /// The range sent.
626 range: ByteRange,
627 /// The complete length, when known.
628 complete: Option<u64>,
629 },
630 /// `bytes */complete`, sent with 416.
631 Unsatisfied {
632 /// The complete length.
633 complete: u64,
634 },
635}
636
637/// Parses `Content-Range` (RFC 9110 section 14.4) for the `bytes` unit.
638///
639/// # Errors
640///
641/// [`ErrorKind::InvalidRange`] (another unit, a missing space, `last < first`,
642/// `last >= complete`, a malformed shape); [`ErrorKind::InvalidNumber`],
643/// [`ErrorKind::NumberOverflow`].
644///
645/// # Examples
646///
647/// ```
648/// use parse_net::{range::parse_content_range, ByteRange, ContentRange};
649///
650/// assert_eq!(parse_content_range(b"bytes 0-499/1234")?,
651/// ContentRange::Satisfied { range: ByteRange { start: 0, end: 499 }, complete: Some(1234) });
652/// assert_eq!(parse_content_range(b"bytes */0")?, ContentRange::Unsatisfied { complete: 0 });
653/// assert!(parse_content_range(b"bytes 0-5/5").is_err());
654/// # Ok::<(), parse_net::ParseError>(())
655/// ```
656pub fn parse_content_range(value: &[u8]) -> Result<ContentRange> {
657 let lead = skip_ows(value);
658 let v = trim_ows(value);
659 inner_content_range(v).map_err(|e| e.shifted(lead))
660}
661
662/// A number of a `Content-Range`: an empty one is a malformed shape.
663fn content_number(s: &[u8]) -> Result<u64> {
664 parse_u64(s).map_err(|e| match e.kind() {
665 ErrorKind::Empty => crate::ParseError::new(ErrorKind::InvalidRange, 0),
666 ErrorKind::InvalidNumber => refused(s, e.offset(), ErrorKind::InvalidNumber),
667 _ => e,
668 })
669}
670
671fn inner_content_range(v: &[u8]) -> Result<ContentRange> {
672 let unit = v.get(..5).unwrap_or(&[]);
673 if !unit.eq_ignore_ascii_case(b"bytes") || v.get(5) != Some(&b' ') {
674 let at = v.iter().take(6).position(|&b| crate::scan::is_control(b));
675 return Err(refused(v, at.unwrap_or(0), ErrorKind::InvalidRange));
676 }
677 let rest = v.get(6..).unwrap_or(&[]);
678 let Some(slash) = rest.iter().position(|&b| b == b'/') else {
679 return err(ErrorKind::InvalidRange, v.len());
680 };
681 let (range_text, complete_text) = (
682 rest.get(..slash).unwrap_or(&[]),
683 rest.get(slash + 1..).unwrap_or(&[]),
684 );
685 let complete_at = 6 + slash + 1;
686 if range_text == b"*" {
687 let complete = content_number(complete_text).map_err(|e| e.shifted(complete_at))?;
688 return Ok(ContentRange::Unsatisfied { complete });
689 }
690 let complete = if complete_text == b"*" {
691 None
692 } else {
693 Some(content_number(complete_text).map_err(|e| e.shifted(complete_at))?)
694 };
695 let Some(dash) = range_text.iter().position(|&b| b == b'-') else {
696 return err(ErrorKind::InvalidRange, 6);
697 };
698 let first = content_number(range_text.get(..dash).unwrap_or(&[])).map_err(|e| e.shifted(6))?;
699 let last = content_number(range_text.get(dash + 1..).unwrap_or(&[]))
700 .map_err(|e| e.shifted(6 + dash + 1))?;
701 if last < first || complete.is_some_and(|c| last >= c) {
702 return err(ErrorKind::InvalidRange, 6);
703 }
704 Ok(ContentRange::Satisfied {
705 range: ByteRange {
706 start: first,
707 end: last,
708 },
709 complete,
710 })
711}
712
713fn put_content_range(
714 r: ByteRange,
715 complete: Option<u64>,
716 out: &mut impl Sink,
717) -> core::result::Result<(), WriteError> {
718 out.put(b"bytes ")?;
719 write_decimal_u64(r.start, out)?;
720 out.put(b"-")?;
721 write_decimal_u64(r.end, out)?;
722 out.put(b"/")?;
723 match complete {
724 Some(c) => write_decimal_u64(c, out)?,
725 None => out.put(b"*")?,
726 }
727 Ok(())
728}
729
730/// Writes `bytes first-last/complete`, or `bytes first-last/*` when the
731/// complete length is unknown.
732///
733/// # Errors
734///
735/// [`WriteError::OutOfRange`] when `end < start` or `end >= complete`;
736/// [`WriteError::Full`] (nothing written when the sink reports its room).
737///
738/// # Examples
739///
740/// ```
741/// use parse_net::{range::write_content_range, ByteRange, SliceSink, WriteError};
742///
743/// let mut buf = [0u8; 32];
744/// let mut out = SliceSink::new(&mut buf);
745/// write_content_range(ByteRange { start: 0, end: 9 }, None, &mut out)?;
746/// assert_eq!(out.written(), b"bytes 0-9/*");
747/// assert_eq!(write_content_range(ByteRange { start: 0, end: 9 }, Some(5), &mut out), Err(WriteError::OutOfRange));
748/// # Ok::<(), parse_net::WriteError>(())
749/// ```
750pub fn write_content_range(
751 r: ByteRange,
752 complete: Option<u64>,
753 out: &mut impl Sink,
754) -> core::result::Result<(), WriteError> {
755 if r.end < r.start || complete.is_some_and(|c| r.end >= c) {
756 return Err(WriteError::OutOfRange);
757 }
758 let mut count = CountSink::new();
759 put_content_range(r, complete, &mut count)?;
760 reserve(out, count.count())?;
761 put_content_range(r, complete, out)
762}
763
764/// Writes `bytes */complete`, for a 416 response.
765///
766/// # Errors
767///
768/// [`WriteError::Full`] (nothing written when the sink reports its room).
769///
770/// # Examples
771///
772/// ```
773/// use parse_net::{range::write_unsatisfied, SliceSink};
774///
775/// let mut buf = [0u8; 32];
776/// let mut out = SliceSink::new(&mut buf);
777/// write_unsatisfied(1234, &mut out).expect("room");
778/// assert_eq!(out.written(), b"bytes */1234");
779/// ```
780pub fn write_unsatisfied(
781 complete: u64,
782 out: &mut impl Sink,
783) -> core::result::Result<(), WriteError> {
784 let mut count = CountSink::new();
785 count.put(b"bytes */")?;
786 write_decimal_u64(complete, &mut count)?;
787 reserve(out, count.count())?;
788 out.put(b"bytes */")?;
789 write_decimal_u64(complete, out)
790}
791
792/// A parsed `If-Range` value.
793///
794/// # Examples
795///
796/// ```
797/// use parse_net::{range::parse_if_range, HttpDate, IfRange};
798///
799/// let date = b"Wed, 21 Oct 2026 07:28:00 GMT";
800/// let cond = parse_if_range(date)?;
801/// assert_eq!(cond, IfRange::Date(HttpDate::parse_imf_fixdate(date)?));
802/// assert!(cond.matches(None, Some(HttpDate::parse_imf_fixdate(date)?)));
803/// # Ok::<(), parse_net::ParseError>(())
804/// ```
805#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
806pub enum IfRange<'a> {
807 /// An HTTP-date.
808 Date(HttpDate),
809 /// An entity tag.
810 Tag(EntityTag<'a>),
811}
812
813impl IfRange<'_> {
814 /// Whether the condition holds, so the ranges may be served (RFC 9110
815 /// section 13.1.5): a tag matches by strong comparison (a weak tag never
816 /// matches); a date matches when it equals `last_modified` exactly. When
817 /// it does not hold, send the whole representation.
818 ///
819 /// # Examples
820 ///
821 /// ```
822 /// use parse_net::{range::parse_if_range, EntityTag};
823 ///
824 /// let current = EntityTag::strong(b"v1")?;
825 /// assert!(parse_if_range(b"\"v1\"")?.matches(Some(¤t), None));
826 /// assert!(!parse_if_range(b"W/\"v1\"")?.matches(Some(¤t), None));
827 /// # Ok::<(), parse_net::ParseError>(())
828 /// ```
829 #[must_use]
830 pub fn matches(
831 &self,
832 current_tag: Option<&EntityTag<'_>>,
833 last_modified: Option<HttpDate>,
834 ) -> bool {
835 match self {
836 IfRange::Tag(t) => current_tag.is_some_and(|c| t.strong_eq(c)),
837 IfRange::Date(d) => last_modified == Some(*d),
838 }
839 }
840}
841
842/// Parses `If-Range`: an entity tag when the value starts with `W/` or `"`,
843/// an HTTP-date otherwise, in any of the three forms (RFC 9110 section 5.6.7).
844/// `now` (Unix seconds) places the two-digit year of an RFC 850 date. Use it
845/// over [`parse_if_range`] when a clock is at hand.
846///
847/// # Errors
848///
849/// The errors of [`EntityTag::parse`] or [`HttpDate::parse_at`].
850///
851/// # Examples
852///
853/// ```
854/// use parse_net::range::parse_if_range_at;
855///
856/// let now = 1_792_540_800;
857/// let imf = parse_if_range_at(b"Sun, 06 Nov 1994 08:49:37 GMT", now)?;
858/// assert_eq!(parse_if_range_at(b"Sunday, 06-Nov-94 08:49:37 GMT", now)?, imf);
859/// assert_eq!(parse_if_range_at(b"Sun Nov 6 08:49:37 1994", now)?, imf);
860/// # Ok::<(), parse_net::ParseError>(())
861/// ```
862pub fn parse_if_range_at(value: &[u8], now: i64) -> Result<IfRange<'_>> {
863 let v = trim_ows(value);
864 if v.starts_with(b"W/") || v.starts_with(b"\"") {
865 EntityTag::parse(value).map(IfRange::Tag)
866 } else {
867 HttpDate::parse_at(value, now).map(IfRange::Date)
868 }
869}
870
871/// Parses `If-Range` without a clock: an entity tag when the value starts
872/// with `W/` or `"`, an IMF-fixdate or asctime date otherwise. An RFC 850
873/// date needs the current time to place its year, so it takes
874/// [`parse_if_range_at`]; here it is [`ErrorKind::InvalidDate`].
875///
876/// # Errors
877///
878/// The errors of [`EntityTag::parse`] or [`HttpDate::parse_at`].
879///
880/// # Examples
881///
882/// ```
883/// use parse_net::{range::parse_if_range, IfRange};
884///
885/// assert!(matches!(parse_if_range(b"\"abc\"")?, IfRange::Tag(t) if t.opaque() == b"abc"));
886/// assert!(matches!(parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT")?, IfRange::Date(_)));
887/// assert!(parse_if_range(b"tomorrow").is_err());
888/// # Ok::<(), parse_net::ParseError>(())
889/// ```
890pub fn parse_if_range(value: &[u8]) -> Result<IfRange<'_>> {
891 let v = trim_ows(value);
892 if v.starts_with(b"W/") || v.starts_with(b"\"") {
893 EntityTag::parse(value).map(IfRange::Tag)
894 } else {
895 match HttpDate::parse_detailed(value, 0)? {
896 (_, crate::DateFormat::Rfc850) => err(ErrorKind::InvalidDate, skip_ows(value)),
897 (d, _) => Ok(IfRange::Date(d)),
898 }
899 }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905
906 fn resolve_str(
907 v: &[u8],
908 length: u64,
909 coalesce: bool,
910 ) -> (RangeOutcome, std::vec::Vec<ByteRange>) {
911 let mut out = [ByteRange { start: 0, end: 0 }; 32];
912 let h = parse(v, &Limits::DEFAULT).unwrap();
913 let o = resolve(&h, length, coalesce, &Limits::DEFAULT, &mut out);
914 let n = if let RangeOutcome::Ranges(n) = o {
915 n
916 } else {
917 0
918 };
919 (o, out[..n].to_vec())
920 }
921
922 #[test]
923 fn test_range_parse_edge_cases() {
924 let l = Limits::DEFAULT;
925 let k = |v: &[u8]| parse(v, &l).unwrap_err().kind();
926 assert!(parse(b"bytes=0-0", &l).is_ok());
927 assert!(parse(b"BYTES=0-1", &l).unwrap().is_bytes());
928 assert_eq!(k(b"bytes=5-4"), ErrorKind::InvalidRange);
929 assert_eq!(k(b"bytes = 0-1"), ErrorKind::InvalidRange);
930 assert_eq!(k(b"bytes= 0-1"), ErrorKind::InvalidRange);
931 assert_eq!(k(b"bytes=0 -1"), ErrorKind::InvalidNumber);
932 assert_eq!(k(b"bytes="), ErrorKind::InvalidRange);
933 assert_eq!(k(b"bytes=-"), ErrorKind::InvalidRange);
934 assert_eq!(
935 parse(b"bytes=0-18446744073709551616", &l)
936 .unwrap()
937 .specs()
938 .next(),
939 Some(RangeSpec::FromTo(0, u64::MAX))
940 );
941 // Past `u64::MAX` the order is still read from the digits.
942 assert_eq!(
943 k(b"bytes=18446744073709551617-18446744073709551616"),
944 ErrorKind::InvalidRange
945 );
946 assert!(parse(b"bytes=0-18446744073709551615", &l).is_ok());
947 assert!(parse(b"bytes=0-1,", &l).is_ok());
948 assert_eq!(k(b"=0-1"), ErrorKind::NotToken);
949 assert_eq!(k(b"bytes"), ErrorKind::InvalidRange);
950 assert!(parse(b"items=0-5", &l).is_ok());
951 assert_eq!(k(b"items=a b"), ErrorKind::InvalidRange);
952 assert_eq!(parse(b"items=0-5", &l).unwrap().specs().count(), 0);
953 }
954
955 #[test]
956 fn test_range_resolution() {
957 assert_eq!(
958 resolve_str(b"bytes=0-", 10, false).1,
959 [ByteRange { start: 0, end: 9 }]
960 );
961 assert_eq!(
962 resolve_str(b"bytes=-0", 10, false).0,
963 RangeOutcome::Unsatisfiable
964 );
965 assert_eq!(resolve_str(b"bytes=-1", 0, false).0, RangeOutcome::Ignore);
966 assert_eq!(
967 resolve_str(b"bytes=-20", 10, false).1,
968 [ByteRange { start: 0, end: 9 }]
969 );
970 assert_eq!(
971 resolve_str(b"bytes=5-100", 10, false).1,
972 [ByteRange { start: 5, end: 9 }]
973 );
974 assert_eq!(
975 resolve_str(b"bytes=10-", 10, false).0,
976 RangeOutcome::Unsatisfiable
977 );
978 assert_eq!(
979 resolve_str(b"bytes=10-, 0-0", 10, false).1,
980 [ByteRange { start: 0, end: 0 }]
981 );
982 assert_eq!(
983 resolve_str(b"bytes=0-18446744073709551615", u64::MAX, false).1[0].len(),
984 u64::MAX
985 );
986 }
987
988 /// `bytes=0-1,10-11,...` with `n` ranges.
989 fn many(n: u64) -> std::vec::Vec<u8> {
990 use std::fmt::Write as _;
991 let mut text = std::string::String::from("bytes=");
992 for i in 0..n {
993 let _ = write!(text, "{}-{},", i * 10, i * 10 + 1);
994 }
995 text.into_bytes()
996 }
997
998 #[test]
999 fn test_range_17_ranges_ignored() {
1000 assert_eq!(
1001 resolve_str(&many(16), 1000, false).0,
1002 RangeOutcome::Ranges(16)
1003 );
1004 assert_eq!(resolve_str(&many(17), 1000, false).0, RangeOutcome::Ignore);
1005 }
1006
1007 #[test]
1008 fn test_range_three_overlapping_ignored() {
1009 assert_eq!(
1010 resolve_str(b"bytes=0-10, 5-15", 100, false).0,
1011 RangeOutcome::Ranges(2)
1012 );
1013 assert_eq!(
1014 resolve_str(b"bytes=0-10, 5-15, 8-20", 100, false).0,
1015 RangeOutcome::Ignore
1016 );
1017 assert_eq!(
1018 resolve_str(b"bytes=50-60, 40-45, 30-35, 20-25", 100, false).0,
1019 RangeOutcome::Ignore
1020 );
1021 assert_eq!(
1022 resolve_str(b"bytes=50-60, 40-45, 30-35", 100, false).0,
1023 RangeOutcome::Ranges(3)
1024 );
1025 }
1026
1027 #[test]
1028 fn test_range_coalesce() {
1029 let (o, r) = resolve_str(b"bytes=40-49, 0-10, 11-20, 5-6", 100, true);
1030 assert_eq!(o, RangeOutcome::Ranges(2));
1031 assert_eq!(
1032 r,
1033 [
1034 ByteRange { start: 0, end: 20 },
1035 ByteRange { start: 40, end: 49 }
1036 ]
1037 );
1038 }
1039
1040 #[test]
1041 fn test_content_range_cases() {
1042 assert!(parse_content_range(b"bytes 0-0/1").is_ok());
1043 assert_eq!(
1044 parse_content_range(b"bytes 0-0/*").unwrap(),
1045 ContentRange::Satisfied {
1046 range: ByteRange { start: 0, end: 0 },
1047 complete: None
1048 }
1049 );
1050 assert_eq!(
1051 parse_content_range(b"bytes */0").unwrap(),
1052 ContentRange::Unsatisfied { complete: 0 }
1053 );
1054 for bad in [
1055 &b"bytes 1-0/5"[..],
1056 b"bytes 0-5/5",
1057 b"bytes0-1/2",
1058 b"items 0-1/2",
1059 b"bytes 0-1",
1060 b"bytes -1/2",
1061 b"bytes */*",
1062 ] {
1063 assert!(
1064 parse_content_range(bad).is_err(),
1065 "{}",
1066 std::string::String::from_utf8_lossy(bad)
1067 );
1068 }
1069 }
1070
1071 #[test]
1072 fn test_content_range_writers() {
1073 let mut buf = [0u8; 64];
1074 let mut out = crate::SliceSink::new(&mut buf);
1075 assert_eq!(
1076 write_content_range(ByteRange { start: 5, end: 4 }, None, &mut out),
1077 Err(WriteError::OutOfRange)
1078 );
1079 assert_eq!(
1080 write_content_range(ByteRange { start: 0, end: 5 }, Some(5), &mut out),
1081 Err(WriteError::OutOfRange)
1082 );
1083 write_content_range(ByteRange { start: 0, end: 4 }, None, &mut out).unwrap();
1084 assert_eq!(out.written(), b"bytes 0-4/*");
1085 assert_eq!(
1086 parse_content_range(out.written()).unwrap(),
1087 ContentRange::Satisfied {
1088 range: ByteRange { start: 0, end: 4 },
1089 complete: None
1090 }
1091 );
1092 let mut small = [0u8; 8];
1093 let mut out = crate::SliceSink::new(&mut small);
1094 assert_eq!(write_unsatisfied(1, &mut out), Err(WriteError::Full));
1095 assert!(out.is_empty());
1096 }
1097
1098 #[test]
1099 fn test_if_range() {
1100 let d = HttpDate::from_unix(784_111_777).unwrap();
1101 let date = parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT").unwrap();
1102 assert!(date.matches(None, Some(d)));
1103 assert!(!date.matches(None, None));
1104 assert!(parse_if_range(b"Sunday, 06-Nov-94 08:49:37 GMT").is_err());
1105 let t = EntityTag::strong(b"a").unwrap();
1106 assert!(!parse_if_range(b"\"b\"").unwrap().matches(Some(&t), Some(d)));
1107 }
1108}