@@ -48,29 +48,52 @@ impl Range {
48
48
/// Creates a `Range` header from bounds.
49
49
pub fn bytes ( bounds : impl RangeBounds < u64 > ) -> Result < Self , InvalidRange > {
50
50
let v = match ( bounds. start_bound ( ) , bounds. end_bound ( ) ) {
51
- ( Bound :: Unbounded , Bound :: Included ( end) ) => format ! ( "bytes=-{}" , end) ,
52
- ( Bound :: Unbounded , Bound :: Excluded ( & end) ) => format ! ( "bytes=-{}" , end - 1 ) ,
53
51
( Bound :: Included ( start) , Bound :: Included ( end) ) => format ! ( "bytes={}-{}" , start, end) ,
54
52
( Bound :: Included ( start) , Bound :: Excluded ( & end) ) => {
55
53
format ! ( "bytes={}-{}" , start, end - 1 )
56
54
}
57
55
( Bound :: Included ( start) , Bound :: Unbounded ) => format ! ( "bytes={}-" , start) ,
56
+ // These do not directly translate.
57
+ //(Bound::Unbounded, Bound::Included(end)) => format!("bytes=-{}", end),
58
+ //(Bound::Unbounded, Bound::Excluded(&end)) => format!("bytes=-{}", end - 1),
58
59
_ => return Err ( InvalidRange { _inner : ( ) } ) ,
59
60
} ;
60
61
61
62
Ok ( Range ( :: HeaderValue :: from_str ( & v) . unwrap ( ) ) )
62
63
}
63
64
64
- /// Iterate the range sets as a tuple of bounds.
65
- pub fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = ( Bound < u64 > , Bound < u64 > ) > + ' a {
65
+ /// Iterate the range sets as a tuple of bounds, if valid with length.
66
+ ///
67
+ /// The length of the content is passed as an argument, and all ranges
68
+ /// that can be satisfied will be iterated.
69
+ pub fn satisfiable_ranges < ' a > (
70
+ & ' a self ,
71
+ len : u64 ,
72
+ ) -> impl Iterator < Item = ( Bound < u64 > , Bound < u64 > ) > + ' a {
66
73
let s = self
67
74
. 0
68
75
. to_str ( )
69
76
. expect ( "valid string checked in Header::decode()" ) ;
70
77
71
- s[ "bytes=" . len ( ) ..] . split ( ',' ) . filter_map ( |spec| {
78
+ s[ "bytes=" . len ( ) ..] . split ( ',' ) . filter_map ( move |spec| {
72
79
let mut iter = spec. trim ( ) . splitn ( 2 , '-' ) ;
73
- Some ( ( parse_bound ( iter. next ( ) ?) ?, parse_bound ( iter. next ( ) ?) ?) )
80
+ let start = parse_bound ( iter. next ( ) ?) ?;
81
+ let end = parse_bound ( iter. next ( ) ?) ?;
82
+
83
+ // Unbounded ranges in HTTP are actually a suffix
84
+ // For example, `-100` means the last 100 bytes.
85
+ if let Bound :: Unbounded = start {
86
+ if let Bound :: Included ( end) = end {
87
+ if len < end {
88
+ // Last N bytes is larger than available!
89
+ return None ;
90
+ }
91
+ return Some ( ( Bound :: Included ( len - end) , Bound :: Unbounded ) ) ;
92
+ }
93
+ // else fall through
94
+ }
95
+
96
+ Some ( ( start, end) )
74
97
} )
75
98
}
76
99
}
@@ -416,3 +439,17 @@ fn test_byte_range_spec_to_satisfiable_range() {
416
439
bench_header!(bytes_multi, Range, { vec![b"bytes=1-1001,2001-3001,10001-".to_vec()]});
417
440
bench_header!(custom_unit, Range, { vec![b"other=0-100000".to_vec()]});
418
441
*/
442
+
443
+ #[ test]
444
+ fn test_to_satisfiable_range_suffix ( ) {
445
+ let range = super :: test_decode :: < Range > ( & [ "bytes=-100" ] ) . unwrap ( ) ;
446
+ let bounds = range. satisfiable_ranges ( 350 ) . next ( ) . unwrap ( ) ;
447
+ assert_eq ! ( bounds, ( Bound :: Included ( 250 ) , Bound :: Unbounded ) ) ;
448
+ }
449
+
450
+ #[ test]
451
+ fn test_to_unsatisfiable_range_suffix ( ) {
452
+ let range = super :: test_decode :: < Range > ( & [ "bytes=-350" ] ) . unwrap ( ) ;
453
+ let bounds = range. satisfiable_ranges ( 100 ) . next ( ) ;
454
+ assert_eq ! ( bounds, None ) ;
455
+ }
0 commit comments