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