Skip to main content

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(&current), None));
826    /// assert!(!parse_if_range(b"W/\"v1\"")?.matches(Some(&current), 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}