http-stat 0.7.1

httpstat visualizes curl(1) statistics in a way of beauty and clarity.
Documentation
# http-stat-rs

模仿是最真诚的致敬。

纯 Rust 编写的**零依赖、单二进制**HTTP 诊断工具。一条命令可视化完整请求生命周期 — DNS 解析、TCP 连接、TLS 握手、服务端处理、内容传输 — 一目了然。参考了 [httpstat](https://github.com/davecheney/httpstat)。

![截图](./screenshot.png)

## 亮点

- **HTTP/1.1、HTTP/2 和 HTTP/3 (QUIC)** — 全面支持现代协议,一个参数即可切换
- **gRPC 健康检查** — 使用 `grpc://``grpcs://` 协议直接探测 gRPC 服务;`grpcs://` 执行真实的 rustls 握手(支持 `-k` 与 mTLS),并报告 TLS 耗时和证书详情
- **请求发送阶段独立计时** — 将请求体上传与服务端处理拆开,POST/PUT 上传慢不再被误判为"服务器慢"
- **Server-Timing 解析** — 解析 RFC 8673 `Server-Timing` 响应头,把服务端报告的子阶段耗时(CDN edge / origin / worker 等)直接展开在 TTFB 之下
- **基准测试模式**`-n 10` 重复 N 次输出 min/max/avg/p50/p95/p99;加 `-K` 复用连接,对比冷启动与热请求延迟
- **多 IP 并发测试**`--resolve` 同时测试多个 IP,结果按成功/失败排序
- **透明解压**`--compressed` 自动解码 `gzip``br``zstd` 响应
- **自定义 DNS** — 指定 DNS 服务器 IP 或使用内置预设:`google``cloudflare``quad9`;DoH/DoT 预设:`google-doh``cloudflare-doh``quad9-doh``google-dot``cloudflare-dot``quad9-dot`
- **DoH/DoT 阶段拆分** — 使用 DoH 或 DoT 时,DNS 一列会拆成 `DNS Connect`(到解析服务器的 TCP+TLS 握手)与 `DNS Query`,让你看出"DoH 慢"是慢在连 DNS 服务器还是慢在查询本身
- **内核 TCP 统计** — Linux 和 macOS 下会在 `connect(2)` 完成后和读完响应体后各采样一次 `getsockopt(TCP_INFO)``--verbose` 或独立的 `--tcp-info` 开关下展示 RTT / MSS / cwnd 以及本次请求期间的重传次数,可以把"Content Transfer 慢"判定到丢包、TCP 慢启动还是应用层延迟。经 HTTP/SOCKS 代理时采样反映客户端到代理的 socket,而非到源站。
- **下载吞吐 + 慢启动拆分** — 响应体大于 1 MiB 时,会在 `Body size` 旁加一行 `Throughput: X MB/s``--verbose` 下进一步拆成"首 100 KB"与"后续"两段速率,可以把"TCP 慢启动主导"和"服务器流式推得慢"两类问题区分开。
- **中英双语输出**`--lang en|zh` 显式指定显示语言;不指定时自动读取 `LC_ALL` / `LC_MESSAGES` / `LANG``zh*` 走中文),无匹配则回退英文。JSON 输出始终保持英文键,避免影响下游脚本。
- **JSON 输出**`--json` 方便脚本集成、CI/CD 流水线和监控系统对接。完整的输出契约见 [JSON_SCHEMA.md]./JSON_SCHEMA.md,包含退出码、可选字段以及可直接复用的 `jq` 配方
- **TLS 证书检查** — verbose 模式展示完整证书链、密码套件、SAN 域名及有效期
- **TLS 握手诊断** — 每次 HTTPS 请求都会报告握手类型(`Full` / `Resumed`)、服务器是否进行 OCSP stapling,以及在 `-n` 基准测试模式下后续请求是否接受了 0-RTT 早期数据
- **Cookie 支持**`-b 'k=v'``-b @file`,配合 `-L` 同 host 重定向自动合并 `Set-Cookie`
- **符合规范的重定向**`-L` 最多跟随 10 跳并解析相对 `Location`;请求方法按 RFC 9110 降级(303 → GET,301/302 的 POST → GET,307/308 保持不变),跨 host 重定向时剥离 `Authorization`、Cookie 与 `--resolve` 钉扎,避免凭据泄露给第三方
- **ALPN 协议协商展示** — 每次响应明确显示客户端与服务端最终协商出的协议版本(`HTTP/1.1``H2``H3`),清楚知道实际使用了哪个版本
- **Alt-Svc 自动升级**`--alt-svc` 检测响应中广告的 HTTP/3 端点(RFC 7838),自动用 h3 重试一次,无需手动 `--http3` 就能看到真实的 h3 耗时
- **JSON 字段选择器**`--jq '.items[].name'` 直接从响应体提取所需字段(支持 `.a.b``.[0]``.[]`);遇到不支持的语法或非 JSON 响应体会明确报错,而不是静默输出完整 body
- **JSON 格式化输出**`--pretty` 原地美化响应体;配合 `--jq` 使用,输出更聚焦、更易读
- **响应头过滤**`--include-header` 只显示关注的响应头;`--exclude-header` 隐藏噪音字段
- **curl 风格操作** — 熟悉的参数(`-H``-X``-d``-L``-k``-o``-4`/`-6`),无缝上手
- **Waterfall 图表**`--waterfall` 将每个阶段渲染为横向进度条,瓶颈一目了然(类似 Chrome DevTools Network 面板)
- **`--connect-to`** — 在 TCP 层将 `HOST1:PORT1` 重定向到 `HOST2:PORT2`,TLS SNI 和 `Host` 头保持不变,与 curl 的 `--connect-to` 一致
- **代理支持**`--proxy` 支持 HTTP/HTTPS/SOCKS5 代理;同时读取 `HTTP_PROXY``HTTPS_PROXY``ALL_PROXY` 环境变量
- **源 IP 绑定**`--bind <IP>` 将出站连接绑定到指定本地地址,多网卡环境、策略路由或验证特定网卡可达性时不可或缺
- **mTLS(双向 TLS)**`--cert`/`--key` 发送客户端证书,适用于零信任网络和服务网格
- **配置文件**`~/.httpstatrc` 设置持久化默认值(DNS、超时、请求头等),CLI 参数始终优先
- **细粒度超时**`--timeout` 作用于每个阶段,包括等待响应头与响应体传输,服务器只连接不应答也无法把进程吊死;`--connect-timeout` 仅限制连接阶段(DNS + TCP + TLS/QUIC);`--max-time` 是覆盖响应体与重定向的整体墙钟上限
- **自动重试**`--retry N` 对瞬时失败(超时、连接错误、HTTP 408/429/500/502/503/504)按指数退避重试,或用 `--retry-delay` 指定固定间隔;适合不稳定的 CI 门禁
- **响应体大小上限** — 响应体整体缓冲在内存中,`--max-filesize` 在超限时直接中止传输(默认 1GB,`0` 表示不限),防止失控的超大响应耗尽内存
- **语义化退出码** — DNS、TCP、TLS、超时、4xx、5xx 各有独立退出码,脚本判断更便捷
- **极小体积** — release 构建采用 LTO + `opt-level=z` + strip,通常 < 5 MB
- **真正的零系统依赖** — 静态链接,不依赖 libcurl、OpenSSL 或 libc(musl 构建),可直接放入 `scratch``alpine` Docker 镜像用于生产环境排查

