@@ -76,6 +76,77 @@ pub enum ByteRangeSpec {
76
76
Last ( u64 )
77
77
}
78
78
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
+
79
150
impl Range {
80
151
/// Get the most common byte range header ("bytes=from-to")
81
152
pub fn bytes ( from : u64 , to : u64 ) -> Range {
@@ -288,5 +359,27 @@ fn test_fmt() {
288
359
assert_eq ! ( & headers. to_string( ) , "Range: custom=1-xxx\r \n " ) ;
289
360
}
290
361
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
+
291
384
bench_header ! ( bytes_multi, Range , { vec![ b"bytes=1-1001,2001-3001,10001-" . to_vec( ) ] } ) ;
292
385
bench_header ! ( custom_unit, Range , { vec![ b"other=0-100000" . to_vec( ) ] } ) ;
0 commit comments