httptap


Namehttptap JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
SummaryHTTP request visualizer with detailed timing breakdown (DNS → TCP → TLS → HTTP)
upload_time2025-10-29 22:40:59
maintainerSergei Ozeranskii
docs_urlNone
authorSergei Ozeranskii
requires_python>=3.10
licenseApache-2.0
keywords http https performance timing dns tls ssl networking monitoring diagnostics waterfall curl httpx
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # httptap

<table>
  <tr>
    <th>Releases</th>
    <th>CI &amp; Analysis</th>
    <th>Project Info</th>
  </tr>
  <tr>
    <td>
      <a href="https://pypi.org/project/httptap/">
        <img src="https://img.shields.io/pypi/v/httptap?color=3775A9&label=PyPI&logo=pypi" alt="PyPI" />
      </a><br />
      <a href="https://pypi.org/project/httptap/">
        <img src="https://img.shields.io/pypi/pyversions/httptap?logo=python" alt="Python Versions" />
      </a>
    </td>
    <td>
      <a href="https://github.com/ozeranskii/httptap/actions/workflows/ci.yml">
        <img src="https://github.com/ozeranskii/httptap/actions/workflows/ci.yml/badge.svg" alt="CI" />
      </a><br />
      <a href="https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml">
        <img src="https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml/badge.svg" alt="CodeQL" />
      </a><br />
      <a href="https://codecov.io/github/ozeranskii/httptap">
        <img src="https://codecov.io/github/ozeranskii/httptap/graph/badge.svg?token=OFOHOI1X5J" alt="Coverage" />
      </a>
    </td>
    <td>
      <a href="https://github.com/astral-sh/uv">
        <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="Build Tool" />
      </a><br />
      <a href="https://github.com/astral-sh/ruff">
        <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Lint" />
      </a><br />
      <a href="https://github.com/ozeranskii/httptap/blob/main/LICENSE">
        <img src="https://img.shields.io/github/license/ozeranskii/httptap?color=2E7D32" alt="License" />
      </a>
    </td>
  </tr>
</table>

`httptap` is a rich-powered CLI that dissects an HTTP request into every meaningful phase-DNS, TCP connect, TLS
handshake, server wait, and body transfer and renders the results as a timeline table, compact summary, or
machine-friendly metrics. It is designed for interactive troubleshooting, regression analysis, and recording of
performance baselines.

---

## Highlights

- **Phase-by-phase timing** – precise measurements built from httpcore trace hooks (with sane fallbacks when metal-level
  data is unavailable).
- **IPv4/IPv6 aware** – the resolver and TLS inspector report both the address and its family.
- **TLS insights** – certificate CN, expiry countdown, cipher suite, and protocol version are captured automatically.
- **Multiple output modes** – rich waterfall view, compact single-line summaries, or `--metrics-only` for scripting.
- **JSON export** – persist full step data (including redirect chains) for later processing.
- **Extensible** – clean Protocol interfaces for DNS, TLS, timing, visualization, and export so you can plug in custom
  behavior.

> 📣 <strong>Exclusive for httptap users:</strong> Save 50% on <a href="https://gitkraken.cello.so/vY8yybnplsZ"><strong>GitKraken Pro</strong></a>. Bundle GitKraken Client, GitLens for VS Code, and powerful CLI tools to accelerate every repo workflow.

---

## Requirements

- Python 3.10-3.14 (CPython)
- macOS, Linux, or Windows (tested on CPython)
- No system dependencies beyond standard networking
- Code must follow the Google Python Style Guide (docstrings, formatting). See
  [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)

---

## Installation

### Using `uv`

```shell
uv pip install httptap
```

### From source

```shell
git clone https://github.com/ozeranskii/httptap.git
cd httptap
uv pip install .
```

---

## Quick Start

Currently `httptap` issues a `GET` request and streams the entire response body. Other HTTP methods and payloads are not
supported yet; this keeps the interface simple and avoids exposing sensitive request data in output. If you need to
profile `POST`/`PUT` workloads, you can wrap `httptap` and override the request executor to plug in custom behavior.

Run a single request and display a rich waterfall:

```shell
httptap https://httpbin.io
```

Add custom headers (repeat `-H` for multiple values):

