1use std::fmt;
2
3use derive_more::Error;
4
5#[derive(Debug, Clone)]
7enum HttpRangeParseError {
8 InvalidRange,
9 NoOverlap,
10}
11
12impl From<http_range::HttpRangeParseError> for HttpRangeParseError {
13 fn from(err: http_range::HttpRangeParseError) -> Self {
14 match err {
15 http_range::HttpRangeParseError::InvalidRange => Self::InvalidRange,
16 http_range::HttpRangeParseError::NoOverlap => Self::NoOverlap,
17 }
18 }
19}
20
21#[derive(Debug, Clone, Error)]
22#[non_exhaustive]
23pub struct ParseRangeErr(#[error(not(source))] HttpRangeParseError);
24
25impl fmt::Display for ParseRangeErr {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.write_str("invalid Range header: ")?;
28 f.write_str(match self.0 {
29 HttpRangeParseError::InvalidRange => "invalid syntax",
30 HttpRangeParseError::NoOverlap => "range starts after end of content",
31 })
32 }
33}
34
35#[derive(Debug, Clone, Copy)]
37pub struct HttpRange {
38 pub start: u64,
40
41 pub length: u64,
43}
44
45impl HttpRange {
46 pub fn parse(header: &str, size: u64) -> Result<Vec<HttpRange>, ParseRangeErr> {
51 let ranges =
52 http_range::HttpRange::parse(header, size).map_err(|err| ParseRangeErr(err.into()))?;
53
54 Ok(ranges
55 .iter()
56 .map(|range| HttpRange {
57 start: range.start,
58 length: range.length,
59 })
60 .collect())
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 struct T(&'static str, u64, Vec<HttpRange>);
69
70 #[test]
71 fn test_parse() {
72 let tests = vec![
73 T("", 0, vec![]),
74 T("", 1000, vec![]),
75 T("foo", 0, vec![]),
76 T("bytes=", 0, vec![]),
77 T("bytes=7", 10, vec![]),
78 T("bytes= 7 ", 10, vec![]),
79 T("bytes=1-", 0, vec![]),
80 T("bytes=5-4", 10, vec![]),
81 T("bytes=0-2,5-4", 10, vec![]),
82 T("bytes=2-5,4-3", 10, vec![]),
83 T("bytes=--5,4--3", 10, vec![]),
84 T("bytes=A-", 10, vec![]),
85 T("bytes=A- ", 10, vec![]),
86 T("bytes=A-Z", 10, vec![]),
87 T("bytes= -Z", 10, vec![]),
88 T("bytes=5-Z", 10, vec![]),
89 T("bytes=Ran-dom, garbage", 10, vec![]),
90 T("bytes=0x01-0x02", 10, vec![]),
91 T("bytes= ", 10, vec![]),
92 T("bytes= , , , ", 10, vec![]),
93 T(
94 "bytes=0-9",
95 10,
96 vec![HttpRange {
97 start: 0,
98 length: 10,
99 }],
100 ),
101 T(
102 "bytes=0-",
103 10,
104 vec![HttpRange {
105 start: 0,
106 length: 10,
107 }],
108 ),
109 T(
110 "bytes=5-",
111 10,
112 vec![HttpRange {
113 start: 5,
114 length: 5,
115 }],
116 ),
117 T(
118 "bytes=0-20",
119 10,
120 vec![HttpRange {
121 start: 0,
122 length: 10,
123 }],
124 ),
125 T(
126 "bytes=15-,0-5",
127 10,
128 vec![HttpRange {
129 start: 0,
130 length: 6,
131 }],
132 ),
133 T(
134 "bytes=1-2,5-",
135 10,
136 vec![
137 HttpRange {
138 start: 1,
139 length: 2,
140 },
141 HttpRange {
142 start: 5,
143 length: 5,
144 },
145 ],
146 ),
147 T(
148 "bytes=-2 , 7-",
149 11,
150 vec![
151 HttpRange {
152 start: 9,
153 length: 2,
154 },
155 HttpRange {
156 start: 7,
157 length: 4,
158 },
159 ],
160 ),
161 T(
162 "bytes=0-0 ,2-2, 7-",
163 11,
164 vec![
165 HttpRange {
166 start: 0,
167 length: 1,
168 },
169 HttpRange {
170 start: 2,
171 length: 1,
172 },
173 HttpRange {
174 start: 7,
175 length: 4,
176 },
177 ],
178 ),
179 T(
180 "bytes=-5",
181 10,
182 vec![HttpRange {
183 start: 5,
184 length: 5,
185 }],
186 ),
187 T(
188 "bytes=-15",
189 10,
190 vec![HttpRange {
191 start: 0,
192 length: 10,
193 }],
194 ),
195 T(
196 "bytes=0-499",
197 10000,
198 vec![HttpRange {
199 start: 0,
200 length: 500,
201 }],
202 ),
203 T(
204 "bytes=500-999",
205 10000,
206 vec![HttpRange {
207 start: 500,
208 length: 500,
209 }],
210 ),
211 T(
212 "bytes=-500",
213 10000,
214 vec![HttpRange {
215 start: 9500,
216 length: 500,
217 }],
218 ),
219 T(
220 "bytes=9500-",
221 10000,
222 vec![HttpRange {
223 start: 9500,
224 length: 500,
225 }],
226 ),
227 T(
228 "bytes=0-0,-1",
229 10000,
230 vec![
231 HttpRange {
232 start: 0,
233 length: 1,
234 },
235 HttpRange {
236 start: 9999,
237 length: 1,
238 },
239 ],
240 ),
241 T(
242 "bytes=500-600,601-999",
243 10000,
244 vec![
245 HttpRange {
246 start: 500,
247 length: 101,
248 },
249 HttpRange {
250 start: 601,
251 length: 399,
252 },
253 ],
254 ),
255 T(
256 "bytes=500-700,601-999",
257 10000,
258 vec![
259 HttpRange {
260 start: 500,
261 length: 201,
262 },
263 HttpRange {
264 start: 601,
265 length: 399,
266 },
267 ],
268 ),
269 T(
271 "bytes= 1 -2 , 4- 5, 7 - 8 , ,,",
272 11,
273 vec![
274 HttpRange {
275 start: 1,
276 length: 2,
277 },
278 HttpRange {
279 start: 4,
280 length: 2,
281 },
282 HttpRange {
283 start: 7,
284 length: 2,
285 },
286 ],
287 ),
288 ];
289
290 for t in tests {
291 let header = t.0;
292 let size = t.1;
293 let expected = t.2;
294
295 let res = HttpRange::parse(header, size);
296
297 if res.is_err() {
298 if expected.is_empty() {
299 continue;
300 } else {
301 panic!(
302 "parse({}, {}) returned error {:?}",
303 header,
304 size,
305 res.unwrap_err()
306 );
307 }
308 }
309
310 let got = res.unwrap();
311
312 if got.len() != expected.len() {
313 panic!(
314 "len(parseRange({}, {})) = {}, want {}",
315 header,
316 size,
317 got.len(),
318 expected.len()
319 );
320 }
321
322 for i in 0..expected.len() {
323 if got[i].start != expected[i].start {
324 panic!(
325 "parseRange({}, {})[{}].start = {}, want {}",
326 header, size, i, got[i].start, expected[i].start
327 )
328 }
329 if got[i].length != expected[i].length {
330 panic!(
331 "parseRange({}, {})[{}].length = {}, want {}",
332 header, size, i, got[i].length, expected[i].length
333 )
334 }
335 }
336 }
337 }
338}