rust_serv/handler/
range.rs1use crate::error::{Error, Result};
2
3#[derive(Debug, Clone, PartialEq)]
5pub struct RangeRequest {
6 pub start: u64,
7 pub end: Option<u64>,
8}
9
10impl RangeRequest {
11 pub fn parse(range_header: &str, file_size: u64) -> Result<Option<Self>> {
13 if !range_header.starts_with("bytes=") {
15 return Err(Error::Http("Invalid Range header format".to_string()));
16 }
17
18 let range_part = &range_header[6..];
19
20 let parts: Vec<&str> = range_part.split('-').collect();
21 let parts: Vec<&str> = parts.into_iter().filter(|p| !p.is_empty()).collect();
23 if parts.is_empty() || parts.len() > 2 {
24 return Err(Error::Http("Invalid Range header format".to_string()));
25 }
26
27 let start: u64 = parts[0].parse()
28 .map_err(|_| Error::Http("Invalid range start".to_string()))?;
29
30 if start > file_size {
32 return Err(Error::Http("Range start exceeds file size".to_string()));
33 }
34
35 let end = if parts.len() == 2 {
36 let end_val: u64 = parts[1].parse()
37 .map_err(|_| Error::Http("Invalid range end".to_string()))?;
38
39 if end_val <= start {
41 return Err(Error::Http("Invalid range (end <= start)".to_string()));
42 }
43
44 Some(end_val.min(file_size - 1))
46 } else {
47 Some(file_size - 1)
49 };
50
51 Ok(Some(RangeRequest { start, end }))
52 }
53
54 pub fn to_range(&self) -> std::ops::Range<usize> {
56 let start = self.start as usize;
57 match self.end {
58 Some(end) => std::ops::Range { start, end: (end + 1) as usize },
59 None => std::ops::Range { start, end: usize::MAX },
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_parse_valid_range() {
70 let range = RangeRequest::parse("bytes=0-499", 1000).unwrap();
71 assert_eq!(range, Some(RangeRequest { start: 0, end: Some(499) }));
72 }
73
74 #[test]
75 fn test_parse_range_without_end() {
76 let range = RangeRequest::parse("bytes=100-", 1000).unwrap();
77 assert_eq!(range, Some(RangeRequest { start: 100, end: Some(999) }));
80 }
81
82 #[test]
83 fn test_parse_invalid_range() {
84 let result = RangeRequest::parse("bytes=200-100", 1000);
85 assert!(result.is_err());
86 }
87
88 #[test]
89 fn test_parse_range_exceeds_file_size() {
90 let result = RangeRequest::parse("bytes=2000-", 1000);
91 assert!(result.is_err());
93 }
94
95 #[test]
96 fn test_parse_range_end_equals_start() {
97 let result = RangeRequest::parse("bytes=100-100", 1000);
98 assert!(result.is_err());
100 }
101
102 #[test]
103 fn test_parse_range_negative_start() {
104 let result = RangeRequest::parse("bytes=-100", 1000);
105 assert!(result.is_ok()); }
109
110 #[test]
111 fn test_parse_range_invalid_format() {
112 let result = RangeRequest::parse("invalid", 1000);
113 assert!(result.is_err());
114 }
115
116 #[test]
117 fn test_parse_range_no_bytes_prefix() {
118 let result = RangeRequest::parse("0-499", 1000);
119 assert!(result.is_err());
120 }
121
122 #[test]
123 fn test_to_range_with_end() {
124 let range = RangeRequest { start: 10, end: Some(20) };
125 let byte_range = range.to_range();
126 assert_eq!(byte_range.start, 10);
127 assert_eq!(byte_range.end, 21); }
129
130 #[test]
131 fn test_parse_range_clamps_to_file_size() {
132 let range = RangeRequest::parse("bytes=0-2000", 1000).unwrap();
133 assert_eq!(range, Some(RangeRequest { start: 0, end: Some(999) }));
134 }
135
136 #[test]
137 fn test_parse_range_start_equals_file_size() {
138 let result = RangeRequest::parse("bytes=1000-", 1000);
139 assert!(result.is_ok());
141 }
142
143 #[test]
144 fn test_parse_range_empty_end() {
145 let range = RangeRequest::parse("bytes=0-", 1000).unwrap();
146 assert_eq!(range, Some(RangeRequest { start: 0, end: Some(999) }));
147 }
148
149 #[test]
150 fn test_parse_range_whitespace() {
151 let result = RangeRequest::parse(" bytes=0-499 ", 1000);
153 assert!(result.is_err());
155 }
156
157 #[test]
158 fn test_to_range_without_end() {
159 let range = RangeRequest { start: 100, end: None };
161 let byte_range = range.to_range();
162 assert_eq!(byte_range.start, 100);
163 assert_eq!(byte_range.end, usize::MAX);
164 }
165}