1#[derive(Debug, Clone, Copy)]
3pub struct HttpRange {
4 pub start: u64,
5 pub length: u64,
6}
7
8static PREFIX: &str = "bytes=";
9const PREFIX_LEN: usize = 6;
10
11impl HttpRange {
12 pub fn parse(header: &str, size: u64) -> Result<Vec<HttpRange>, ()> {
17 if header.is_empty() {
18 return Ok(Vec::new());
19 }
20 if !header.starts_with(PREFIX) {
21 return Err(());
22 }
23
24 let size_sig = size as i64;
25 let mut no_overlap = false;
26
27 let all_ranges: Vec<Option<HttpRange>> = header[PREFIX_LEN..]
28 .split(',')
29 .map(|x| x.trim())
30 .filter(|x| !x.is_empty())
31 .map(|ra| {
32 let mut start_end_iter = ra.split('-');
33
34 let start_str = start_end_iter.next().ok_or(())?.trim();
35 let end_str = start_end_iter.next().ok_or(())?.trim();
36
37 if start_str.is_empty() {
38 let mut length: i64 = end_str.parse().map_err(|_| ())?;
41
42 if length > size_sig {
43 length = size_sig;
44 }
45
46 Ok(Some(HttpRange {
47 start: (size_sig - length) as u64,
48 length: length as u64,
49 }))
50 } else {
51 let start: i64 = start_str.parse().map_err(|_| ())?;
52
53 if start < 0 {
54 return Err(());
55 }
56 if start >= size_sig {
57 no_overlap = true;
58 return Ok(None);
59 }
60
61 let length = if end_str.is_empty() {
62 size_sig - start
64 } else {
65 let mut end: i64 = end_str.parse().map_err(|_| ())?;
66
67 if start > end {
68 return Err(());
69 }
70
71 if end >= size_sig {
72 end = size_sig - 1;
73 }
74
75 end - start + 1
76 };
77
78 Ok(Some(HttpRange { start: start as u64, length: length as u64 }))
79 }
80 })
81 .collect::<Result<_, _>>()?;
82
83 let ranges: Vec<HttpRange> = all_ranges.into_iter().flatten().collect();
84
85 if no_overlap && ranges.is_empty() {
86 return Err(());
87 }
88
89 Ok(ranges)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 struct T(&'static str, u64, Vec<HttpRange>);
98
99 #[test]
100 fn test_parse() {
101 let tests = vec![
102 T("", 0, vec![]),
103 T("", 1000, vec![]),
104 T("foo", 0, vec![]),
105 T("bytes=", 0, vec![]),
106 T("bytes=7", 10, vec![]),
107 T("bytes= 7 ", 10, vec![]),
108 T("bytes=1-", 0, vec![]),
109 T("bytes=5-4", 10, vec![]),
110 T("bytes=0-2,5-4", 10, vec![]),
111 T("bytes=2-5,4-3", 10, vec![]),
112 T("bytes=--5,4--3", 10, vec![]),
113 T("bytes=A-", 10, vec![]),
114 T("bytes=A- ", 10, vec![]),
115 T("bytes=A-Z", 10, vec![]),
116 T("bytes= -Z", 10, vec![]),
117 T("bytes=5-Z", 10, vec![]),
118 T("bytes=Ran-dom, garbage", 10, vec![]),
119 T("bytes=0x01-0x02", 10, vec![]),
120 T("bytes= ", 10, vec![]),
121 T("bytes= , , , ", 10, vec![]),
122 T("bytes=0-9", 10, vec![HttpRange { start: 0, length: 10 }]),
123 T("bytes=0-", 10, vec![HttpRange { start: 0, length: 10 }]),
124 T("bytes=5-", 10, vec![HttpRange { start: 5, length: 5 }]),
125 T("bytes=0-20", 10, vec![HttpRange { start: 0, length: 10 }]),
126 T("bytes=15-,0-5", 10, vec![HttpRange { start: 0, length: 6 }]),
127 T(
128 "bytes=1-2,5-",
129 10,
130 vec![HttpRange { start: 1, length: 2 }, HttpRange { start: 5, length: 5 }],
131 ),
132 T(
133 "bytes=-2 , 7-",
134 11,
135 vec![HttpRange { start: 9, length: 2 }, HttpRange { start: 7, length: 4 }],
136 ),
137 T(
138 "bytes=0-0 ,2-2, 7-",
139 11,
140 vec![
141 HttpRange { start: 0, length: 1 },
142 HttpRange { start: 2, length: 1 },
143 HttpRange { start: 7, length: 4 },
144 ],
145 ),
146 T("bytes=-5", 10, vec![HttpRange { start: 5, length: 5 }]),
147 T("bytes=-15", 10, vec![HttpRange { start: 0, length: 10 }]),
148 T("bytes=0-499", 10000, vec![HttpRange { start: 0, length: 500 }]),
149 T("bytes=500-999", 10000, vec![HttpRange { start: 500, length: 500 }]),
150 T("bytes=-500", 10000, vec![HttpRange { start: 9500, length: 500 }]),
151 T("bytes=9500-", 10000, vec![HttpRange { start: 9500, length: 500 }]),
152 T(
153 "bytes=0-0,-1",
154 10000,
155 vec![HttpRange { start: 0, length: 1 }, HttpRange { start: 9999, length: 1 }],
156 ),
157 T(
158 "bytes=500-600,601-999",
159 10000,
160 vec![
161 HttpRange { start: 500, length: 101 },
162 HttpRange { start: 601, length: 399 },
163 ],
164 ),
165 T(
166 "bytes=500-700,601-999",
167 10000,
168 vec![
169 HttpRange { start: 500, length: 201 },
170 HttpRange { start: 601, length: 399 },
171 ],
172 ),
173 T(
175 "bytes= 1 -2 , 4- 5, 7 - 8 , ,,",
176 11,
177 vec![
178 HttpRange { start: 1, length: 2 },
179 HttpRange { start: 4, length: 2 },
180 HttpRange { start: 7, length: 2 },
181 ],
182 ),
183 ];
184
185 for t in tests {
186 let header = t.0;
187 let size = t.1;
188 let expected = t.2;
189
190 let res = HttpRange::parse(header, size);
191
192 if res.is_err() {
193 if expected.is_empty() {
194 continue;
195 } else {
196 panic!("parse({}, {}) returned error {:?}", header, size, res.unwrap_err());
197 }
198 }
199
200 let got = res.unwrap();
201
202 if got.len() != expected.len() {
203 panic!(
204 "len(parseRange({}, {})) = {}, want {}",
205 header,
206 size,
207 got.len(),
208 expected.len()
209 );
210 }
211
212 for i in 0..expected.len() {
213 if got[i].start != expected[i].start {
214 panic!(
215 "parseRange({}, {})[{}].start = {}, want {}",
216 header, size, i, got[i].start, expected[i].start
217 )
218 }
219 if got[i].length != expected[i].length {
220 panic!(
221 "parseRange({}, {})[{}].length = {}, want {}",
222 header, size, i, got[i].length, expected[i].length
223 )
224 }
225 }
226 }
227 }
228}