```shell
httptap \
  -H "Accept: application/json" \
  -H "Authorization: Bearer super-secret" \
  https://httpbin.io/bearer
```

Follow redirect chains and dump metrics to JSON:

```shell
httptap --follow --json out/report.json https://httpbin.io/redirect/2
```

Collect compact (single-line) timings suitable for logs:

```shell
httptap --compact https://httpbin.io/get
```

Expose raw metrics for scripts:

```shell
httptap --metrics-only https://httpbin.io/get | tee timings.log
```

Programmatic users can inject a custom executor for advanced scenarios. The
default analyzer accepts either a modern `RequestExecutor` implementation or a
legacy callable wrapped with `CallableRequestExecutor`, so new request flags
remain backward compatible.

Bypass TLS verification when troubleshooting self-signed endpoints:

```shell
httptap --ignore-ssl https://self-signed.badssl.com
```

The flag disables certificate validation and relaxes many handshake
constraints so that legacy endpoints (expired/self-signed/hostname
mismatches, weak hashes, older TLS versions) still complete. Some
algorithms removed from modern OpenSSL builds (for example RC4 or
3DES) may remain unavailable. Use this mode only on trusted networks.

Route traffic through an HTTP/SOCKS proxy (explicit override takes precedence over env vars `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`):

```shell
httptap --proxy socks5h://proxy.local:1080 https://example.com
```

The output and JSON export include the proxy URI so you can confirm what
path was used.

---

## Releasing

### Prerequisites

- GitHub Environment `pypi` must be configured in repository settings
- PyPI Trusted Publishing configured for `ozeranskii/httptap`

### Steps

1. Trigger the **Release** workflow from GitHub Actions:
   - Provide exact version (e.g., `0.3.0`), OR
   - Select bump type: `patch`, `minor`, or `major`
2. The workflow will:
   - Update version in `pyproject.toml` using `uv version`
   - Generate changelog with `git-cliff` and update `CHANGELOG.md`
   - Commit changes and create a git tag
   - Run full test suite on the tagged version
   - Build wheel and source distribution
   - Publish to PyPI via Trusted Publishing (OIDC)
   - Create GitHub Release with generated notes

---

## Sample Output

![sample-output.png](docs/assets/sample-output.png)

The redirect summary includes a total row:
![sample-follow-redirects-output.png](docs/assets/sample-follow-redirects-output.png)

---

## JSON Export Structure

