tower_http_cache/
range.rs1use http::{HeaderMap, HeaderValue, StatusCode};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct RangeRequest {
29 pub start: u64,
31
32 pub end: Option<u64>,
34}
35
36impl RangeRequest {
37 pub fn new(start: u64, end: Option<u64>) -> Self {
39 Self { start, end }
40 }
41
42 pub fn len(&self) -> Option<u64> {
44 self.end
45 .map(|end| end.saturating_sub(self.start).saturating_add(1))
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.len() == Some(0)
51 }
52
53 pub fn is_satisfiable(&self, total_size: u64) -> bool {
57 if self.start >= total_size {
58 return false;
59 }
60
61 if let Some(end) = self.end {
62 if end >= total_size || end < self.start {
63 return false;
64 }
65 }
66
67 true
68 }
69
70 pub fn normalize(&self, total_size: u64) -> Option<Self> {
72 if !self.is_satisfiable(total_size) {
73 return None;
74 }
75
76 let end = self.end.unwrap_or(total_size.saturating_sub(1));
77 let end = end.min(total_size.saturating_sub(1));
78
79 Some(Self {
80 start: self.start,
81 end: Some(end),
82 })
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
88pub enum RangeHandling {
89 #[default]
91 PassThrough,
92
93 CacheFullServeRanges,
96
97 #[allow(dead_code)]
100 CacheChunks,
101}
102
103pub fn parse_range_header(headers: &HeaderMap) -> Option<RangeRequest> {
129 let range_header = headers.get(http::header::RANGE)?;
130 let range_str = range_header.to_str().ok()?;
131
132 if !range_str.starts_with("bytes=") {
134 return None;
135 }
136
137 let range_spec = &range_str[6..]; let first_range = range_spec.split(',').next()?;
142
143 let parts: Vec<&str> = first_range.split('-').collect();
145 if parts.len() != 2 {
146 return None;
147 }
148
149 let start = parts[0].trim().parse::<u64>().ok()?;
151
152 let end = if parts[1].trim().is_empty() {
154 None
155 } else {
156 Some(parts[1].trim().parse::<u64>().ok()?)
157 };
158
159 Some(RangeRequest { start, end })
160}
161
162pub fn is_partial_content(status: StatusCode) -> bool {
166 status == StatusCode::PARTIAL_CONTENT
167}
168
169pub fn build_content_range_header(start: u64, end: u64, total: u64) -> HeaderValue {
182 let value = format!("bytes {}-{}/{}", start, end, total);
183 HeaderValue::from_str(&value).expect("Content-Range value should be valid")
184}
185
186pub fn parse_content_range_header(headers: &HeaderMap) -> Option<(u64, u64, u64)> {
190 let content_range = headers.get(http::header::CONTENT_RANGE)?;
191 let range_str = content_range.to_str().ok()?;
192
193 if !range_str.starts_with("bytes ") {
195 return None;
196 }
197
198 let range_spec = &range_str[6..]; let parts: Vec<&str> = range_spec.split('/').collect();
200 if parts.len() != 2 {
201 return None;
202 }
203
204 let range_parts: Vec<&str> = parts[0].split('-').collect();
205 if range_parts.len() != 2 {
206 return None;
207 }
208
209 let start = range_parts[0].trim().parse::<u64>().ok()?;
210 let end = range_parts[1].trim().parse::<u64>().ok()?;
211 let total = parts[1].trim().parse::<u64>().ok()?;
212
213 Some((start, end, total))
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_parse_range_with_start_and_end() {
222 let mut headers = HeaderMap::new();
223 headers.insert(http::header::RANGE, "bytes=0-1023".parse().unwrap());
224
225 let range = parse_range_header(&headers).unwrap();
226 assert_eq!(range.start, 0);
227 assert_eq!(range.end, Some(1023));
228 }
229
230 #[test]
231 fn test_parse_range_with_start_only() {
232 let mut headers = HeaderMap::new();
233 headers.insert(http::header::RANGE, "bytes=1024-".parse().unwrap());
234
235 let range = parse_range_header(&headers).unwrap();
236 assert_eq!(range.start, 1024);
237 assert_eq!(range.end, None);
238 }
239
240 #[test]
241 fn test_parse_range_missing_header() {
242 let headers = HeaderMap::new();
243 assert!(parse_range_header(&headers).is_none());
244 }
245
246 #[test]
247 fn test_parse_range_invalid_unit() {
248 let mut headers = HeaderMap::new();
249 headers.insert(http::header::RANGE, "items=0-10".parse().unwrap());
250
251 assert!(parse_range_header(&headers).is_none());
252 }
253
254 #[test]
255 fn test_parse_range_malformed() {
256 let mut headers = HeaderMap::new();
257 headers.insert(http::header::RANGE, "bytes=invalid".parse().unwrap());
258
259 assert!(parse_range_header(&headers).is_none());
260 }
261
262 #[test]
263 fn test_range_len() {
264 let range = RangeRequest::new(0, Some(1023));
265 assert_eq!(range.len(), Some(1024));
266
267 let range_open = RangeRequest::new(1024, None);
268 assert_eq!(range_open.len(), None);
269 }
270
271 #[test]
272 fn test_range_is_satisfiable() {
273 let range = RangeRequest::new(0, Some(1023));
274 assert!(range.is_satisfiable(10240));
275 assert!(!range.is_satisfiable(512));
276
277 let range_at_end = RangeRequest::new(10240, Some(10250));
278 assert!(!range_at_end.is_satisfiable(10240));
279 }
280
281 #[test]
282 fn test_range_normalize() {
283 let range = RangeRequest::new(0, None);
284 let normalized = range.normalize(10240).unwrap();
285 assert_eq!(normalized.start, 0);
286 assert_eq!(normalized.end, Some(10239));
287
288 let range_explicit = RangeRequest::new(0, Some(1023));
289 let normalized_explicit = range_explicit.normalize(10240).unwrap();
290 assert_eq!(normalized_explicit.start, 0);
291 assert_eq!(normalized_explicit.end, Some(1023));
292 }
293
294 #[test]
295 fn test_range_normalize_out_of_bounds() {
296 let range = RangeRequest::new(10240, Some(20000));
297 assert!(range.normalize(10240).is_none());
298 }
299
300 #[test]
301 fn test_is_partial_content() {
302 assert!(is_partial_content(StatusCode::PARTIAL_CONTENT));
303 assert!(!is_partial_content(StatusCode::OK));
304 assert!(!is_partial_content(StatusCode::NOT_FOUND));
305 }
306
307 #[test]
308 fn test_build_content_range_header() {
309 let header = build_content_range_header(0, 1023, 10240);
310 assert_eq!(header.to_str().unwrap(), "bytes 0-1023/10240");
311 }
312
313 #[test]
314 fn test_parse_content_range_header() {
315 let mut headers = HeaderMap::new();
316 headers.insert(
317 http::header::CONTENT_RANGE,
318 "bytes 0-1023/10240".parse().unwrap(),
319 );
320
321 let (start, end, total) = parse_content_range_header(&headers).unwrap();
322 assert_eq!(start, 0);
323 assert_eq!(end, 1023);
324 assert_eq!(total, 10240);
325 }
326
327 #[test]
328 fn test_parse_content_range_missing() {
329 let headers = HeaderMap::new();
330 assert!(parse_content_range_header(&headers).is_none());
331 }
332
333 #[test]
334 fn test_range_request_is_empty() {
335 let empty_range = RangeRequest::new(0, Some(0));
336 assert!(!empty_range.is_empty()); let range = RangeRequest::new(100, Some(99)); let len = range.len();
340 assert!(len.is_some());
343 }
344
345 #[test]
346 fn test_parse_range_with_whitespace() {
347 let mut headers = HeaderMap::new();
348 headers.insert(http::header::RANGE, "bytes= 0 - 1023 ".parse().unwrap());
349
350 if let Some(range) = parse_range_header(&headers) {
352 assert_eq!(range.start, 0);
353 assert_eq!(range.end, Some(1023));
354 }
355 }
356
357 #[test]
358 fn test_range_multiple_ranges_first_only() {
359 let mut headers = HeaderMap::new();
360 headers.insert(
361 http::header::RANGE,
362 "bytes=0-1023,1024-2047".parse().unwrap(),
363 );
364
365 let range = parse_range_header(&headers).unwrap();
367 assert_eq!(range.start, 0);
368 assert_eq!(range.end, Some(1023));
369 }
370}