actix_files/
range.rs

1use std::fmt;
2
3use derive_more::Error;
4
5/// Copy of `http_range::HttpRangeParseError`.
6#[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/// HTTP Range header representation.
36#[derive(Debug, Clone, Copy)]
37pub struct HttpRange {
38    /// Start of range.
39    pub start: u64,
40
41    /// Length of range.
42    pub length: u64,
43}
44
45impl HttpRange {
46    /// Parses Range HTTP header string as per RFC 2616.
47    ///
48    /// `header` is HTTP Range header (e.g. `bytes=bytes=0-9`).
49    /// `size` is full size of response (file).
50    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            // Match Apache laxity:
270            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}