{"id":27900546,"url":"https://github.com/vicanso/http-stat-rs","last_synced_at":"2026-05-30T11:01:23.639Z","repository":{"id":290682457,"uuid":"974831202","full_name":"vicanso/http-stat-rs","owner":"vicanso","description":"Pure rust version of http statistics, which refer to httpstat, and supports http1, http2 and http3.","archived":false,"fork":false,"pushed_at":"2026-05-30T07:55:35.000Z","size":1009,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-30T09:13:15.865Z","etag":null,"topics":["curl","http-stat","http3"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vicanso.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-29T11:24:21.000Z","updated_at":"2026-05-30T07:55:38.000Z","dependencies_parsed_at":"2025-05-24T02:26:23.571Z","dependency_job_id":"1ea38a10-a35d-48d6-ad68-d359584fe0fb","html_url":"https://github.com/vicanso/http-stat-rs","commit_stats":null,"previous_names":["vicanso/http-stat-rs"],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/vicanso/http-stat-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vicanso%2Fhttp-stat-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vicanso%2Fhttp-stat-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vicanso%2Fhttp-stat-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vicanso%2Fhttp-stat-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vicanso","download_url":"https://codeload.github.com/vicanso/http-stat-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vicanso%2Fhttp-stat-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33689564,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["curl","http-stat","http3"],"created_at":"2025-05-05T20:17:06.618Z","updated_at":"2026-05-30T11:01:23.620Z","avatar_url":"https://github.com/vicanso.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# http-stat-rs\n\nImitation is the sincerest form of flattery.\n\nA **zero-dependency, single-binary** HTTP diagnostics tool written in pure Rust. Visualizes the full request lifecycle — DNS, TCP, TLS, server processing, content transfer — in one clear timeline. Inspired by [httpstat](https://github.com/davecheney/httpstat).\n\n[中文](./README_zh.md)\n\n![screenshot](./screenshot.png)\n\n## Highlights\n\n- **HTTP/1.1, HTTP/2 \u0026 HTTP/3 (QUIC)** — first-class support for all modern protocols, switch with a single flag\n- **gRPC health check** — use `grpc://` or `grpcs://` scheme to probe gRPC services\n- **Request Send phase** — request body upload is timed separately from Server Processing, so a slow POST upload no longer hides as \"server is slow\"\n- **Server-Timing inspection** — parses the `Server-Timing` response header (RFC 8673) and displays the server-reported breakdown *inside* TTFB (CDN edge vs origin vs worker, etc.)\n- **Benchmark mode** — `-n 10` repeats N times with min/max/avg/p50/p95/p99; add `-K` to reuse the connection and compare cold vs warm latency\n- **Multi-IP concurrent testing** — `--resolve` tests multiple IPs in parallel, results sorted by success\n- **Transparent decompression** — auto-decodes `gzip`, `br`, `zstd` responses with `--compressed`\n- **Custom DNS** — specify DNS servers by IP or use built-in presets: `google`, `cloudflare`, `quad9`; DoH/DoT presets: `google-doh`, `cloudflare-doh`, `quad9-doh`, `google-dot`, `cloudflare-dot`, `quad9-dot`\n- **DoH/DoT phase split** — when DoH or DoT is in use, the DNS column splits into `DNS Connect` (TCP+TLS to the resolver) and `DNS Query`, so you can tell whether \"DoH is slow\" means slow handshake to the resolver or slow query processing\n- **Kernel TCP statistics** — on Linux and macOS, samples `getsockopt(TCP_INFO)` right after `connect(2)` and again after the response body is read. Shown under `--verbose` or with the dedicated `--tcp-info` flag; surfaces RTT, MSS, cwnd, and retransmits-during-request, which isolates \"Content Transfer slow\" into packet loss vs TCP slow start vs application latency. Through an HTTP/SOCKS proxy the sample reflects the client↔proxy socket, not the origin.\n- **Download throughput with slow-start split** — for response bodies larger than 1 MiB, a `Throughput: X MB/s` line is shown next to `Body size`. Under `--verbose`, it also breaks down into \"first 100 KB\" vs \"tail\" so you can tell TCP slow-start–dominated downloads from servers that simply stream slowly the whole way.\n- **Bilingual output (English / Simplified Chinese)** — `--lang en|zh` overrides the display language; when omitted, the locale is sniffed from `LC_ALL` / `LC_MESSAGES` / `LANG` (anything starting with `zh` → Chinese), with English as the safe fallback. JSON output keeps English keys so machine consumers aren't affected by user locale.\n- **JSON output** — `--json` for scripting, CI/CD pipelines, and monitoring integration\n- **TLS inspection** — verbose mode shows full certificate chain, cipher suite, SAN domains, and validity\n- **TLS handshake diagnostics** — every HTTPS request reports whether the handshake was `Full` or `Resumed`, whether the server stapled an OCSP response, and (in `-n` benchmark mode, where runs 2+ can resume) whether 0-RTT early data was accepted\n- **Cookie support** — `-b 'k=v'` or `-b @file`, auto-carried across `-L` redirects with `Set-Cookie` merging\n- **ALPN negotiation display** — every response line shows the final protocol agreed between client and server (`HTTP/1.1`, `H2`, `H3`), so you always know which version was actually used\n- **JSON field selector** — `--jq '.items[].name'` extracts fields directly from the response body; no need to pipe through `jq`\n- **Pretty JSON** — `--pretty` formats the response body in-place; combine with `--jq` for focused, readable output\n- **Response header filtering** — `--include-header` shows only the headers you care about; `--exclude-header` hides the noisy ones\n- **curl-like UX** — familiar flags (`-H`, `-X`, `-d`, `-L`, `-k`, `-o`, `-4`/`-6`) for a smooth transition\n- **Waterfall chart** — `--waterfall` renders each phase as a horizontal bar, making bottlenecks instantly visible (like Chrome DevTools Network panel)\n- **`--connect-to`** — redirect `HOST1:PORT1` to `HOST2:PORT2` at the TCP level; TLS SNI and `Host` header stay unchanged, like curl's `--connect-to`\n- **Proxy support** — `--proxy` for HTTP/HTTPS/SOCKS5 proxies; also reads `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` environment variables\n- **Source IP binding** — `--bind \u003cIP\u003e` pins outbound connections to a specific local address; essential for multi-NIC hosts, policy routing, or validating which interface reaches a target\n- **mTLS (mutual TLS)** — `--cert`/`--key` sends a client certificate for zero-trust and service mesh authentication\n- **Config file** — `~/.httpstatrc` sets persistent defaults (DNS, timeout, headers, etc.); CLI flags always win\n- **Semantic exit codes** — distinct codes for DNS, TCP, TLS, timeout, 4xx, 5xx failures for easy scripting\n- **Tiny binary** — release build uses LTO + `opt-level=z` + strip, typically \u003c 5 MB\n- **Truly zero system dependencies** — statically linked, no libcurl, no OpenSSL, no libc on musl builds; drop the binary directly into a `scratch` or `alpine` Docker image for production diagnostics\n\n## Installation\n\n### One-line install (Linux \u0026 macOS)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/vicanso/http-stat-rs/main/install.sh | sh\n```\n\n### Pre-built binaries\n\n[Pre-built binaries](https://github.com/vicanso/http-stat-rs/releases) for Windows, macOS and Linux.\n\n```bash\n# Linux\ncurl -L https://github.com/vicanso/http-stat-rs/releases/latest/download/httpstat-linux-musl-$(uname -m).tar.gz | tar -xzf -\nsudo mv httpstat /usr/local/bin/\n\n# macOS\ncurl -L https://github.com/vicanso/http-stat-rs/releases/latest/download/httpstat-darwin-$(uname -m).tar.gz | tar -xzf -\nsudo mv httpstat /usr/local/bin/\n```\n\n### From source\n\n```bash\ncargo install http-stat\n```\n\n## Request Lifecycle\n\nEvery HTTP request goes through up to six sequential phases. httpstat measures each one independently:\n\n```\n  DNS Lookup   TCP Connect  TLS Handshake  Request Send  Server Processing  Content Transfer\n[────────────][────────────][──────────────][────────────][──────────────────][───────────────]\n      │              │              │             │                │                  │\n  hostname       3-way SYN      TLS/SSL      headers +         waiting for       downloading\n   → IP          handshake    negotiation    request body       first byte         response\n                                 (HTTPS)      on the wire\n                                                                                          ▲\n                                                                              Total = all phases\n```\n\n| Phase | What it measures |\n|---|---|\n| DNS Lookup | Time to resolve the hostname to an IP address |\n| TCP Connect | Time for the 3-way TCP handshake |\n| TLS Handshake | Time to negotiate the TLS session (HTTPS/HTTP2/HTTP3 only) |\n| Request Send | Time to write request headers and body to the transport — matters for POST/PUT uploads |\n| Server Processing | Time from the last request byte sent to the first response byte received — pure server latency |\n| Content Transfer | Time to download the complete response body |\n\n\u003e For HTTP/3, **QUIC Connect** replaces both TCP Connect and TLS Handshake (QUIC combines transport and crypto in a single handshake).\n\nIf the server emits a `Server-Timing` response header, httpstat parses it and displays the server-reported sub-phases right above the timeline — useful for splitting a slow Server Processing into CDN edge / origin / worker components without leaving the terminal.\n\n## Usage\n\n```bash\n# Basic — auto-negotiates HTTP/2 via ALPN\nhttpstat https://www.cloudflare.com/\n\n# HTTP/3 (QUIC) with compressed response\nhttpstat --http3 --compressed https://cloudflare-quic.com/\n\n# Test multiple IPs concurrently in silent mode\nhttpstat --resolve=183.240.99.169,2409:8c54:870:310:0:ff:b0ed:40ac -s https://www.baidu.com/\n\n# POST with request body from file\nhttpstat -X POST -d @payload.json -H 'Content-Type: application/json' https://httpbin.org/post\n\n# POST with request body from stdin\necho '{\"key\":\"value\"}' | httpstat -X POST -d @- -H 'Content-Type: application/json' https://httpbin.org/post\n\n# gRPC health check\nhttpstat grpc://localhost:50051\n\n# Verbose mode — full cert chain + request headers\nhttpstat -v https://github.com\n\n# JSON output for scripting\nhttpstat --json https://example.com\n\n# JSON benchmark output (pipe to jq, etc.)\nhttpstat --json -n 5 https://example.com\n\n# Send cookies\nhttpstat -b 'session=abc123; lang=en' https://httpbin.org/cookies\n\n# Custom DNS server (plain UDP)\nhttpstat --dns-servers=cloudflare https://example.com\n\n# DNS-over-HTTPS\nhttpstat --dns-servers=cloudflare-doh https://example.com\n\n# DNS-over-TLS\nhttpstat --dns-servers=google-dot https://example.com\n\n# Pretty-print JSON response\nhttpstat --pretty https://httpbin.org/get\n\n# Benchmark — repeat 10 times, show percentile stats\nhttpstat -n 10 https://example.com\n\n# Benchmark with connection reuse — measure warm request latency\nhttpstat -n 10 -K https://example.com\n\n# Only show specific response headers\nhttpstat --include-header content-type --include-header server https://example.com\n\n# Hide noisy response headers\nhttpstat --exclude-header date --exclude-header via https://example.com\n\n# Set timeout\nhttpstat --timeout 5s https://example.com\n\n# mTLS — send client certificate\nhttpstat --cert client.crt --key client.key https://mtls.example.com\n\n# Waterfall bar chart — spot bottlenecks at a glance\nhttpstat --waterfall https://example.com\n\n# Connect-to: test a specific backend without changing DNS or Host header\nhttpstat --connect-to example.com:443:staging.internal:443 https://example.com\n\n# Repeatable for multiple overrides\nhttpstat --connect-to api.example.com:443:192.168.1.10:8443 https://api.example.com\n\n# HTTP proxy\nhttpstat --proxy http://proxy.corp:8080 https://example.com\n\n# SOCKS5 proxy\nhttpstat --proxy socks5://127.0.0.1:1080 https://example.com\n\n# Proxy from environment variable\nHTTPS_PROXY=http://proxy.corp:8080 httpstat https://example.com\n\n# Bind to a specific local IP (multi-NIC / policy routing)\nhttpstat --bind 192.168.1.100 https://example.com\n```\n\n## Options\n\n```\nhttpstat visualizes curl(1) statistics in a way of beauty and clarity.\n\nUsage: httpstat [OPTIONS] [URL_ARG]\n\nArguments:\n  [URL_ARG]  url to request\n\nOptions:\n  -u, --url \u003cURL\u003e                  URL to request (optional, can be provided as the last argument)\n  -H \u003cHEADERS\u003e                     set HTTP header; repeatable: -H 'Accept: ...' -H 'Range: ...'\n  -4                               resolve host to ipv4 only\n  -6                               resolve host to ipv6 only\n  -k                               skip verify tls certificate\n  -o \u003cOUTPUT\u003e                      output file\n  -L                               follow 30x redirects\n  -X \u003cMETHOD\u003e                      HTTP method to use (default GET)\n  -d, --data \u003cDATA\u003e                the body of a POST or PUT request; from file use @filename, from stdin use @-\n      --resolve \u003cRESOLVE\u003e          resolve the request host to specific ip address (e.g. 1.2.3.4,1.2.3.5)\n      --compressed                 request compressed response: gzip, br, zstd\n      --http3                      use http/3\n      --http2                      use http/2\n      --http1                      use http/1.1\n  -s                               silent mode, only output the connect address and result\n      --dns-servers \u003cDNS_SERVERS\u003e  dns server address to use, format: 8.8.8.8,8.8.4.4; presets: google, cloudflare, quad9, google-doh, cloudflare-doh, quad9-doh, google-dot, cloudflare-dot, quad9-dot\n  -v, --verbose                    verbose mode\n      --pretty                     pretty mode\n      --timeout \u003cTIMEOUT\u003e          timeout\n  -n, --count \u003cCOUNT\u003e              number of requests for benchmarking, show min/max/avg/p50/p95/p99 stats\n  -K, --reuse                      reuse connection in benchmark mode (requires -n), test warm request performance\n  -b, --cookie \u003cCOOKIE\u003e            send cookies: 'name=value; name2=value2' or from file use @filename\n      --json                       output results as JSON for scripting and CI/CD\n      --include-header \u003cHEADER\u003e    only show these response headers (repeatable, case-insensitive)\n      --exclude-header \u003cHEADER\u003e    hide these response headers (repeatable, case-insensitive)\n      --waterfall                  show timing as a waterfall bar chart\n      --connect-to \u003cCONNECT_TO\u003e    redirect HOST1:PORT1 to HOST2:PORT2 (repeatable, IPv6 uses [addr])\n      --proxy \u003cPROXY\u003e              proxy URL: http://host:port, https://host:port, socks5://host:port\n      --cert \u003cCERT\u003e                client certificate for mTLS (PEM file)\n      --key \u003cKEY\u003e                  client private key for mTLS (PEM file)\n  -h, --help                       Print help\n  -V, --version                    Print version\n```\n\n## Config File (`~/.httpstatrc`)\n\nSet persistent defaults so you don't have to repeat flags on every invocation.\n\nCreate `~/.httpstatrc` as a JSON object — any field can be omitted. CLI flags always override config values.\n\n```json\n{\n  \"compressed\": true,\n  \"dns_servers\": \"cloudflare\",\n  \"timeout\": \"10s\",\n  \"verbose\": false,\n  \"pretty\": false,\n  \"silent\": false,\n  \"follow_redirect\": false,\n  \"skip_verify\": false,\n  \"http1\": false,\n  \"http2\": false,\n  \"http3\": false,\n  \"json\": false,\n  \"headers\": [\"Accept: application/json\"],\n  \"include_header\": [],\n  \"exclude_header\": [\"date\", \"via\"]\n}\n```\n\n## Exit Codes\n\n| Code | Meaning |\n|------|---------|\n| 0 | Success |\n| 1 | General / unknown error |\n| 2 | DNS resolution failure |\n| 3 | TCP connection failure |\n| 4 | TLS / SSL error |\n| 5 | Timeout |\n| 6 | HTTP 4xx client error |\n| 7 | HTTP 5xx server error |\n\n## License\n\nhttp-stat-rs is provided under the MIT license. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvicanso%2Fhttp-stat-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvicanso%2Fhttp-stat-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvicanso%2Fhttp-stat-rs/lists"}