## 安装

### 一键安装(Linux & macOS)

```bash
curl -fsSL https://raw.githubusercontent.com/vicanso/http-stat-rs/main/install.sh | sh
```

### 预编译二进制

为 Windows、macOS 和 Linux 提供[预编译二进制文件](https://github.com/vicanso/http-stat-rs/releases)。

```bash
# Linux
curl -L https://github.com/vicanso/http-stat-rs/releases/latest/download/httpstat-linux-musl-$(uname -m).tar.gz | tar -xzf -
sudo mv httpstat /usr/local/bin/

# macOS
curl -L https://github.com/vicanso/http-stat-rs/releases/latest/download/httpstat-darwin-$(uname -m).tar.gz | tar -xzf -
sudo mv httpstat /usr/local/bin/
```

### 从源码安装

```bash
cargo install http-stat
```

## 请求生命周期

每个 HTTP 请求最多经历六个串行阶段,httpstat 对每个阶段单独计时:

```
  DNS 解析     TCP 连接     TLS 握手      请求发送       服务端处理        内容传输
[────────────][────────────][──────────────][────────────][──────────────────][───────────────]
      │              │              │             │              │                  │
  域名解析       三次握手        TLS/SSL      请求头 +        等待首字节           下载响应
  → IP 地址    SYN 交换       协商加密     请求体写入       (纯服务延迟)          正文
                             (仅 HTTPS)    传输层
                                                                              Total = 所有阶段之和
```

| 阶段 | 含义 |
|---|---|
| DNS 解析 | 将域名解析为 IP 地址所花费的时间 |
| TCP 连接 | 完成三次握手建立 TCP 连接的时间 |
| TLS 握手 | 协商 TLS 会话的时间(仅 HTTPS/HTTP2/HTTP3) |
| 请求发送 | 将请求头与请求体写入传输层的时间——POST/PUT 上传大 body 时尤其重要 |
| 服务端处理 | 从发出最后一个请求字节到收到第一个响应字节的时间——纯服务器延迟 |
| 内容传输 | 下载完整响应正文的时间 |

> HTTP/3 中,**QUIC 连接**阶段取代了 TCP 连接和 TLS 握手(QUIC 将传输层与加密握手合并为一步完成)。

如果服务器返回了 `Server-Timing` 响应头,httpstat 会自动解析并在时间线之上展示服务端报告的子阶段耗时——可以在终端里直接把一段慢 "服务端处理" 拆成 CDN edge / origin / worker 等具体来源。

## 使用示例

```bash
# 基础用法 — 通过 ALPN 自动协商 HTTP/2
httpstat https://www.cloudflare.com/

# HTTP/3 (QUIC) + 压缩响应
httpstat --http3 --compressed https://cloudflare-quic.com/

# 当服务器通过 Alt-Svc 广告 HTTP/3 时自动升级
httpstat --alt-svc https://cloudflare.com

# 多 IP 并发测试,静默模式
httpstat --resolve=183.240.99.169,2409:8c54:870:310:0:ff:b0ed:40ac -s https://www.baidu.com/

# POST 请求,从文件读取请求体
httpstat -X POST -d @payload.json -H 'Content-Type: application/json' https://httpbin.org/post

# POST 请求,从 stdin 读取请求体
echo '{"key":"value"}' | httpstat -X POST -d @- -H 'Content-Type: application/json' https://httpbin.org/post

# gRPC 健康检查
httpstat grpc://localhost:50051

# 详细模式 — 展示完整证书链和请求头
httpstat -v https://github.com

# JSON 输出,方便脚本处理
httpstat --json https://example.com

# JSON 基准测试输出(可配合 jq 使用)
httpstat --json -n 5 https://example.com

# 发送 Cookie
httpstat -b 'session=abc123; lang=en' https://httpbin.org/cookies

# 指定 DNS 服务器(明文 UDP)
httpstat --dns-servers=cloudflare https://example.com

# DNS-over-HTTPS
httpstat --dns-servers=cloudflare-doh https://example.com

# DNS-over-TLS
httpstat --dns-servers=google-dot https://example.com

# JSON 响应格式化输出
httpstat --pretty https://httpbin.org/get

# 基准测试 — 重复 10 次,输出百分位统计
httpstat -n 10 https://example.com

# 连接复用基准测试 — 测量热请求延迟
httpstat -n 10 -K https://example.com

# 只显示指定响应头
httpstat --include-header content-type --include-header server https://example.com

# 隐藏特定响应头
httpstat --exclude-header date --exclude-header via https://example.com

# 为每个阶段设置超时
httpstat --timeout 5s https://example.com

# 仅限制连接阶段,并单独设置整体墙钟上限
httpstat --connect-timeout 3s --max-time 30s https://example.com

# 对瞬时失败按指数退避重试(适合 CI 门禁)
httpstat --retry 3 https://example.com

# 用固定间隔重试,而非退避
httpstat --retry 5 --retry-delay 2s https://example.com

# 响应体超过 10MB 直接中止(默认上限 1GB)
httpstat --max-filesize 10MB https://example.com/big.bin

# mTLS — 发送客户端证书
httpstat --cert client.crt --key client.key https://mtls.example.com

# Waterfall 图表 — 一眼看出瓶颈所在
httpstat --waterfall https://example.com

# connect-to:测试指定后端,不影响 DNS 或 Host 头
httpstat --connect-to example.com:443:staging.internal:443 https://example.com

# 可重复指定多个重定向规则
httpstat --connect-to api.example.com:443:192.168.1.10:8443 https://api.example.com

# HTTP 代理
httpstat --proxy http://proxy.corp:8080 https://example.com

# SOCKS5 代理
httpstat --proxy socks5://127.0.0.1:1080 https://example.com

# 从环境变量读取代理
HTTPS_PROXY=http://proxy.corp:8080 httpstat https://example.com

# 绑定指定本地 IP(多网卡 / 策略路由)
httpstat --bind 192.168.1.100 https://example.com
```

## 选项

```
httpstat 以美观清晰的方式展示 curl(1) 的统计信息。

用法: httpstat [选项] [URL参数]

参数:
  [URL参数]  要请求的 URL

选项:
  -u, --url <URL>                  要请求的 URL(可选,可以作为最后一个参数提供)
  -H <HEADERS>                     设置 HTTP 头;可重复使用:-H 'Accept: ...' -H 'Range: ...'
  -4                               仅使用 IPv4 解析主机
  -6                               仅使用 IPv6 解析主机
  -k                               跳过 TLS 证书验证
  -o <OUTPUT>                      输出文件
  -L                               跟随 30x 重定向
  -X <METHOD>                      使用的 HTTP 方法(默认为 GET)
  -d, --data <DATA>                POST 或 PUT 请求的请求体;从文件读取使用 @filename,从 stdin 读取使用 @-
      --resolve <RESOLVE>          解析域名到指定 IP(例如 1.2.3.4,1.2.3.5)
      --compressed                 请求压缩响应:gzip, br, zstd
      --http3                      使用 HTTP/3
      --http2                      使用 HTTP/2
      --http1                      使用 HTTP/1.1
      --alt-svc                    若响应通过 Alt-Svc 广告 HTTP/3,则用 h3 重试一次
  -s                               静默模式,仅输出连接地址和结果
      --dns-servers <DNS_SERVERS>  指定 DNS 服务器,格式:8.8.8.8,8.8.4.4;预设:google、cloudflare、quad9、google-doh、cloudflare-doh、quad9-doh、google-dot、cloudflare-dot、quad9-dot
  -v, --verbose                    详细模式
      --pretty                     格式化输出模式
      --waterfall                  以 waterfall 条形图展示各阶段耗时
      --tcp-info                   显示内核 TCP_INFO 统计(RTT、cwnd、重传);仅 Linux + macOS
      --lang <LANG>                显示语言:en | zh(默认跟随系统)
      --timeout <TIMEOUT>          超时时间(作用于每个阶段:DNS、TCP、TLS、请求、QUIC)
      --connect-timeout <DUR>      仅连接阶段的超时(DNS + TCP + TLS/QUIC),例如 5s
      --max-time <DUR>             整个操作(含响应体与重定向)的总时限,例如 30s
      --retry <RETRY>              瞬时失败时最多重试 N 次(超时、连接错误、408/429/5xx)
      --retry-delay <DUR>          重试之间的固定间隔(例如 2s);默认使用指数退避
      --max-filesize <SIZE>        响应体缓冲上限,例如 100MB(默认 1GB);0 表示不限
  -n, --count <COUNT>              基准测试请求次数,输出 min/max/avg/p50/p95/p99 统计
  -K, --reuse                      基准测试中复用连接(需配合 -n),测试热请求性能
  -b, --cookie <COOKIE>            发送 Cookie:'name=value; name2=value2' 或从文件读取 @filename
      --json                       以 JSON 格式输出结果,方便脚本和 CI/CD 使用
      --connect-to <CONNECT_TO>    在 TCP 层将 HOST1:PORT1 重定向到 HOST2:PORT2(可重复);TLS SNI 与 Host 头保持不变
      --proxy <PROXY>              代理 URL:http://host:port、https://host:port、socks5://host:port
      --cert <CERT>                mTLS 客户端证书(PEM 文件)
      --key <KEY>                  mTLS 客户端私钥(PEM 文件)
      --bind <BIND>                绑定到指定本地 IP(例如 192.168.1.100 或 ::1)
      --jq <JQ>                    用 jq 风格选择器过滤 JSON 响应体(例如 ".items[].name")
      --include-header <HEADER>    只显示指定响应头(可重复,不区分大小写)
      --exclude-header <HEADER>    隐藏指定响应头(可重复,不区分大小写)
  -h, --help                       显示帮助信息
  -V, --version                    显示版本信息
```

## 配置文件(`~/.httpstatrc`

设置持久化默认值,避免每次输入重复参数。

创建 `~/.httpstatrc`,内容为 JSON 对象,所有字段均可省略。CLI 参数始终覆盖配置文件中的值。

```json
{
  "compressed": true,
  "dns_servers": "cloudflare",
  "timeout": "10s",
  "connect_timeout": "5s",
  "max_time": "30s",
  "retry": 3,
  "retry_delay": "2s",
  "max_filesize": "1GB",
  "verbose": false,
  "pretty": false,
  "silent": false,
  "follow_redirect": false,
  "skip_verify": false,
  "http1": false,
  "http2": false,
  "http3": false,
  "alt_svc": false,
  "json": false,
  "headers": ["Accept: application/json"],
  "include_header": [],
  "exclude_header": ["date", "via"]
}
```

## 退出码

| 退出码 | 含义 |
|--------|------|
| 0 | 成功 |
| 1 | 通用 / 未知错误 |
| 2 | DNS 解析失败 |
| 3 | TCP 连接失败 |
| 4 | TLS / SSL 错误 |
| 5 | 超时 |
| 6 | HTTP 4xx 客户端错误 |
| 7 | HTTP 5xx 服务端错误 |

## 许可证

http-stat-rs 使用 MIT 许可证。详见 [LICENSE](LICENSE)。