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    list,
30    num::parse_u64,
31    text::{skip_ows, trim_ows},
32    write::{CountSink, Sink, reserve, write_decimal_u64},
33};
34
35/// One `range-spec` of a `bytes` range set.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37pub enum RangeSpec {
38    /// `first-` : from `first` to the end.
39    From(u64),
40    /// `first-last`, both inclusive.
41    FromTo(u64, u64),
42    /// `-length`: the last `length` bytes.
43    Suffix(u64),
44}
45
46/// A resolved byte range, both ends inclusive.
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
48pub struct ByteRange {
49    /// The first byte.
50    pub start: u64,
51    /// The last byte (inclusive).
52    pub end: u64,
53}
54
55impl ByteRange {
56    /// The number of bytes, `end - start + 1`, saturating at `u64::MAX`; zero
57    /// when `end < start`.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// assert_eq!(parse_net::ByteRange { start: 0, end: 499 }.len(), 500);
63    /// ```
64    #[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    /// Whether the range holds no bytes (only possible when `end < start`).
74    #[must_use]
75    pub const fn is_empty(&self) -> bool {
76        self.end < self.start
77    }
78}
79
80/// What to do with a `Range` request.
81#[non_exhaustive]
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83pub enum RangeOutcome {
84    /// Serve these ranges (206): the first `n` entries of the output.
85    Ranges(usize),
86    /// No range is satisfiable: answer 416 with `Content-Range: bytes */length`.
87    Unsatisfiable,
88    /// Ignore the field and serve the whole representation (200): the set is
89    /// abusive (too many ranges, too many overlapping, too much out of order)
90    /// or does not fit the output.
91    Ignore,
92    /// Another range unit: ignore the field and serve the whole
93    /// representation.
94    NotBytes,
95}
96
97/// A validated `Range` value.
98#[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    /// Whether the unit is `bytes` (case-insensitive).
106    #[must_use]
107    pub fn is_bytes(&self) -> bool {
108        self.unit.eq_ignore_ascii_case(b"bytes")
109    }
110
111    /// The range unit, as sent.
112    #[must_use]
113    pub const fn unit(&self) -> &'a [u8] {
114        self.unit
115    }
116
117    /// The range specs, in request order; empty unless the unit is `bytes`.
118    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    /// The number of members in the range set, whatever the unit (so it can
127    /// be nonzero when [`specs`](Self::specs) is empty).
128    #[must_use]
129    pub const fn len(&self) -> usize {
130        self.set.len()
131    }
132
133    /// Whether the set is empty (never true for a parsed value).
134    #[must_use]
135    pub const fn is_empty(&self) -> bool {
136        self.set.is_empty()
137    }
138}
139
140/// Parses one `int-range / suffix-range`.
141fn 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
166/// Parses `range-unit "=" range-set` (RFC 9110 section 14.1.2).
167///
168/// The unit compares case-insensitively. A `bytes` set must hold only
169/// `first-last`, `first-`, and `-length` specs; another unit's set is
170/// checked as `other-range` bytes and not interpreted.
171///
172/// # Errors
173///
174/// [`ErrorKind::NotToken`] for the unit; [`ErrorKind::InvalidRange`] for a
175/// missing `=`, whitespace around it, an empty set, `first > last`, or a
176/// malformed spec; [`ErrorKind::InvalidNumber`], [`ErrorKind::NumberOverflow`];
177/// the list errors.
178///
179/// # Examples
180///
181/// ```
182/// use parse_net::{range, Limits, RangeSpec};
183///
184/// let h = range::parse(b"bytes=0-0, -5, 10-", &Limits::DEFAULT)?;
185/// assert_eq!(h.specs().collect::<Vec<_>>(), [RangeSpec::FromTo(0, 0), RangeSpec::Suffix(5), RangeSpec::From(10)]);
186/// assert!(range::parse(b"bytes=5-4", &Limits::DEFAULT).is_err());
187/// # Ok::<(), parse_net::ParseError>(())
188/// ```
189pub 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/// Resolves a parsed `Range` against a representation of `length` bytes.
222///
223/// Suffix ranges become absolute; a last position beyond the end is clamped;
224/// a range starting at or after the end, and a zero-length suffix, are
225/// dropped. More than [`Limits::max_ranges`] ranges, more than two ranges
226/// that overlap another, or more than two ranges that start before the one
227/// before them give [`RangeOutcome::Ignore`] (RFC 9110 section 14.2 allows
228/// ignoring abusive sets). With `coalesce`, overlapping and adjacent ranges
229/// are merged into ascending order (this changes the shape of a multipart
230/// response, so it is the caller's choice). An `out` too small for the
231/// ranges gives `Ignore`: the set is never silently truncated.
232///
233/// # Examples
234///
235/// ```
236/// use parse_net::{range, ByteRange, Limits, RangeOutcome};
237///
238/// let h = range::parse(b"bytes=0-10, 5-20, 40-49", &Limits::DEFAULT)?;
239/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
240/// assert_eq!(range::resolve(&h, 100, true, &Limits::DEFAULT, &mut out), RangeOutcome::Ranges(2));
241/// assert_eq!(out[..2], [ByteRange { start: 0, end: 20 }, ByteRange { start: 40, end: 49 }]);
242/// # Ok::<(), parse_net::ParseError>(())
243/// ```
244#[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        // Insertion sort: at most `max_ranges` entries.
294        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
327/// The absolute range of `s` in a representation of `length` bytes, or
328/// `None` when it is unsatisfiable.
329fn 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
348/// Parses and resolves `Range` against `length` with the default limits and
349/// no coalescing.
350///
351/// # Errors
352///
353/// As [`parse`]. RFC 9110 lets a server ignore a `Range` it cannot read:
354/// treat an error as [`RangeOutcome::Ignore`] unless the request should be
355/// refused.
356///
357/// # Examples
358///
359/// ```
360/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
361///
362/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
363/// assert_eq!(byte_ranges(b"bytes=0-0, 2-2", 10, &mut out)?, RangeOutcome::Ignore); // `out` too small
364/// # Ok::<(), parse_net::ParseError>(())
365/// ```
366pub 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/// A parsed `Content-Range` value.
372#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
373pub enum ContentRange {
374    /// `bytes first-last/complete` or `bytes first-last/*`.
375    Satisfied {
376        /// The range sent.
377        range: ByteRange,
378        /// The complete length, when known.
379        complete: Option<u64>,
380    },
381    /// `bytes */complete`, sent with 416.
382    Unsatisfied {
383        /// The complete length.
384        complete: u64,
385    },
386}
387
388/// Parses `Content-Range` (RFC 9110 section 14.4) for the `bytes` unit.
389///
390/// # Errors
391///
392/// [`ErrorKind::InvalidRange`] (another unit, a missing space, `last < first`,
393/// `last >= complete`, a malformed shape); [`ErrorKind::InvalidNumber`],
394/// [`ErrorKind::NumberOverflow`].
395///
396/// # Examples
397///
398/// ```
399/// use parse_net::{range::parse_content_range, ByteRange, ContentRange};
400///
401/// assert_eq!(parse_content_range(b"bytes 0-499/1234")?,
402///            ContentRange::Satisfied { range: ByteRange { start: 0, end: 499 }, complete: Some(1234) });
403/// assert_eq!(parse_content_range(b"bytes */0")?, ContentRange::Unsatisfied { complete: 0 });
404/// assert!(parse_content_range(b"bytes 0-5/5").is_err());
405/// # Ok::<(), parse_net::ParseError>(())
406/// ```
407pub 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
471/// Writes `bytes first-last/complete`, or `bytes first-last/*` when the
472/// complete length is unknown.
473///
474/// # Errors
475///
476/// [`WriteError::OutOfRange`] when `end < start` or `end >= complete`;
477/// [`WriteError::Full`] (nothing written when the sink reports its room).
478pub 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
492/// Writes `bytes */complete`, for a 416 response.
493///
494/// # Errors
495///
496/// [`WriteError::Full`] (nothing written when the sink reports its room).
497///
498/// # Examples
499///
500/// ```
501/// use parse_net::{range::write_unsatisfied, SliceSink};
502///
503/// let mut buf = [0u8; 32];
504/// let mut out = SliceSink::new(&mut buf);
505/// write_unsatisfied(1234, &mut out).expect("room");
506/// assert_eq!(out.written(), b"bytes */1234");
507/// ```
508pub 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/// A parsed `If-Range` value.
521#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
522pub enum IfRange<'a> {
523    /// An HTTP-date.
524    Date(HttpDate),
525    /// An entity tag.
526    Tag(EntityTag<'a>),
527}
528
529impl IfRange<'_> {
530    /// Whether the condition holds, so the ranges may be served (RFC 9110
531    /// section 13.1.5): a tag matches by strong comparison (a weak tag never
532    /// matches); a date matches when it equals `last_modified` exactly. When
533    /// it does not hold, send the whole representation.
534    ///
535    /// # Examples
536    ///
537    /// ```
538    /// use parse_net::{range::parse_if_range, EntityTag};
539    ///
540    /// let current = EntityTag::strong(b"v1")?;
541    /// assert!(parse_if_range(b"\"v1\"")?.matches(Some(&current), None));
542    /// assert!(!parse_if_range(b"W/\"v1\"")?.matches(Some(&current), None));
543    /// # Ok::<(), parse_net::ParseError>(())
544    /// ```
545    #[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
558/// Parses `If-Range`: an entity tag when the value starts with `W/` or `"`,
559/// an IMF-fixdate otherwise.
560///
561/// # Errors
562///
563/// The errors of [`EntityTag::parse`] or [`HttpDate::parse_imf_fixdate`].
564pub 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    /// `bytes=0-1,10-11,...` with `n` ranges.
655    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}