http_fs/headers/
range.rs

1//!HTTP Range module
2
3/// HTTP Range header representation.
4#[derive(Debug, Clone, Copy)]
5pub struct HttpRange {
6    ///Starting position
7    pub start: u64,
8    ///Length of range.
9    pub length: u64,
10}
11
12static PREFIX: &'static str = "bytes=";
13const PREFIX_LEN: usize = 6;
14
15impl HttpRange {
16    /// Parses Range HTTP header string as per RFC 2616.
17    ///
18    /// `header` is HTTP Range header (e.g. `bytes=bytes=0-9`).
19    /// `size` is full size of response (file).
20    pub fn parse(header: &str, size: u64) -> Result<Vec<Self>, ()> {
21        if header.is_empty() {
22            return Ok(Vec::new());
23        }
24        if !header.starts_with(PREFIX) {
25            return Err(());
26        }
27
28        let size_sig = size as i64;
29        let mut no_overlap = false;
30
31        let mut ranges = Vec::new();
32
33        for ra in header[PREFIX_LEN..].split(',').map(|x| x.trim()).filter(|x| !x.is_empty()) {
34            let mut start_end_iter = ra.split('-');
35
36            let start_str = start_end_iter.next().ok_or(())?.trim();
37            let end_str = start_end_iter.next().ok_or(())?.trim();
38
39            if start_str.is_empty() {
40                // If no start is specified, end specifies the
41                // range start relative to the end of the file.
42                let mut length: i64 = end_str.parse().map_err(|_| ())?;
43
44                if length > size_sig {
45                    length = size_sig;
46                }
47
48                ranges.push(Self {
49                    start: (size_sig - length) as u64,
50                    length: length as u64,
51                })
52            } else {
53                let start: i64 = start_str.parse().map_err(|_| ())?;
54
55                if start < 0 {
56                    return Err(());
57                }
58                if start >= size_sig {
59                    no_overlap = true;
60                    continue;
61                }
62
63                let length = if end_str.is_empty() {
64                    // If no end is specified, range extends to end of the file.
65                    size_sig - start
66                } else {
67                    let mut end: i64 = end_str.parse().map_err(|_| ())?;
68
69                    if start > end {
70                        return Err(());
71                    }
72
73                    if end >= size_sig {
74                        end = size_sig - 1;
75                    }
76
77                    end - start + 1
78                };
79
80                ranges.push(Self {
81                    start: start as u64,
82                    length: length as u64,
83                });
84            }
85        }
86
87        if no_overlap && ranges.is_empty() {
88            return Err(());
89        }
90
91        Ok(ranges)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    struct T(&'static str, u64, Vec<HttpRange>);
100
101    #[test]
102    fn verify_parser() {
103        let tests = vec![
104            T("", 0, vec![]),
105            T("", 1000, vec![]),
106            T("foo", 0, vec![]),
107            T("bytes=", 0, vec![]),
108            T("bytes=7", 10, vec![]),
109            T("bytes= 7 ", 10, vec![]),
110            T("bytes=1-", 0, vec![]),
111            T("bytes=5-4", 10, vec![]),
112            T("bytes=0-2,5-4", 10, vec![]),
113            T("bytes=2-5,4-3", 10, vec![]),
114            T("bytes=--5,4--3", 10, vec![]),
115            T("bytes=A-", 10, vec![]),
116            T("bytes=A- ", 10, vec![]),
117            T("bytes=A-Z", 10, vec![]),
118            T("bytes= -Z", 10, vec![]),
119            T("bytes=5-Z", 10, vec![]),
120            T("bytes=Ran-dom, garbage", 10, vec![]),
121            T("bytes=0x01-0x02", 10, vec![]),
122            T("bytes=         ", 10, vec![]),
123            T("bytes= , , ,   ", 10, vec![]),
124            T(
125                "bytes=0-9",
126                10,
127                vec![HttpRange {
128                    start: 0,
129                    length: 10,
130                }],
131            ),
132            T(
133                "bytes=0-",
134                10,
135                vec![HttpRange {
136                    start: 0,
137                    length: 10,
138                }],
139            ),
140            T(
141                "bytes=5-",
142                10,
143                vec![HttpRange {
144                    start: 5,
145                    length: 5,
146                }],
147            ),
148            T(
149                "bytes=0-20",
150                10,
151                vec![HttpRange {
152                    start: 0,
153                    length: 10,
154                }],
155            ),
156            T(
157                "bytes=15-,0-5",
158                10,
159                vec![HttpRange {
160                    start: 0,
161                    length: 6,
162                }],
163            ),
164            T(
165                "bytes=1-2,5-",
166                10,
167                vec![
168                    HttpRange {
169                        start: 1,
170                        length: 2,
171                    },
172                    HttpRange {
173                        start: 5,
174                        length: 5,
175                    },
176                ],
177            ),
178            T(
179                "bytes=-2 , 7-",
180                11,
181                vec![
182                    HttpRange {
183                        start: 9,
184                        length: 2,
185                    },
186                    HttpRange {
187                        start: 7,
188                        length: 4,
189                    },
190                ],
191            ),
192            T(
193                "bytes=0-0 ,2-2, 7-",
194                11,
195                vec![
196                    HttpRange {
197                        start: 0,
198                        length: 1,
199                    },
200                    HttpRange {
201                        start: 2,
202                        length: 1,
203                    },
204                    HttpRange {
205                        start: 7,
206                        length: 4,
207                    },
208                ],
209            ),
210            T(
211                "bytes=-5",
212                10,
213                vec![HttpRange {
214                    start: 5,
215                    length: 5,
216                }],
217            ),
218            T(
219                "bytes=-15",
220                10,
221                vec![HttpRange {
222                    start: 0,
223                    length: 10,
224                }],
225            ),
226            T(
227                "bytes=0-499",
228                10000,
229                vec![HttpRange {
230                    start: 0,
231                    length: 500,
232                }],
233            ),
234            T(
235                "bytes=500-999",
236                10000,
237                vec![HttpRange {
238                    start: 500,
239                    length: 500,
240                }],
241            ),
242            T(
243                "bytes=-500",
244                10000,
245                vec![HttpRange {
246                    start: 9500,
247                    length: 500,
248                }],
249            ),
250            T(
251                "bytes=9500-",
252                10000,
253                vec![HttpRange {
254                    start: 9500,
255                    length: 500,
256                }],
257            ),
258            T(
259                "bytes=0-0,-1",
260                10000,
261                vec![
262                    HttpRange {
263                        start: 0,
264                        length: 1,
265                    },
266                    HttpRange {
267                        start: 9999,
268                        length: 1,
269                    },
270                ],
271            ),
272            T(
273                "bytes=500-600,601-999",
274                10000,
275                vec![
276                    HttpRange {
277                        start: 500,
278                        length: 101,
279                    },
280                    HttpRange {
281                        start: 601,
282                        length: 399,
283                    },
284                ],
285            ),
286            T(
287                "bytes=500-700,601-999",
288                10000,
289                vec![
290                    HttpRange {
291                        start: 500,
292                        length: 201,
293                    },
294                    HttpRange {
295                        start: 601,
296                        length: 399,
297                    },
298                ],
299            ),
300            // Match Apache laxity:
301            T(
302                "bytes=   1 -2   ,  4- 5, 7 - 8 , ,,",
303                11,
304                vec![
305                    HttpRange {
306                        start: 1,
307                        length: 2,
308                    },
309                    HttpRange {
310                        start: 4,
311                        length: 2,
312                    },
313                    HttpRange {
314                        start: 7,
315                        length: 2,
316                    },
317                ],
318            ),
319        ];
320
321        for t in tests {
322            let header = t.0;
323            let size = t.1;
324            let expected = t.2;
325
326            let res = HttpRange::parse(header, size);
327
328            if res.is_err() {
329                if expected.is_empty() {
330                    continue;
331                } else {
332                    assert!(
333                        false,
334                        "parse({}, {}) returned error {:?}",
335                        header,
336                        size,
337                        res.unwrap_err()
338                    );
339                }
340            }
341
342            let got = res.unwrap();
343
344            if got.len() != expected.len() {
345                assert!(
346                    false,
347                    "len(parseRange({}, {})) = {}, want {}",
348                    header,
349                    size,
350                    got.len(),
351                    expected.len()
352                );
353                continue;
354            }
355
356            for i in 0..expected.len() {
357                if got[i].start != expected[i].start {
358                    assert!(
359                        false,
360                        "parseRange({}, {})[{}].start = {}, want {}",
361                        header, size, i, got[i].start, expected[i].start
362                    )
363                }
364                if got[i].length != expected[i].length {
365                    assert!(
366                        false,
367                        "parseRange({}, {})[{}].length = {}, want {}",
368                        header, size, i, got[i].length, expected[i].length
369                    )
370                }
371            }
372        }
373    }
374}