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
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
rust
use std::cmp::{Ordering, Reverse}; use std::collections::BinaryHeap; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Lines, Write};
K-way merge of sorted logs in Rust
binary-heap
k-way-merge
streaming-io
Intermediate
8 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 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.