bolt-load is an embeddable Rust library for single-stream and multi-stream concurrent file downloads. It automatically detects HTTP range support, splits files into parallel byte-range chunks, and dynamically rebalances slow connections — all while staying runtime-agnostic.
- Pluggable adapters — bring any download source via the
BoltLoadAdaptertrait; ships with reqwest and ureq. - Multi-runtime — Tokio, Smol out of the box; custom runtimes via traits.
- Adaptive concurrency — failure-rate monitoring with automatic degradation and recovery.
- Dynamic range splitting — slow runners have their remaining ranges reassigned in real time.
- Multiple I/O backends — mmap, pwrite thread pool, compio writer.
- Backpressure-aware — lock-free SPSC ring-buffer channels prevent memory blow-up.
- Structured errors — retryable vs. unretryable classification drives intelligent retry logic.
- Observable — EMA speed sampling, progress events,
indicatifprogress bars,tracingspans.
Add to your Cargo.toml:
[dependencies]
bolt-load = "0.2"
reqwest = "0.13"
indicatif = "0.18"
tokio = { version = "1", features = ["full"] }Download a file with concurrent range requests and a terminal progress bar:
use bolt_load::task::Task;
use bolt_load::adapter::reqwest::IntoReqwestAdapter;
use bolt_load::runtime::ThreadedRuntimeImpl;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = reqwest::Client::new();
let adapter = client.into_reqwest_adapter((
reqwest::Method::GET,
"https://example.com/large-file.bin".parse()?,
));
let task = Task::builder(adapter)
.runtime(ThreadedRuntimeImpl::new_tokio_rt())
.save_to("large-file.bin")
.spawn()?;
task.drive_progress_bar(indicatif::ProgressBar::no_length());
let output = task.await?;
println!("saved {} bytes to {:?}", output.progress.downloaded, output.path);
Ok(())
}task.status() is a latest-value receiver of TaskStatus snapshots (state,
progress, speed) for your own UI; dropping the Task cancels the download.
┌──────────────────────────────────────────────────────┐
│ Task │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Singleton mode │ │ Concurrent mode │ │
│ │ (1 runner) │ │ (N runners) │ │
│ └────────┬─────────┘ └────────┬─────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ TaskRunner[] RunnerManager │
│ │ ├─ ChunkPlanner │
│ │ ├─ StrategyControl │
│ │ ├─ SpeedSampler │
│ │ └─ FileWriter │
│ ▼ │
│ AnyAdapter (BoltLoadAdapter trait) │
└──────────────────────────────────────────────────────┘
Task is the user-facing entry point. It selects between two download strategies automatically:
| Mode | Condition | Behavior |
|---|---|---|
| Singleton | Adapter lacks range support | Single TaskRunner, sequential stream |
| Concurrent | Adapter supports byte ranges | N parallel TaskRunners with dynamic splitting |
The engine monitors runner failure rates over a sliding window:
- Normal — spawn up to CPU-core-count runners.
- Degraded — failure rate > 30% triggers stepwise concurrency reduction.
- Recovering — after 30 s cooldown, probe with +1 runner; commit if stable over 15 s.
| Backend | Feature | Description |
|---|---|---|
MmapWriter |
mmap |
Memory-mapped random-access writes |
PoolWriter |
(default) | Thread-pool pwrite (portable) |
CompioWriter |
compio |
io_uring-based async writes (Linux) |
NullWriter |
— | Discard output for benchmarking |
| Crate | Description |
|---|---|
| bolt-load | Main download engine |
| bolt-load-core | BoltLoadAdapter trait and error types |
| bolt-load-adapter | HTTP adapters: reqwest, ureq |
| bolt-load-http | Status, range and content-coding checks and header parsing for HTTP adapters |
| bolt-load-sync | Runtime-agnostic sync primitives: watch channel, slot |
| bolt-load-utils | Cross-runtime streams, telemetry |
| bolt-load-tests | Test adapters and HTTP server fixtures |
| Feature | Default | Description |
|---|---|---|
tokio |
✓ | Tokio async runtime |
smol |
Smol async runtime | |
compio |
Compio (io_uring) file writer | |
reqwest |
✓ | Reqwest HTTP adapter |
ureq2 |
Ureq 2.x adapter (blocking) | |
ureq3 |
Ureq 3.x adapter (blocking) | |
http |
HTTP header parsing (Content-Disposition) | |
serde |
✓ | Serialization for progress types |
tracing |
✓ | Distributed tracing instrumentation |
progressbar |
✓ | indicatif terminal progress bars |
mmap |
✓ | Memory-mapped file writer |
Implement BoltLoadAdapter to download from any source (S3, FTP, in-memory, …):
impl BoltLoadAdapter for MySource {
async fn is_range_stream_available(&self) -> bool { true }
async fn retrieve_meta(&self) -> Result<BoltLoadAdapterMeta, AdapterError> { /* ... */ }
async fn full_stream(&self) -> Result<AnyBytesStream, AdapterError> { /* ... */ }
async fn range_stream(&self, start: u64, end: u64) -> Result<AnyBytesStream, AdapterError> { /* ... */ }
}The futures must be Send. Pass the adapter with Task::builder(source),
or share a type-erased one built by DynBoltLoadAdapter::new_arc with
Task::builder_arc.
Read the code guide before contributing; it covers style, API design, error handling, async and unsafe code, tests, and commit messages. Coding agents start from AGENTS.md.
Install Lefthook and Deno before working on this repository.
lefthook installThe configured Git hooks run the following checks:
cargo fmt --all -- --check— Rust formatting.cargo clippy --all-targets --all-features -- -D warnings— Rust linting.deno fmt --check— TypeScript/JavaScript formatting.deno lint— TypeScript/JavaScript linting.@commitlint/cli— Conventional Commits validation.
Commit messages must follow Conventional Commits:
feat: add resumable download support
fix: handle zero-length range responses
refactor: migrate error types to snafu
The examples can report to tokio-console, which needs tokio's unstable instrumentation:
RUSTFLAGS="--cfg tokio_unstable" cargo run --example test_performanceThe concurrent download engine uses a two-phase approach to find optimal parallelism:
Start with 1 runner, doubling each period until additional runners stop producing significant throughput gains:
After slow start, add one runner at a time by splitting the chunk with the longest remaining range:
Rust 1.88.0+ (Edition 2024)