Skip to content

Commit 7b2a205

Browse files
authored
Merge pull request #1370 from srijs/feat/byte-range-spec-satisfiable
feat(header): implement ByteRangeSpec::to_satisfiable_range
2 parents 19caf54 + bb54e36 commit 7b2a205

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

src/header/common/range.rs

+93
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,77 @@ pub enum ByteRangeSpec {
7676
Last(u64)
7777
}
7878

79+
impl ByteRangeSpec {
80+
/// Given the full length of the entity, attempt to normalize the byte range
81+
/// into an satisfiable end-inclusive (from, to) range.
82+
///
83+
/// The resulting range is guaranteed to be a satisfiable range within the bounds
84+
/// of `0 <= from <= to < full_length`.
85+
///
86+
/// If the byte range is deemed unsatisfiable, `None` is returned.
87+
/// An unsatisfiable range is generally cause for a server to either reject
88+
/// the client request with a `416 Range Not Satisfiable` status code, or to
89+
/// simply ignore the range header and serve the full entity using a `200 OK`
90+
/// status code.
91+
///
92+
/// This function closely follows [RFC 7233][1] section 2.1.
93+
/// As such, it considers ranges to be satisfiable if they meet the following
94+
/// conditions:
95+
///
96+
/// > If a valid byte-range-set includes at least one byte-range-spec with
97+
/// a first-byte-pos that is less than the current length of the
98+
/// representation, or at least one suffix-byte-range-spec with a
99+
/// non-zero suffix-length, then the byte-range-set is satisfiable.
100+
/// Otherwise, the byte-range-set is unsatisfiable.
101+
///
102+
/// The function also computes remainder ranges based on the RFC:
103+
///
104+
/// > If the last-byte-pos value is
105+
/// absent, or if the value is greater than or equal to the current
106+
/// length of the representation data, the byte range is interpreted as
107+
/// the remainder of the representation (i.e., the server replaces the
108+
/// value of last-byte-pos with a value that is one less than the current
109+
/// length of the selected representation).
110+
///
111+
/// [1]: https://tools.ietf.org/html/rfc7233
112+
pub fn to_satisfiable_range(&self, full_length: u64) -> Option<(u64, u64)> {
113+
// If the full length is zero, there is no satisfiable end-inclusive range.
114+
if full_length == 0 {
115+
return None;
116+
}
117+
match self {
118+
&ByteRangeSpec::FromTo(from, to) => {
119+
if from < full_length && from <= to {
120+
Some((from, ::std::cmp::min(to, full_length - 1)))
121+
} else {
122+
None
123+
}
124+
},
125+
&ByteRangeSpec::AllFrom(from) => {
126+
if from < full_length {
127+
Some((from, full_length - 1))
128+
} else {
129+
None
130+
}
131+
},
132+
&ByteRangeSpec::Last(last) => {
133+
if last > 0 {
134+
// From the RFC: If the selected representation is shorter
135+
// than the specified suffix-length,
136+
// the entire representation is used.
137+
if last > full_length {
138+
Some((0, full_length - 1))
139+
} else {
140+
Some((full_length - last, full_length - 1))
141+
}
142+
} else {
143+
None
144+
}
145+
}
146+
}
147+
}
148+
}
149+
79150
impl Range {
80151
/// Get the most common byte range header ("bytes=from-to")
81152
pub fn bytes(from: u64, to: u64) -> Range {
@@ -288,5 +359,27 @@ fn test_fmt() {
288359
assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n");
289360
}
290361

362+
#[test]
363+
fn test_byte_range_spec_to_satisfiable_range() {
364+
assert_eq!(Some((0, 0)), ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3));
365+
assert_eq!(Some((1, 2)), ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3));
366+
assert_eq!(Some((1, 2)), ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3));
367+
assert_eq!(None, ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3));
368+
assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3));
369+
assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0));
370+
371+
assert_eq!(Some((0, 2)), ByteRangeSpec::AllFrom(0).to_satisfiable_range(3));
372+
assert_eq!(Some((2, 2)), ByteRangeSpec::AllFrom(2).to_satisfiable_range(3));
373+
assert_eq!(None, ByteRangeSpec::AllFrom(3).to_satisfiable_range(3));
374+
assert_eq!(None, ByteRangeSpec::AllFrom(5).to_satisfiable_range(3));
375+
assert_eq!(None, ByteRangeSpec::AllFrom(0).to_satisfiable_range(0));
376+
377+
assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3));
378+
assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3));
379+
assert_eq!(Some((0, 2)), ByteRangeSpec::Last(5).to_satisfiable_range(3));
380+
assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3));
381+
assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0));
382+
}
383+
291384
bench_header!(bytes_multi, Range, { vec![b"bytes=1-1001,2001-3001,10001-".to_vec()]});
292385
bench_header!(custom_unit, Range, { vec![b"other=0-100000".to_vec()]});

0 commit comments

Comments
 (0)