```json
{
  "initial_url": "https://httpbin.io/redirect/2",
  "total_steps": 3,
  "steps": [
    {
      "url": "https://httpbin.io/redirect/2",
      "step_number": 1,
      "timing": {
        "dns_ms": 8.947208058089018,
        "connect_ms": 96.97712492197752,
        "tls_ms": 194.56583401188254,
        "ttfb_ms": 445.9513339679688,
        "total_ms": 447.3437919514254,
        "wait_ms": 145.46116697601974,
        "xfer_ms": 1.392457983456552,
        "is_estimated": false
      },
      "network": {
        "ip": "44.211.11.205",
        "ip_family": "IPv4",
        "tls_version": "TLSv1.2",
        "tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
        "cert_cn": "httpbin.io",
        "cert_days_left": 143
      },
      "response": {
        "status": 302,
        "bytes": 0,
        "content_type": null,
        "server": null,
        "date": "2025-10-23T19:20:36+00:00",
        "location": "/relative-redirect/1",
        "headers": {
          "access-control-allow-credentials": "true",
          "access-control-allow-origin": "*",
          "location": "/relative-redirect/1",
          "date": "Thu, 23 Oct 2025 19:20:36 GMT",
          "content-length": "0"
        }
      },
      "error": null,
      "note": null
    },
    {
      "url": "https://httpbin.io/relative-redirect/1",
      "step_number": 2,
      "timing": {
        "dns_ms": 2.6895420160144567,
        "connect_ms": 97.51500003039837,
        "tls_ms": 193.99016606621444,
        "ttfb_ms": 400.2034160075709,
        "total_ms": 400.60841606464237,
        "wait_ms": 106.00870789494365,
        "xfer_ms": 0.4050000570714474,
        "is_estimated": false
      },
      "network": {
        "ip": "44.211.11.205",
        "ip_family": "IPv4",
        "tls_version": "TLSv1.2",
        "tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
        "cert_cn": "httpbin.io",
        "cert_days_left": 143
      },
      "response": {
        "status": 302,
        "bytes": 0,
        "content_type": null,
        "server": null,
        "date": "2025-10-23T19:20:36+00:00",
        "location": "/get",
        "headers": {
          "access-control-allow-credentials": "true",
          "access-control-allow-origin": "*",
          "location": "/get",
          "date": "Thu, 23 Oct 2025 19:20:36 GMT",
          "content-length": "0"
        }
      },
      "error": null,
      "note": null
    },
    {
      "url": "https://httpbin.io/get",
      "step_number": 3,
      "timing": {
        "dns_ms": 2.643457963131368,
        "connect_ms": 97.36416593659669,
        "tls_ms": 197.3062080796808,
        "ttfb_ms": 403.2038329169154,
        "total_ms": 403.9644579170272,
        "wait_ms": 105.89000093750656,
        "xfer_ms": 0.7606250001117587,
        "is_estimated": false
      },
      "network": {
        "ip": "52.70.33.41",
        "ip_family": "IPv4",
        "tls_version": "TLSv1.2",
        "tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
        "cert_cn": "httpbin.io",
        "cert_days_left": 143
      },
      "response": {
        "status": 200,
        "bytes": 389,
        "content_type": "application/json; charset=utf-8",
        "server": null,
        "date": "2025-10-23T19:20:37+00:00",
        "location": null,
        "headers": {
          "access-control-allow-credentials": "true",
          "access-control-allow-origin": "*",
          "content-type": "application/json; charset=utf-8",
          "date": "Thu, 23 Oct 2025 19:20:37 GMT",
          "content-length": "389"
        }
      },
      "error": null,
      "note": null
    }
  ],
  "summary": {
    "total_time_ms": 1251.916665933095,
    "final_status": 200,
    "final_url": "https://httpbin.io/get",
    "final_bytes": 389,
    "errors": 0
  }
}
```

## Metrics-only scripting

```shell
httptap --metrics-only https://httpbin.io/get
```

```terminaloutput
Step 1: dns=30.1 connect=97.3 tls=199.0 ttfb=472.2 total=476.0 status=200 bytes=389 ip=44.211.11.205 family=IPv4
tls_version=TLSv1.2
```

---

## Advanced Usage

### Custom Implementations

Swap in your own resolver or TLS inspector (anything satisfying the Protocol from `httptap.interfaces`):

```python
from httptap import HTTPTapAnalyzer, SystemDNSResolver


class HardcodedDNS(SystemDNSResolver):
    def resolve(self, host, port, timeout):
        return "93.184.216.34", "IPv4", 0.1


analyzer = HTTPTapAnalyzer(dns_resolver=HardcodedDNS())
steps = analyzer.analyze_url("https://httpbin.io")
```

---

## Development

```shell
git clone https://github.com/ozeranskii/httptap.git
cd httptap
uv sync
uv run pytest
uv run ruff check
uv run ruff format .
```

Tests expect outbound network access; you can mock `SystemDNSResolver` / `SocketTLSInspector` when running offline.

---

## Contributing

1. Fork and clone the repo.
2. Create a feature branch.
3. Run `pytest` and `ruff` before committing.
4. Submit a pull request with a clear description and any relevant screenshots or benchmarks.

We welcome bug reports, feature proposals, doc improvements, and creative new visualizations or exporters.

---

## License

