ntex_files/
range.rs

1/// HTTP Range header representation.
2#[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    /// Parses Range HTTP header string as per RFC 2616.
13    ///
14    /// `header` is HTTP Range header (e.g. `bytes=bytes=0-9`).
15    /// `size` is full size of response (file).
16    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                    // If no start is specified, end specifies the
39                    // range start relative to the end of the file.
40                    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                        // If no end is specified, range extends to end of the file.
63                        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            // Match Apache laxity:
174            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}