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.