Function http_range_header::parse_range_header
source · pub fn parse_range_header(
range_header_value: &str
) -> Result<ParsedRanges, RangeUnsatisfiableError>
Expand description
Function that parses the content of a range header.
Follows the spec here
And here
Will only accept bytes ranges, will update when this spec changes to allow other units.
Parses ranges strictly, as in the examples contained in the above specifications.
Ranges such as bytes=0-15, 16-20, abc
will be rejected immediately.
It preserves the range ordering, the specification leaves it open to the server to determine whether
ranges that are out of order are correct or not, ie bytes=20-30, 0-15
Example no trailing or leading whitespaces
// Ok
assert!(http_range_header::parse_range_header("bytes=0-15").is_ok());
// Not allowed
assert!(http_range_header::parse_range_header("bytes=0-15 ").is_err());
// Also not allowed
assert!(http_range_header::parse_range_header("bytes= 0-15").is_err());
Example No leading whitespaces except in the case of separating multipart ranges
// Ok, multipart with a leading whitespace after comma
assert!(http_range_header::parse_range_header("bytes=0-15, 20-30").is_ok());
// Ok multipart without leading whitespace after comma
assert!(http_range_header::parse_range_header("bytes=0-15,20-30").is_ok());
Example No negative values, no leading zeroes, no plus-sign
// No negatives
assert!(http_range_header::parse_range_header("bytes=-12-15").is_err());
// No leading zeroes
assert!(http_range_header::parse_range_header("bytes=00-15").is_err());
// No plus sign
assert!(http_range_header::parse_range_header("bytes=+0-15").is_err());
Makes two passes and parses ranges strictly. On the first pass, if any range is malformed returns an Err
.
On the second pass if the ranges doesn’t make sense (reversed range, range out of bounds, etc.) returns an Err
.
Example with a standard valid range
let input = "bytes=0-15";
let file_size_bytes = 512;
let parsed_ranges = http_range_header::parse_range_header(input);
match parsed_ranges {
Ok(ranges) => {
match ranges.validate(file_size_bytes) {
Ok(valid_ranges) => {
for range in valid_ranges {
// do something with ranges
assert_eq!(0..=15, range)
}
}
Err(_err) => {
// Do something when ranges doesn't make sense
panic!("Weird range!")
}
}
}
Err(_err) => {
// Do something with malformed ranges
panic!("Malformed range!")
}
}
The parser makes two passes, one without a known file-size, ensuring all ranges are syntactically correct.
The returned struct will through its validate
method accept a file-size and figure out whether or not the
syntactically correct ranges actually makes sense in context
The range bytes=0-20
on a file with 15 bytes will be accepted in the first pass as the content size is unknown.
On the second pass (validate
) it will be truncated to file_size - 1
as per the spec.
Example range truncates in validate
because it exceedes
let input = "bytes=0-20";
let file_size_bytes = 15;
let parsed_ranges = http_range_header::parse_range_header(input)
// Is syntactically correct
.unwrap();
let validated = parsed_ranges.validate(file_size_bytes).unwrap();
assert_eq!(vec![0..=14], validated);
Range reversal and overlap is also checked in the second pass, the range bytes=0-20, 5-10
will become two syntactically correct ranges, but validate
will return ann Err
.
This is an opinionated implementation, the spec allows a server to determine its implementation of overlapping ranges, this api currently does not allow it.
Example multipart-range fails validate
because of an overlap
let input = "bytes=0-15, 10-20, 30-50";
let file_size_bytes = 512;
let parsed_ranges = http_range_header::parse_range_header(input)
// Is syntactically correct
.unwrap();
let validated = parsed_ranges.validate(file_size_bytes);
// Some ranges overlap, all valid ranges get truncated to 1 Err
assert!(validated.is_err());
Errors
Will return an error if the range_header_value
cannot be strictly parsed into a range
per the http spec.