Apache License 2.0 © httptap contributors. See [LICENSE](https://github.com/ozeranskii/httptap/blob/main/LICENSE) for
details.

---

## Acknowledgements

- Built on the shoulders of fantastic
  libraries: [httpx](https://www.python-httpx.org/), [httpcore](https://github.com/encode/httpcore),
  and [Rich](https://github.com/Textualize/rich).
- Inspired by the tooling ecosystem around web performance (e.g., DevTools waterfalls, `curl --trace`).
- Special thanks to everyone who opens issues, shares ideas, or contributes patches.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "httptap",
    "maintainer": "Sergei Ozeranskii",
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "http, https, performance, timing, dns, tls, ssl, networking, monitoring, diagnostics, waterfall, curl, httpx",
    "author": "Sergei Ozeranskii",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/30/2c/bb8ab02dcc3e37228db762d513851866ab28d8fc6787a8b7744340a7faea/httptap-0.2.0.tar.gz",
    "platform": null,
    "description": "# httptap\n\n<table>\n  <tr>\n    <th>Releases</th>\n    <th>CI &amp; Analysis</th>\n    <th>Project Info</th>\n  </tr>\n  <tr>\n    <td>\n      <a href=\"https://pypi.org/project/httptap/\">\n        <img src=\"https://img.shields.io/pypi/v/httptap?color=3775A9&label=PyPI&logo=pypi\" alt=\"PyPI\" />\n      </a><br />\n      <a href=\"https://pypi.org/project/httptap/\">\n        <img src=\"https://img.shields.io/pypi/pyversions/httptap?logo=python\" alt=\"Python Versions\" />\n      </a>\n    </td>\n    <td>\n      <a href=\"https://github.com/ozeranskii/httptap/actions/workflows/ci.yml\">\n        <img src=\"https://github.com/ozeranskii/httptap/actions/workflows/ci.yml/badge.svg\" alt=\"CI\" />\n      </a><br />\n      <a href=\"https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml\">\n        <img src=\"https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml/badge.svg\" alt=\"CodeQL\" />\n      </a><br />\n      <a href=\"https://codecov.io/github/ozeranskii/httptap\">\n        <img src=\"https://codecov.io/github/ozeranskii/httptap/graph/badge.svg?token=OFOHOI1X5J\" alt=\"Coverage\" />\n      </a>\n    </td>\n    <td>\n      <a href=\"https://github.com/astral-sh/uv\">\n        <img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json\" alt=\"Build Tool\" />\n      </a><br />\n      <a href=\"https://github.com/astral-sh/ruff\">\n        <img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json\" alt=\"Lint\" />\n      </a><br />\n      <a href=\"https://github.com/ozeranskii/httptap/blob/main/LICENSE\">\n        <img src=\"https://img.shields.io/github/license/ozeranskii/httptap?color=2E7D32\" alt=\"License\" />\n      </a>\n    </td>\n  </tr>\n</table>\n\n`httptap` is a rich-powered CLI that dissects an HTTP request into every meaningful phase-DNS, TCP connect, TLS\nhandshake, server wait, and body transfer and renders the results as a timeline table, compact summary, or\nmachine-friendly metrics. It is designed for interactive troubleshooting, regression analysis, and recording of\nperformance baselines.\n\n---\n\n## Highlights\n\n- **Phase-by-phase timing** \u2013 precise measurements built from httpcore trace hooks (with sane fallbacks when metal-level\n  data is unavailable).\n- **IPv4/IPv6 aware** \u2013 the resolver and TLS inspector report both the address and its family.\n- **TLS insights** \u2013 certificate CN, expiry countdown, cipher suite, and protocol version are captured automatically.\n- **Multiple output modes** \u2013 rich waterfall view, compact single-line summaries, or `--metrics-only` for scripting.\n- **JSON export** \u2013 persist full step data (including redirect chains) for later processing.\n- **Extensible** \u2013 clean Protocol interfaces for DNS, TLS, timing, visualization, and export so you can plug in custom\n  behavior.\n\n> \ud83d\udce3 <strong>Exclusive for httptap users:</strong> Save 50% on <a href=\"https://gitkraken.cello.so/vY8yybnplsZ\"><strong>GitKraken Pro</strong></a>. Bundle GitKraken Client, GitLens for VS Code, and powerful CLI tools to accelerate every repo workflow.\n\n---\n\n## Requirements\n\n- Python 3.10-3.14 (CPython)\n- macOS, Linux, or Windows (tested on CPython)\n- No system dependencies beyond standard networking\n- Code must follow the Google Python Style Guide (docstrings, formatting). See\n  [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)\n\n---\n\n## Installation\n\n### Using `uv`\n\n```shell\nuv pip install httptap\n```\n\n### From source\n\n```shell\ngit clone https://github.com/ozeranskii/httptap.git\ncd httptap\nuv pip install .\n```\n\n---\n\n## Quick Start\n\nCurrently `httptap` issues a `GET` request and streams the entire response body. Other HTTP methods and payloads are not\nsupported yet; this keeps the interface simple and avoids exposing sensitive request data in output. If you need to\nprofile `POST`/`PUT` workloads, you can wrap `httptap` and override the request executor to plug in custom behavior.\n\nRun a single request and display a rich waterfall:\n\n```shell\nhttptap https://httpbin.io\n```\n\nAdd custom headers (repeat `-H` for multiple values):\n\n```shell\nhttptap \\\n  -H \"Accept: application/json\" \\\n  -H \"Authorization: Bearer super-secret\" \\\n  https://httpbin.io/bearer\n```\n\nFollow redirect chains and dump metrics to JSON:\n\n```shell\nhttptap --follow --json out/report.json https://httpbin.io/redirect/2\n```\n\nCollect compact (single-line) timings suitable for logs:\n\n```shell\nhttptap --compact https://httpbin.io/get\n```\n\nExpose raw metrics for scripts:\n\n```shell\nhttptap --metrics-only https://httpbin.io/get | tee timings.log\n```\n\nProgrammatic users can inject a custom executor for advanced scenarios. The\ndefault analyzer accepts either a modern `RequestExecutor` implementation or a\nlegacy callable wrapped with `CallableRequestExecutor`, so new request flags\nremain backward compatible.\n\nBypass TLS verification when troubleshooting self-signed endpoints:\n\n```shell\nhttptap --ignore-ssl https://self-signed.badssl.com\n```\n\nThe flag disables certificate validation and relaxes many handshake\nconstraints so that legacy endpoints (expired/self-signed/hostname\nmismatches, weak hashes, older TLS versions) still complete. Some\nalgorithms removed from modern OpenSSL builds (for example RC4 or\n3DES) may remain unavailable. Use this mode only on trusted networks.\n\nRoute traffic through an HTTP/SOCKS proxy (explicit override takes precedence over env vars `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`):\n\n```shell\nhttptap --proxy socks5h://proxy.local:1080 https://example.com\n```\n\nThe output and JSON export include the proxy URI so you can confirm what\npath was used.\n\n---\n\n## Releasing\n\n### Prerequisites\n\n- GitHub Environment `pypi` must be configured in repository settings\n- PyPI Trusted Publishing configured for `ozeranskii/httptap`\n\n### Steps\n\n1. Trigger the **Release** workflow from GitHub Actions:\n   - Provide exact version (e.g., `0.3.0`), OR\n   - Select bump type: `patch`, `minor`, or `major`\n2. The workflow will:\n   - Update version in `pyproject.toml` using `uv version`\n   - Generate changelog with `git-cliff` and update `CHANGELOG.md`\n   - Commit changes and create a git tag\n   - Run full test suite on the tagged version\n   - Build wheel and source distribution\n   - Publish to PyPI via Trusted Publishing (OIDC)\n   - Create GitHub Release with generated notes\n\n---\n\n## Sample Output\n\n![sample-output.png](docs/assets/sample-output.png)\n\nThe redirect summary includes a total row:\n![sample-follow-redirects-output.png](docs/assets/sample-follow-redirects-output.png)\n\n---\n\n## JSON Export Structure\n\n```json\n{\n  \"initial_url\": \"https://httpbin.io/redirect/2\",\n  \"total_steps\": 3,\n  \"steps\": [\n    {\n      \"url\": \"https://httpbin.io/redirect/2\",\n      \"step_number\": 1,\n      \"timing\": {\n        \"dns_ms\": 8.947208058089018,\n        \"connect_ms\": 96.97712492197752,\n        \"tls_ms\": 194.56583401188254,\n        \"ttfb_ms\": 445.9513339679688,\n        \"total_ms\": 447.3437919514254,\n        \"wait_ms\": 145.46116697601974,\n        \"xfer_ms\": 1.392457983456552,\n        \"is_estimated\": false\n      },\n      \"network\": {\n        \"ip\": \"44.211.11.205\",\n        \"ip_family\": \"IPv4\",\n        \"tls_version\": \"TLSv1.2\",\n        \"tls_cipher\": \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"cert_cn\": \"httpbin.io\",\n        \"cert_days_left\": 143\n      },\n      \"response\": {\n        \"status\": 302,\n        \"bytes\": 0,\n        \"content_type\": null,\n        \"server\": null,\n        \"date\": \"2025-10-23T19:20:36+00:00\",\n        \"location\": \"/relative-redirect/1\",\n        \"headers\": {\n          \"access-control-allow-credentials\": \"true\",\n          \"access-control-allow-origin\": \"*\",\n          \"location\": \"/relative-redirect/1\",\n          \"date\": \"Thu, 23 Oct 2025 19:20:36 GMT\",\n          \"content-length\": \"0\"\n        }\n      },\n      \"error\": null,\n      \"note\": null\n    },\n    {\n      \"url\": \"https://httpbin.io/relative-redirect/1\",\n      \"step_number\": 2,\n      \"timing\": {\n        \"dns_ms\": 2.6895420160144567,\n        \"connect_ms\": 97.51500003039837,\n        \"tls_ms\": 193.99016606621444,\n        \"ttfb_ms\": 400.2034160075709,\n        \"total_ms\": 400.60841606464237,\n        \"wait_ms\": 106.00870789494365,\n        \"xfer_ms\": 0.4050000570714474,\n        \"is_estimated\": false\n      },\n      \"network\": {\n        \"ip\": \"44.211.11.205\",\n        \"ip_family\": \"IPv4\",\n        \"tls_version\": \"TLSv1.2\",\n        \"tls_cipher\": \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"cert_cn\": \"httpbin.io\",\n        \"cert_days_left\": 143\n      },\n      \"response\": {\n        \"status\": 302,\n        \"bytes\": 0,\n        \"content_type\": null,\n        \"server\": null,\n        \"date\": \"2025-10-23T19:20:36+00:00\",\n        \"location\": \"/get\",\n        \"headers\": {\n          \"access-control-allow-credentials\": \"true\",\n          \"access-control-allow-origin\": \"*\",\n          \"location\": \"/get\",\n          \"date\": \"Thu, 23 Oct 2025 19:20:36 GMT\",\n          \"content-length\": \"0\"\n        }\n      },\n      \"error\": null,\n      \"note\": null\n    },\n    {\n      \"url\": \"https://httpbin.io/get\",\n      \"step_number\": 3,\n      \"timing\": {\n        \"dns_ms\": 2.643457963131368,\n        \"connect_ms\": 97.36416593659669,\n        \"tls_ms\": 197.3062080796808,\n        \"ttfb_ms\": 403.2038329169154,\n        \"total_ms\": 403.9644579170272,\n        \"wait_ms\": 105.89000093750656,\n        \"xfer_ms\": 0.7606250001117587,\n        \"is_estimated\": false\n      },\n      \"network\": {\n        \"ip\": \"52.70.33.41\",\n        \"ip_family\": \"IPv4\",\n        \"tls_version\": \"TLSv1.2\",\n        \"tls_cipher\": \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"cert_cn\": \"httpbin.io\",\n        \"cert_days_left\": 143\n      },\n      \"response\": {\n        \"status\": 200,\n        \"bytes\": 389,\n        \"content_type\": \"application/json; charset=utf-8\",\n        \"server\": null,\n        \"date\": \"2025-10-23T19:20:37+00:00\",\n        \"location\": null,\n        \"headers\": {\n          \"access-control-allow-credentials\": \"true\",\n          \"access-control-allow-origin\": \"*\",\n          \"content-type\": \"application/json; charset=utf-8\",\n          \"date\": \"Thu, 23 Oct 2025 19:20:37 GMT\",\n          \"content-length\": \"389\"\n        }\n      },\n      \"error\": null,\n      \"note\": null\n    }\n  ],\n  \"summary\": {\n    \"total_time_ms\": 1251.916665933095,\n    \"final_status\": 200,\n    \"final_url\": \"https://httpbin.io/get\",\n    \"final_bytes\": 389,\n    \"errors\": 0\n  }\n}\n```\n\n## Metrics-only scripting\n\n```shell\nhttptap --metrics-only https://httpbin.io/get\n```\n\n```terminaloutput\nStep 1: dns=30.1 connect=97.3 tls=199.0 ttfb=472.2 total=476.0 status=200 bytes=389 ip=44.211.11.205 family=IPv4\ntls_version=TLSv1.2\n```\n\n---\n\n## Advanced Usage\n\n### Custom Implementations\n\nSwap in your own resolver or TLS inspector (anything satisfying the Protocol from `httptap.interfaces`):\n\n```python\nfrom httptap import HTTPTapAnalyzer, SystemDNSResolver\n\n\nclass HardcodedDNS(SystemDNSResolver):\n    def resolve(self, host, port, timeout):\n        return \"93.184.216.34\", \"IPv4\", 0.1\n\n\nanalyzer = HTTPTapAnalyzer(dns_resolver=HardcodedDNS())\nsteps = analyzer.analyze_url(\"https://httpbin.io\")\n```\n\n---\n\n## Development\n\n```shell\ngit clone https://github.com/ozeranskii/httptap.git\ncd httptap\nuv sync\nuv run pytest\nuv run ruff check\nuv run ruff format .\n```\n\nTests expect outbound network access; you can mock `SystemDNSResolver` / `SocketTLSInspector` when running offline.\n\n---\n\n## Contributing\n\n1. Fork and clone the repo.\n2. Create a feature branch.\n3. Run `pytest` and `ruff` before committing.\n4. Submit a pull request with a clear description and any relevant screenshots or benchmarks.\n\nWe welcome bug reports, feature proposals, doc improvements, and creative new visualizations or exporters.\n\n---\n\n## License\n\nApache License 2.0 \u00a9 httptap contributors. See [LICENSE](https://github.com/ozeranskii/httptap/blob/main/LICENSE) for\ndetails.\n\n---\n\n## Acknowledgements\n\n- Built on the shoulders of fantastic\n  libraries: [httpx](https://www.python-httpx.org/), [httpcore](https://github.com/encode/httpcore),\n  and [Rich](https://github.com/Textualize/rich).\n- Inspired by the tooling ecosystem around web performance (e.g., DevTools waterfalls, `curl --trace`).\n- Special thanks to everyone who opens issues, shares ideas, or contributes patches.\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "HTTP request visualizer with detailed timing breakdown (DNS \u2192 TCP \u2192 TLS \u2192 HTTP)",
    "version": "0.2.0",
    "project_urls": {
        "Changelog": "https://github.com/ozeranskii/httptap/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/ozeranskii/httptap?tab=readme-ov-file",
        "Homepage": "https://github.com/ozeranskii/httptap",
        "Issues": "https://github.com/ozeranskii/httptap/issues",
        "Repository": "https://github.com/ozeranskii/httptap.git"
    },
    "split_keywords": [
        "http",
        " https",
        " performance",
        " timing",
        " dns",
        " tls",
        " ssl",
        " networking",
        " monitoring",
        " diagnostics",
        " waterfall",
        " curl",
        " httpx"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c45f4b5a68935940f7a64dff5e5ef41ccb11c2efe3940f1984e26ff6cbc57156",
                "md5": "8fcd017b3cf5509d5a0a1a3350a3e452",
                "sha256": "d43345b012b80d60d03fdebf8313dd532e653c7f3a0161a7a0af53cda1983f83"
            },
            "downloads": -1,
            "filename": "httptap-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8fcd017b3cf5509d5a0a1a3350a3e452",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 43764,
            "upload_time": "2025-10-29T22:40:57",
            "upload_time_iso_8601": "2025-10-29T22:40:57.635996Z",
            "url": "https://files.pythonhosted.org/packages/c4/5f/4b5a68935940f7a64dff5e5ef41ccb11c2efe3940f1984e26ff6cbc57156/httptap-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "302cbb8ab02dcc3e37228db762d513851866ab28d8fc6787a8b7744340a7faea",
                "md5": "d95679b958a425d7f0809f4b47c14e47",
                "sha256": "50a029b1fe4d67cd42e39f92e955b87b2377c0149409390e44c891f716c99394"
            },
            "downloads": -1,
            "filename": "httptap-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d95679b958a425d7f0809f4b47c14e47",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 35581,
            "upload_time": "2025-10-29T22:40:59",
            "upload_time_iso_8601": "2025-10-29T22:40:59.354353Z",
            "url": "https://files.pythonhosted.org/packages/30/2c/bb8ab02dcc3e37228db762d513851866ab28d8fc6787a8b7744340a7faea/httptap-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-29 22:40:59",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ozeranskii",
    "github_project": "httptap",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "httptap"
}
        
Elapsed time: 2.65514s