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/// Parses one `int-range / suffix-range`.
240/// The members of a range set: the text between commas, OWS trimmed, empty
241/// members skipped. The set is validated before it is walked.
242struct Elements<'a> {
243 s: &'a [u8],
244}
245
246impl<'a> Iterator for Elements<'a> {
247 type Item = &'a [u8];
248
249 fn next(&mut self) -> Option<&'a [u8]> {
250 loop {
251 if self.s.is_empty() {
252 return None;
253 }
254 let end = crate::scan::find_byte(b',', self.s).unwrap_or(self.s.len());
255 let e = trim_ows(self.s.get(..end).unwrap_or(&[]));
256 self.s = self.s.get(end + 1..).unwrap_or(&[]);
257 if !e.is_empty() {
258 return Some(e);
259 }
260 }
261 }
262}
263
264fn spec(e: &[u8]) -> Result<RangeSpec> {
265 let Some(dash) = e.iter().position(|&b| b == b'-') else {
266 return err(ErrorKind::InvalidRange, e.len());
267 };
268 let (first, last) = (
269 e.get(..dash).unwrap_or(&[]),
270 e.get(dash + 1..).unwrap_or(&[]),
271 );
272 match (first.is_empty(), last.is_empty()) {
273 (true, true) => err(ErrorKind::InvalidRange, 0),
274 (true, false) => Ok(RangeSpec::Suffix(
275 parse_u64(last).map_err(|x| x.shifted(dash + 1))?,
276 )),
277 (false, true) => Ok(RangeSpec::From(parse_u64(first)?)),
278 (false, false) => {
279 let f = parse_u64(first)?;
280 let l = parse_u64(last).map_err(|x| x.shifted(dash + 1))?;
281 if f > l {
282 return err(ErrorKind::InvalidRange, 0);
283 }
284 Ok(RangeSpec::FromTo(f, l))
285 }
286 }
287}
288
289/// Parses `range-unit "=" range-set` (RFC 9110 section 14.1.2).
290///
291/// The unit compares case-insensitively. A `bytes` set must hold only
292/// `first-last`, `first-`, and `-length` specs; another unit's set is
293/// checked as `other-range` bytes and not interpreted.
294///
295/// # Errors
296///
297/// [`ErrorKind::NotToken`] for the unit; [`ErrorKind::InvalidRange`] for a
298/// missing `=`, whitespace around it, an empty set, `first > last`, or a
299/// malformed spec; [`ErrorKind::InvalidNumber`], [`ErrorKind::NumberOverflow`];
300/// the list errors.
301///
302/// # Examples
303///
304/// ```
305/// use parse_net::{range, Limits, RangeSpec};
306///
307/// let h = range::parse(b"bytes=0-0, -5, 10-", &Limits::DEFAULT)?;
308/// assert_eq!(h.specs().collect::<Vec<_>>(), [RangeSpec::FromTo(0, 0), RangeSpec::Suffix(5), RangeSpec::From(10)]);
309/// assert!(range::parse(b"bytes=5-4", &Limits::DEFAULT).is_err());
310/// # Ok::<(), parse_net::ParseError>(())
311/// ```
312pub fn parse<'a>(value: &'a [u8], limits: &Limits) -> Result<RangeHeader<'a>> {
313 let lead = skip_ows(value);
314 let v = trim_ows(value);
315 let unit_len = v.iter().take_while(|&&b| class::is_tchar(b)).count();
316 if unit_len == 0 {
317 return err(ErrorKind::NotToken, lead);
318 }
319 if v.get(unit_len) != Some(&b'=') {
320 return err(ErrorKind::InvalidRange, lead + unit_len);
321 }
322 let unit = v.get(..unit_len).unwrap_or(&[]);
323 let set_at = unit_len + 1;
324 let set_text = v.get(set_at..).unwrap_or(&[]);
325 if set_text.first().is_some_and(|&b| crate::text::is_ows(b)) {
326 return err(ErrorKind::InvalidRange, lead + set_at);
327 }
328 // One pass over the set: each member is found by its comma, trimmed,
329 // and checked; empty members are skipped up to the limit.
330 let bytes = unit.eq_ignore_ascii_case(b"bytes");
331 let (mut i, mut count, mut empty) = (0usize, 0usize, 0usize);
332 while i < set_text.len() {
333 let rest = set_text.get(i..).unwrap_or(&[]);
334 let end = crate::scan::find_byte(b',', rest).unwrap_or(rest.len());
335 let raw = rest.get(..end).unwrap_or(&[]);
336 let e = trim_ows(raw);
337 let at = lead + set_at + i + skip_ows(raw);
338 if e.is_empty() {
339 empty += 1;
340 if empty > usize::from(limits.empty_elements) {
341 return err(ErrorKind::TooManyEmptyElements, at);
342 }
343 } else {
344 count += 1;
345 if count > usize::from(limits.list_members) {
346 return err(ErrorKind::TooManyMembers, at);
347 }
348 if bytes {
349 spec(e).map(|_| ()).map_err(|x| x.shifted(at))?;
350 } else if let Some(j) = e.iter().position(|&b| !class::is_vchar(b)) {
351 return Err(match e.get(j) {
352 Some(&b) if crate::scan::is_control(b) => {
353 crate::ParseError::new(crate::scan::control_kind(b), at + j)
354 }
355 _ => crate::ParseError::new(ErrorKind::InvalidRange, at + j),
356 });
357 }
358 }
359 i += end + 1;
360 }
361 if count == 0 {
362 return err(ErrorKind::InvalidRange, lead + set_at);
363 }
364 Ok(RangeHeader {
365 unit,
366 set: set_text,
367 count,
368 })
369}
370
371/// Resolves a parsed `Range` against a representation of `length` bytes.
372///
373/// Suffix ranges become absolute; a last position beyond the end is clamped;
374/// a range starting at or after the end, and a zero-length suffix, are
375/// dropped. More than [`Limits::max_ranges`] ranges, more than two ranges
376/// that overlap another, or more than two ranges that start before the one
377/// before them give [`RangeOutcome::Ignore`] (RFC 9110 section 14.2 allows
378/// ignoring abusive sets). With `coalesce`, overlapping and adjacent ranges
379/// are merged into ascending order (this changes the shape of a multipart
380/// response, so it is the caller's choice). An `out` too small for the
381/// ranges gives `Ignore`: the set is never silently truncated.
382///
383/// # Examples
384///
385/// ```
386/// use parse_net::{range, ByteRange, Limits, RangeOutcome};
387///
388/// let h = range::parse(b"bytes=0-10, 5-20, 40-49", &Limits::DEFAULT)?;
389/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
390/// assert_eq!(range::resolve(&h, 100, true, &Limits::DEFAULT, &mut out), RangeOutcome::Ranges(2));
391/// assert_eq!(out[..2], [ByteRange { start: 0, end: 20 }, ByteRange { start: 40, end: 49 }]);
392/// # Ok::<(), parse_net::ParseError>(())
393/// ```
394#[must_use]
395pub fn resolve(
396 h: &RangeHeader<'_>,
397 length: u64,
398 coalesce: bool,
399 limits: &Limits,
400 out: &mut [ByteRange],
401) -> RangeOutcome {
402 if !h.is_bytes() {
403 return RangeOutcome::NotBytes;
404 }
405 if h.len() > usize::from(limits.ranges) {
406 return RangeOutcome::Ignore;
407 }
408 let mut n = 0usize;
409 let mut descents = 0usize;
410 let mut previous_start: Option<u64> = None;
411 for s in h.specs() {
412 let Some(r) = absolute(s, length) else {
413 continue;
414 };
415 if previous_start.is_some_and(|p| r.start < p) {
416 descents += 1;
417 }
418 previous_start = Some(r.start);
419 match out.get_mut(n) {
420 Some(slot) => *slot = r,
421 None => return RangeOutcome::Ignore,
422 }
423 n += 1;
424 }
425 if n == 0 {
426 return RangeOutcome::Unsatisfiable;
427 }
428 let resolved = out.get_mut(..n).unwrap_or(&mut []);
429 let overlapping = resolved
430 .iter()
431 .enumerate()
432 .filter(|&(i, a)| {
433 resolved
434 .iter()
435 .enumerate()
436 .any(|(j, b)| i != j && a.start <= b.end && b.start <= a.end)
437 })
438 .count();
439 if overlapping > 2 || descents > 2 {
440 return RangeOutcome::Ignore;
441 }
442 if coalesce {
443 // Insertion sort: at most `max_ranges` entries.
444 for i in 1..resolved.len() {
445 let mut j = i;
446 while j > 0 {
447 let (Some(&a), Some(&b)) = (resolved.get(j - 1), resolved.get(j)) else {
448 break;
449 };
450 if a.start <= b.start {
451 break;
452 }
453 resolved.swap(j - 1, j);
454 j -= 1;
455 }
456 }
457 let mut merged = 0usize;
458 for i in 0..resolved.len() {
459 let Some(&r) = resolved.get(i) else { break };
460 match merged.checked_sub(1).and_then(|m| resolved.get_mut(m)) {
461 Some(last) if r.start <= last.end.saturating_add(1) => {
462 last.end = last.end.max(r.end);
463 }
464 _ => {
465 if let Some(slot) = resolved.get_mut(merged) {
466 *slot = r;
467 }
468 merged += 1;
469 }
470 }
471 }
472 n = merged;
473 }
474 RangeOutcome::Ranges(n)
475}
476
477/// The absolute range of `s` in a representation of `length` bytes, or
478/// `None` when it is unsatisfiable.
479fn absolute(s: RangeSpec, length: u64) -> Option<ByteRange> {
480 let last = length.checked_sub(1)?;
481 match s {
482 RangeSpec::From(f) if f <= last => Some(ByteRange {
483 start: f,
484 end: last,
485 }),
486 RangeSpec::FromTo(f, l) if f <= last => Some(ByteRange {
487 start: f,
488 end: l.min(last),
489 }),
490 RangeSpec::Suffix(n) if n > 0 => Some(ByteRange {
491 start: length - n.min(length),
492 end: last,
493 }),
494 _ => None,
495 }
496}
497
498/// Parses and resolves `Range` against `length` with the default limits and
499/// no coalescing.
500///
501/// # Errors
502///
503/// As [`parse`]. RFC 9110 lets a server ignore a `Range` it cannot read:
504/// treat an error as [`RangeOutcome::Ignore`] unless the request should be
505/// refused.
506///
507/// # Examples
508///
509/// ```
510/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
511///
512/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
513/// assert_eq!(byte_ranges(b"bytes=0-0, 2-2", 10, &mut out)?, RangeOutcome::Ignore); // `out` too small
514/// # Ok::<(), parse_net::ParseError>(())
515/// ```
516pub fn byte_ranges(value: &[u8], length: u64, out: &mut [ByteRange]) -> Result<RangeOutcome> {
517 let h = parse(value, &Limits::DEFAULT)?;
518 Ok(resolve(&h, length, false, &Limits::DEFAULT, out))
519}
520
521/// A parsed `Content-Range` value.
522///
523/// # Examples
524///
525/// ```
526/// use parse_net::{range::parse_content_range, ContentRange};
527///
528/// let ContentRange::Satisfied { range, complete } = parse_content_range(b"bytes 42-1233/*")? else { unreachable!() };
529/// assert_eq!((range.len(), complete), (1192, None));
530/// # Ok::<(), parse_net::ParseError>(())
531/// ```
532#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
533pub enum ContentRange {
534 /// `bytes first-last/complete` or `bytes first-last/*`.
535 Satisfied {
536 /// The range sent.
537 range: ByteRange,
538 /// The complete length, when known.
539 complete: Option<u64>,
540 },
541 /// `bytes */complete`, sent with 416.
542 Unsatisfied {
543 /// The complete length.
544 complete: u64,
545 },
546}
547
548/// Parses `Content-Range` (RFC 9110 section 14.4) for the `bytes` unit.
549///
550/// # Errors
551///
552/// [`ErrorKind::InvalidRange`] (another unit, a missing space, `last < first`,
553/// `last >= complete`, a malformed shape); [`ErrorKind::InvalidNumber`],
554/// [`ErrorKind::NumberOverflow`].
555///
556/// # Examples
557///
558/// ```
559/// use parse_net::{range::parse_content_range, ByteRange, ContentRange};
560///
561/// assert_eq!(parse_content_range(b"bytes 0-499/1234")?,
562/// ContentRange::Satisfied { range: ByteRange { start: 0, end: 499 }, complete: Some(1234) });
563/// assert_eq!(parse_content_range(b"bytes */0")?, ContentRange::Unsatisfied { complete: 0 });
564/// assert!(parse_content_range(b"bytes 0-5/5").is_err());
565/// # Ok::<(), parse_net::ParseError>(())
566/// ```
567pub fn parse_content_range(value: &[u8]) -> Result<ContentRange> {
568 let lead = skip_ows(value);
569 let v = trim_ows(value);
570 inner_content_range(v).map_err(|e| e.shifted(lead))
571}
572
573fn inner_content_range(v: &[u8]) -> Result<ContentRange> {
574 let unit = v.get(..5).unwrap_or(&[]);
575 if !unit.eq_ignore_ascii_case(b"bytes") || v.get(5) != Some(&b' ') {
576 return err(ErrorKind::InvalidRange, 0);
577 }
578 let rest = v.get(6..).unwrap_or(&[]);
579 let Some(slash) = rest.iter().position(|&b| b == b'/') else {
580 return err(ErrorKind::InvalidRange, v.len());
581 };
582 let (range_text, complete_text) = (
583 rest.get(..slash).unwrap_or(&[]),
584 rest.get(slash + 1..).unwrap_or(&[]),
585 );
586 let complete_at = 6 + slash + 1;
587 if range_text == b"*" {
588 let complete = parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?;
589 return Ok(ContentRange::Unsatisfied { complete });
590 }
591 let complete = if complete_text == b"*" {
592 None
593 } else {
594 Some(parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?)
595 };
596 let Some(dash) = range_text.iter().position(|&b| b == b'-') else {
597 return err(ErrorKind::InvalidRange, 6);
598 };
599 let first = parse_u64(range_text.get(..dash).unwrap_or(&[])).map_err(|e| e.shifted(6))?;
600 let last = parse_u64(range_text.get(dash + 1..).unwrap_or(&[]))
601 .map_err(|e| e.shifted(6 + dash + 1))?;
602 if last < first || complete.is_some_and(|c| last >= c) {
603 return err(ErrorKind::InvalidRange, 6);
604 }
605 Ok(ContentRange::Satisfied {
606 range: ByteRange {
607 start: first,
608 end: last,
609 },
610 complete,
611 })
612}
613
614fn put_content_range(
615 r: ByteRange,
616 complete: Option<u64>,
617 out: &mut impl Sink,
618) -> core::result::Result<(), WriteError> {
619 out.put(b"bytes ")?;
620 write_decimal_u64(r.start, out)?;
621 out.put(b"-")?;
622 write_decimal_u64(r.end, out)?;
623 out.put(b"/")?;
624 match complete {
625 Some(c) => write_decimal_u64(c, out)?,
626 None => out.put(b"*")?,
627 }
628 Ok(())
629}
630
631/// Writes `bytes first-last/complete`, or `bytes first-last/*` when the
632/// complete length is unknown.
633///
634/// # Errors
635///
636/// [`WriteError::OutOfRange`] when `end < start` or `end >= complete`;
637/// [`WriteError::Full`] (nothing written when the sink reports its room).
638///
639/// # Examples
640///
641/// ```
642/// use parse_net::{range::write_content_range, ByteRange, SliceSink, WriteError};
643///
644/// let mut buf = [0u8; 32];
645/// let mut out = SliceSink::new(&mut buf);
646/// write_content_range(ByteRange { start: 0, end: 9 }, None, &mut out)?;
647/// assert_eq!(out.written(), b"bytes 0-9/*");
648/// assert_eq!(write_content_range(ByteRange { start: 0, end: 9 }, Some(5), &mut out), Err(WriteError::OutOfRange));
649/// # Ok::<(), parse_net::WriteError>(())
650/// ```
651pub fn write_content_range(
652 r: ByteRange,
653 complete: Option<u64>,
654 out: &mut impl Sink,
655) -> core::result::Result<(), WriteError> {
656 if r.end < r.start || complete.is_some_and(|c| r.end >= c) {
657 return Err(WriteError::OutOfRange);
658 }
659 let mut count = CountSink::new();
660 put_content_range(r, complete, &mut count)?;
661 reserve(out, count.count())?;
662 put_content_range(r, complete, out)
663}
664
665/// Writes `bytes */complete`, for a 416 response.
666///
667/// # Errors
668///
669/// [`WriteError::Full`] (nothing written when the sink reports its room).
670///
671/// # Examples
672///
673/// ```
674/// use parse_net::{range::write_unsatisfied, SliceSink};
675///
676/// let mut buf = [0u8; 32];
677/// let mut out = SliceSink::new(&mut buf);
678/// write_unsatisfied(1234, &mut out).expect("room");
679/// assert_eq!(out.written(), b"bytes */1234");
680/// ```
681pub fn write_unsatisfied(
682 complete: u64,
683 out: &mut impl Sink,
684) -> core::result::Result<(), WriteError> {
685 let mut count = CountSink::new();
686 count.put(b"bytes */")?;
687 write_decimal_u64(complete, &mut count)?;
688 reserve(out, count.count())?;
689 out.put(b"bytes */")?;
690 write_decimal_u64(complete, out)
691}
692
693/// A parsed `If-Range` value.
694///
695/// # Examples
696///
697/// ```
698/// use parse_net::{range::parse_if_range, HttpDate, IfRange};
699///
700/// let date = b"Wed, 21 Oct 2026 07:28:00 GMT";
701/// let cond = parse_if_range(date)?;
702/// assert_eq!(cond, IfRange::Date(HttpDate::parse_imf_fixdate(date)?));
703/// assert!(cond.matches(None, Some(HttpDate::parse_imf_fixdate(date)?)));
704/// # Ok::<(), parse_net::ParseError>(())
705/// ```
706#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
707pub enum IfRange<'a> {
708 /// An HTTP-date.
709 Date(HttpDate),
710 /// An entity tag.
711 Tag(EntityTag<'a>),
712}
713
714impl IfRange<'_> {
715 /// Whether the condition holds, so the ranges may be served (RFC 9110
716 /// section 13.1.5): a tag matches by strong comparison (a weak tag never
717 /// matches); a date matches when it equals `last_modified` exactly. When
718 /// it does not hold, send the whole representation.
719 ///
720 /// # Examples
721 ///
722 /// ```
723 /// use parse_net::{range::parse_if_range, EntityTag};
724 ///
725 /// let current = EntityTag::strong(b"v1")?;
726 /// assert!(parse_if_range(b"\"v1\"")?.matches(Some(¤t), None));
727 /// assert!(!parse_if_range(b"W/\"v1\"")?.matches(Some(¤t), None));
728 /// # Ok::<(), parse_net::ParseError>(())
729 /// ```
730 #[must_use]
731 pub fn matches(
732 &self,
733 current_tag: Option<&EntityTag<'_>>,
734 last_modified: Option<HttpDate>,
735 ) -> bool {
736 match self {
737 IfRange::Tag(t) => current_tag.is_some_and(|c| t.strong_eq(c)),
738 IfRange::Date(d) => last_modified == Some(*d),
739 }
740 }
741}
742
743/// Parses `If-Range`: an entity tag when the value starts with `W/` or `"`,
744/// an IMF-fixdate otherwise.
745///
746/// # Errors
747///
748/// The errors of [`EntityTag::parse`] or [`HttpDate::parse_imf_fixdate`].
749///
750/// # Examples
751///
752/// ```
753/// use parse_net::{range::parse_if_range, IfRange};
754///
755/// assert!(matches!(parse_if_range(b"\"abc\"")?, IfRange::Tag(t) if t.opaque() == b"abc"));
756/// assert!(matches!(parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT")?, IfRange::Date(_)));
757/// assert!(parse_if_range(b"tomorrow").is_err());
758/// # Ok::<(), parse_net::ParseError>(())
759/// ```
760pub fn parse_if_range(value: &[u8]) -> Result<IfRange<'_>> {
761 let v = trim_ows(value);
762 if v.starts_with(b"W/") || v.starts_with(b"\"") {
763 EntityTag::parse(value).map(IfRange::Tag)
764 } else {
765 HttpDate::parse_imf_fixdate(value).map(IfRange::Date)
766 }
767}
768
769#[cfg(test)]
770mod tests {
771 use super::*;
772
773 fn resolve_str(
774 v: &[u8],
775 length: u64,
776 coalesce: bool,
777 ) -> (RangeOutcome, std::vec::Vec<ByteRange>) {
778 let mut out = [ByteRange { start: 0, end: 0 }; 32];
779 let h = parse(v, &Limits::DEFAULT).unwrap();
780 let o = resolve(&h, length, coalesce, &Limits::DEFAULT, &mut out);
781 let n = if let RangeOutcome::Ranges(n) = o {
782 n
783 } else {
784 0
785 };
786 (o, out[..n].to_vec())
787 }
788
789 #[test]
790 fn test_range_parse_edge_cases() {
791 let l = Limits::DEFAULT;
792 let k = |v: &[u8]| parse(v, &l).unwrap_err().kind();
793 assert!(parse(b"bytes=0-0", &l).is_ok());
794 assert!(parse(b"BYTES=0-1", &l).unwrap().is_bytes());
795 assert_eq!(k(b"bytes=5-4"), ErrorKind::InvalidRange);
796 assert_eq!(k(b"bytes = 0-1"), ErrorKind::InvalidRange);
797 assert_eq!(k(b"bytes= 0-1"), ErrorKind::InvalidRange);
798 assert_eq!(k(b"bytes=0 -1"), ErrorKind::InvalidNumber);
799 assert_eq!(k(b"bytes="), ErrorKind::InvalidRange);
800 assert_eq!(k(b"bytes=-"), ErrorKind::InvalidRange);
801 assert_eq!(
802 k(b"bytes=0-18446744073709551616"),
803 ErrorKind::NumberOverflow
804 );
805 assert!(parse(b"bytes=0-18446744073709551615", &l).is_ok());
806 assert!(parse(b"bytes=0-1,", &l).is_ok());
807 assert_eq!(k(b"=0-1"), ErrorKind::NotToken);
808 assert_eq!(k(b"bytes"), ErrorKind::InvalidRange);
809 assert!(parse(b"items=0-5", &l).is_ok());
810 assert_eq!(k(b"items=a b"), ErrorKind::InvalidRange);
811 assert_eq!(parse(b"items=0-5", &l).unwrap().specs().count(), 0);
812 }
813
814 #[test]
815 fn test_range_resolution() {
816 assert_eq!(
817 resolve_str(b"bytes=0-", 10, false).1,
818 [ByteRange { start: 0, end: 9 }]
819 );
820 assert_eq!(
821 resolve_str(b"bytes=-0", 10, false).0,
822 RangeOutcome::Unsatisfiable
823 );
824 assert_eq!(
825 resolve_str(b"bytes=-1", 0, false).0,
826 RangeOutcome::Unsatisfiable
827 );
828 assert_eq!(
829 resolve_str(b"bytes=-20", 10, false).1,
830 [ByteRange { start: 0, end: 9 }]
831 );
832 assert_eq!(
833 resolve_str(b"bytes=5-100", 10, false).1,
834 [ByteRange { start: 5, end: 9 }]
835 );
836 assert_eq!(
837 resolve_str(b"bytes=10-", 10, false).0,
838 RangeOutcome::Unsatisfiable
839 );
840 assert_eq!(
841 resolve_str(b"bytes=10-, 0-0", 10, false).1,
842 [ByteRange { start: 0, end: 0 }]
843 );
844 assert_eq!(
845 resolve_str(b"bytes=0-18446744073709551615", u64::MAX, false).1[0].len(),
846 u64::MAX
847 );
848 }
849
850 /// `bytes=0-1,10-11,...` with `n` ranges.
851 fn many(n: u64) -> std::vec::Vec<u8> {
852 use std::fmt::Write as _;
853 let mut text = std::string::String::from("bytes=");
854 for i in 0..n {
855 let _ = write!(text, "{}-{},", i * 10, i * 10 + 1);
856 }
857 text.into_bytes()
858 }
859
860 #[test]
861 fn test_range_17_ranges_ignored() {
862 assert_eq!(
863 resolve_str(&many(16), 1000, false).0,
864 RangeOutcome::Ranges(16)
865 );
866 assert_eq!(resolve_str(&many(17), 1000, false).0, RangeOutcome::Ignore);
867 }
868
869 #[test]
870 fn test_range_three_overlapping_ignored() {
871 assert_eq!(
872 resolve_str(b"bytes=0-10, 5-15", 100, false).0,
873 RangeOutcome::Ranges(2)
874 );
875 assert_eq!(
876 resolve_str(b"bytes=0-10, 5-15, 8-20", 100, false).0,
877 RangeOutcome::Ignore
878 );
879 assert_eq!(
880 resolve_str(b"bytes=50-60, 40-45, 30-35, 20-25", 100, false).0,
881 RangeOutcome::Ignore
882 );
883 assert_eq!(
884 resolve_str(b"bytes=50-60, 40-45, 30-35", 100, false).0,
885 RangeOutcome::Ranges(3)
886 );
887 }
888
889 #[test]
890 fn test_range_coalesce() {
891 let (o, r) = resolve_str(b"bytes=40-49, 0-10, 11-20, 5-6", 100, true);
892 assert_eq!(o, RangeOutcome::Ranges(2));
893 assert_eq!(
894 r,
895 [
896 ByteRange { start: 0, end: 20 },
897 ByteRange { start: 40, end: 49 }
898 ]
899 );
900 }
901
902 #[test]
903 fn test_content_range_cases() {
904 assert!(parse_content_range(b"bytes 0-0/1").is_ok());
905 assert_eq!(
906 parse_content_range(b"bytes 0-0/*").unwrap(),
907 ContentRange::Satisfied {
908 range: ByteRange { start: 0, end: 0 },
909 complete: None
910 }
911 );
912 assert_eq!(
913 parse_content_range(b"bytes */0").unwrap(),
914 ContentRange::Unsatisfied { complete: 0 }
915 );
916 for bad in [
917 &b"bytes 1-0/5"[..],
918 b"bytes 0-5/5",
919 b"bytes0-1/2",
920 b"items 0-1/2",
921 b"bytes 0-1",
922 b"bytes -1/2",
923 b"bytes */*",
924 ] {
925 assert!(
926 parse_content_range(bad).is_err(),
927 "{}",
928 std::string::String::from_utf8_lossy(bad)
929 );
930 }
931 }
932
933 #[test]
934 fn test_content_range_writers() {
935 let mut buf = [0u8; 64];
936 let mut out = crate::SliceSink::new(&mut buf);
937 assert_eq!(
938 write_content_range(ByteRange { start: 5, end: 4 }, None, &mut out),
939 Err(WriteError::OutOfRange)
940 );
941 assert_eq!(
942 write_content_range(ByteRange { start: 0, end: 5 }, Some(5), &mut out),
943 Err(WriteError::OutOfRange)
944 );
945 write_content_range(ByteRange { start: 0, end: 4 }, None, &mut out).unwrap();
946 assert_eq!(out.written(), b"bytes 0-4/*");
947 assert_eq!(
948 parse_content_range(out.written()).unwrap(),
949 ContentRange::Satisfied {
950 range: ByteRange { start: 0, end: 4 },
951 complete: None
952 }
953 );
954 let mut small = [0u8; 8];
955 let mut out = crate::SliceSink::new(&mut small);
956 assert_eq!(write_unsatisfied(1, &mut out), Err(WriteError::Full));
957 assert!(out.is_empty());
958 }
959
960 #[test]
961 fn test_if_range() {
962 let d = HttpDate::from_unix(784_111_777).unwrap();
963 let date = parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT").unwrap();
964 assert!(date.matches(None, Some(d)));
965 assert!(!date.matches(None, None));
966 assert!(parse_if_range(b"Sunday, 06-Nov-94 08:49:37 GMT").is_err());
967 let t = EntityTag::strong(b"a").unwrap();
968 assert!(!parse_if_range(b"\"b\"").unwrap().matches(Some(&t), Some(d)));
969 }
970}