Python internet throughput measurement: ten sequential HTTP requests, CLI and live web dashboard.
  • Python 43.5%
  • CSS 24.6%
  • HTML 14.7%
  • JavaScript 12%
  • PowerShell 1.8%
  • Other 3.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
akadmin 3edf0d3344
All checks were successful
CI / verify (push) Successful in 1m19s
Restore full gauge arc and balance inner reading proportion
2026-09-29 10:21:31 +03:00
.forgejo/workflows Fit speed test into one desktop screen and hide advanced tools 2026-09-29 09:04:36 +03:00
.github/workflows Fit speed test into one desktop screen and hide advanced tools 2026-09-29 09:04:36 +03:00
deploy Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
docs Remove secondary speed caption from main card 2026-09-29 10:09:20 +03:00
scripts Clarify browser speed units and center gauge reading 2026-09-29 10:07:29 +03:00
src/speedprobe Restore full gauge arc and balance inner reading proportion 2026-09-29 10:21:31 +03:00
tests Verify streamed-body timeout and cancellation cleanup 2026-09-29 09:37:53 +03:00
.dockerignore Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
.gitignore Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
AGENTS.md Codify verified Speedprobe design acceptance contract 2026-09-29 09:50:51 +03:00
compose.prod.yml Verify production paths and isolate CI deployment releases 2026-09-29 08:58:29 +03:00
compose.yml Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
Dockerfile Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
LICENSE Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
Makefile Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
pyproject.toml Polish toolkit, add vector hero, English UI and site metadata 2026-09-29 09:18:10 +03:00
README.md Remove secondary speed caption from main card 2026-09-29 10:09:20 +03:00
speedtest.py Build Speedprobe CLI, browser benchmark, APIs and production delivery 2026-09-29 08:55:45 +03:00
uv.lock Polish toolkit, add vector hero, English UI and site metadata 2026-09-29 09:18:10 +03:00

Speedprobe

One URL. Ten sequential downloads. Clear timing, byte counts, and throughput.

A Python CLI that completes the original assignment on your computer, with a browser dashboard, HTTP API, WebSocket progress, and self-hosted 1 / 5 / 20 MB PNG fixtures.

Live demo · Source repository · Design contract · CI/CD · Forgejo Actions

Speedprobe dashboard

Quick install

Linux / macOS:

curl -fsSL https://git.ligand.su/akadmin/speedprobe/raw/tag/v1.0.1/scripts/install.sh | sh

Windows PowerShell:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; irm https://git.ligand.su/akadmin/speedprobe/raw/tag/v1.0.1/scripts/install.ps1 | iex

Then run:

speedprobe

Choose a 1, 5, or 20 MB image in the terminal. For scripts, use speedprobe --size 5 --json; pass any HTTP(S) URL to test your own target.

Installation uses your user profile. The installer downloads uv and Python 3.12 when needed. Installation details, script review, and removal.

Run from source

Requires Python 3.12+ and uv.

git clone https://git.ligand.su/akadmin/speedprobe.git
cd speedprobe
uv sync --frozen
uv run speedprobe

You can also use uv run python speedtest.py URL or uv run python -m speedprobe URL.

The CLI makes exactly ten sequential GET requests, fully downloads each response before starting the next, and prints the result. Example output (illustrative values):

 1/10     5.000 MB    0.421 s    11.876 MB/s
...
Requests: 10; downloaded: 50,000,000 bytes
Mean request time: 0.425 s
Throughput: 11.765 MB/s (94.118 Mbps)
uv run speedprobe https://example.com/large-image.jpg --json
uv run speedprobe https://example.com/large-image.jpg --timeout 20 --max-mib 32

--json returns the summary and all ten samples. Errors also produce JSON and exit code 1; Ctrl+C exits with code 130. Redirects are rejected: supply the final URL. The CLI accepts any HTTP(S) target, rejects embedded credentials, and ignores proxy environment variables.

Measurement model

  • Request duration: from the start of GET until the full response body arrives, including connection setup and server latency. The client reuses connections.
  • Downloaded bytes: response body bytes received; HTTP/TLS headers and protocol overhead are excluded.
  • Mean duration: sum(elapsed) / 10.
  • Throughput: sum(bytes) / sum(elapsed) / 1_000_000 MB/s. This is aggregate throughput, rather than the arithmetic mean of individual request rates.
  • Browser display: Mbps is the primary speed reading; the method panel explains its conversion to MB/s. The CLI retains MB/s as required by the assignment. This changes the display unit, not measured bandwidth.
  • Units: 1 MB = 1,000,000 bytes; 1 Mbps = 1,000,000 bits/second; Mbps = MB/s × 8. The --max-mib limit uses binary MiB.

Results describe the route to the selected server, rather than guaranteed ISP capacity. Smaller files are more sensitive to latency. Python streams each response with bounded memory and rejects compression; browser downloads bypass the client cache and count decoded response bytes. Defaults are 30 seconds and 64 MiB per response. An interrupted or failed run never reports partial measurements as success.

Browser, HTTP, and WebSocket

Start the Python application:

uv run uvicorn speedprobe.api:app --host 127.0.0.1 --port 8000

Or start the complete local stack with Nginx and generated fixtures:

docker compose up --build -d

Open http://localhost:18120/. See deployment instructions.

Browser downloads measure your computer → fixture server. HTTP and WebSocket APIs run Python downloads along application server → fixture server. Their results demonstrate the server-side protocols and do not measure the visitor's internet connection.

curl https://ligand.su/speedtest/api/fixtures
curl -X POST https://ligand.su/speedtest/api/measure \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://ligand.su/speedtest/fixtures/5mb.png","requests":10}'

Connect to wss://ligand.su/speedtest/ws/measure and send the same JSON payload as the first message. The server emits progress messages containing sample, completed, and total, followed by a summary result or an error message. Disconnecting cancels the download. Interactive OpenAPI documentation is available at /speedtest/docs.

The public API accepts only exact URLs of configured fixtures, rejects arbitrary targets, and never follows redirects, preventing visitor-controlled SSRF. Defaults allow two concurrent runs, four starts per minute per IP, and ten downloads per run. The application uses one worker; horizontal scaling requires shared admission and rate-limit state or enforcement at the edge.

Development

uv sync --frozen
uv run pytest
uv run ruff check .
Path Responsibility
src/speedprobe/core.py Streaming measurement engine, independent of UI
src/speedprobe/cli.py Terminal interface
src/speedprobe/api.py FastAPI, fixture allowlist, admission limits, WebSocket
src/speedprobe/static/ Browser dashboard
tests/ Calculations, sequential downloads, errors, limits, and API behavior
deploy/ Self-hosting and generated fixtures

Application settings: SPEEDPROBE_PUBLIC_ORIGIN (origin without a path), SPEEDPROBE_ROOT_PATH (such as /speedtest), SPEEDPROBE_FIXTURE_ORIGIN (trusted internal Nginx origin), and SPEEDPROBE_CONCURRENCY (1–16). SPEEDPROBE_FIXTURES optionally supplies a JSON array of {id,label,bytes,url} objects; this is trusted administrator configuration. API downloads use the internal fixture origin selected at startup; the visitor's URL is only an allowlist lookup key.

The application runs as a non-root user. Container builds use the frozen uv.lock; Nginx serves the static test files from tmpfs with cached file descriptors. Deployment documentation explains caching and resource boundaries. Automated tests require no external network access. Release acceptance evidence records checks and platform limitations.

MIT licensed.