rust
47 lines · 10 steps
Parsing an HTTP Range header in Rust
How to parse a bytes= Range header and read exactly that slice of a file from disk.
Explained by
highlit
1use std::fs::File;
2use std::io::{Read, Seek, SeekFrom};
3
4#[derive(Debug)]
5pub struct RangeSlice {
6 pub start: u64,
7 pub end: u64,
8 pub total: u64,
9 pub bytes: Vec<u8>,
10}
11
12pub fn read_range(path: &str, header: &str) -> std::io::Result<Option<RangeSlice>> {
13 let mut file = File::open(path)?;
14 let total = file.metadata()?.len();
15
16 let spec = match header.strip_prefix("bytes=") {
17 Some(s) => s.trim(),
18 None => return Ok(None),
19 };
20
21 if spec.contains(',') || total == 0 {
22 return Ok(None);
23 }
24
25 let (start_raw, end_raw) = spec.split_once('-')?;
26
27 let (start, end) = match (start_raw.trim(), end_raw.trim()) {
28 ("", suffix) => {
29 let len: u64 = suffix.parse().ok()?;
30 let len = len.min(total);
31 (total - len, total - 1)
32 }
33 (s, "") => (s.parse().ok()?, total - 1),
34 (s, e) => (s.parse().ok()?, e.parse::<u64>().ok()?.min(total - 1)),
35 };
36
37 if start > end || start >= total {
38 return Ok(None);
39 }
40
41 let len = (end - start + 1) as usize;
42 let mut bytes = vec![0u8; len];
43 file.seek(SeekFrom::Start(start))?;
44 file.read_exact(&mut bytes)?;
45
46 Ok(Some(RangeSlice { start, end, total, bytes }))
47}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Returning Option inside a Result cleanly separates 'no valid range' from real I/O errors.
- 2The ? operator on Option lets malformed input bail out early without verbose match ladders.
- 3Suffix and open-ended range forms each need distinct arithmetic against the file's total length.
Related explainers
python
import hashlib from collections import defaultdict from pathlib import Path
Finding duplicate files by size then hash
hashing
file-io
deduplication
Intermediate
7 steps
rust
use std::time::{Duration, Instant}; pub struct Ewma { alpha: f64,
A time-decayed moving average in Rust
exponential-smoothing
time-decay
state-machine
Intermediate
8 steps
go
package middleware import ( "net/http"
Wrapping Gin requests in a DB transaction
middleware
transactions
error-handling
Intermediate
8 steps
ruby
require "charlock_holmes" class TextFileNormalizer DEFAULT_CONFIDENCE = 60
Normalizing text files to clean UTF-8 in Ruby
encoding
text-processing
file-io
Intermediate
8 steps
rust
use rand::distributions::{Alphanumeric, DistString}; use rand::rngs::OsRng; #[derive(Debug, Clone, PartialEq, Eq)]
A newtype wrapper for API tokens in Rust
newtype-pattern
randomness
encapsulation
Intermediate
6 steps
rust
pub fn normalize_path(input: &str) -> String { let is_absolute = input.starts_with('/'); let has_trailing_slash = input.len() > 1 && input.ends_with('/'); let mut stack: Vec<&str> = Vec::new();
Normalizing filesystem paths in Rust
string-processing
stack
path-manipulation
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-an-http-range-header-in-rust-explained-rust-a3c6/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.