httptap 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- httptap-0.2.1/PKG-INFO +519 -0
- httptap-0.2.1/README.md +477 -0
- httptap-0.2.1/httptap/__init__.py +77 -0
- httptap-0.2.1/httptap/_pkgmeta.py +90 -0
- httptap-0.2.1/httptap/analyzer.py +257 -0
- httptap-0.2.1/httptap/cli.py +410 -0
- httptap-0.2.1/httptap/constants.py +33 -0
- httptap-0.2.1/httptap/exporter.py +155 -0
- httptap-0.2.1/httptap/formatters.py +197 -0
- httptap-0.2.1/httptap/http_client.py +483 -0
- httptap-0.2.1/httptap/implementations/__init__.py +26 -0
- httptap-0.2.1/httptap/implementations/dns.py +130 -0
- httptap-0.2.1/httptap/implementations/timing.py +58 -0
- httptap-0.2.1/httptap/implementations/tls.py +80 -0
- httptap-0.2.1/httptap/interfaces.py +218 -0
- httptap-0.2.1/httptap/models.py +272 -0
- httptap-0.2.1/httptap/render.py +259 -0
- httptap-0.2.1/httptap/request_executor.py +129 -0
- httptap-0.2.1/httptap/tls_inspector.py +196 -0
- httptap-0.2.1/httptap/utils.py +211 -0
- httptap-0.2.1/httptap/visualizer.py +152 -0
- httptap-0.2.1/pyproject.toml +196 -0
httptap-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: httptap
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: HTTP request visualizer with detailed timing breakdown (DNS → TCP → TLS → HTTP)
|
|
5
|
+
Keywords: http,https,performance,timing,dns,tls,ssl,networking,monitoring,diagnostics,waterfall,curl,httpx
|
|
6
|
+
Author: Sergei Ozeranskii
|
|
7
|
+
License: Apache-2.0
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: System Administrators
|
|
11
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
12
|
+
Classifier: Topic :: System :: Networking :: Monitoring
|
|
13
|
+
Classifier: Topic :: Software Development :: Testing
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Environment :: Console
|
|
24
|
+
Classifier: Operating System :: OS Independent
|
|
25
|
+
Classifier: Operating System :: POSIX
|
|
26
|
+
Classifier: Operating System :: MacOS
|
|
27
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Dist: httpx[http2,socks]>=0.28.1
|
|
30
|
+
Requires-Dist: rich>=14.2.0
|
|
31
|
+
Requires-Dist: dnspython>=2.8.0
|
|
32
|
+
Requires-Dist: argcomplete>=3.6.3 ; extra == 'completion'
|
|
33
|
+
Maintainer: Sergei Ozeranskii
|
|
34
|
+
Requires-Python: >=3.10
|
|
35
|
+
Project-URL: Changelog, https://github.com/ozeranskii/httptap/blob/main/CHANGELOG.md
|
|
36
|
+
Project-URL: Documentation, https://github.com/ozeranskii/httptap?tab=readme-ov-file
|
|
37
|
+
Project-URL: Homepage, https://github.com/ozeranskii/httptap
|
|
38
|
+
Project-URL: Issues, https://github.com/ozeranskii/httptap/issues
|
|
39
|
+
Project-URL: Repository, https://github.com/ozeranskii/httptap.git
|
|
40
|
+
Provides-Extra: completion
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# httptap
|
|
44
|
+
|
|
45
|
+
<table>
|
|
46
|
+
<tr>
|
|
47
|
+
<th>Releases</th>
|
|
48
|
+
<th>CI & Analysis</th>
|
|
49
|
+
<th>Project Info</th>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td>
|
|
53
|
+
<a href="https://pypi.org/project/httptap/">
|
|
54
|
+
<img src="https://img.shields.io/pypi/v/httptap?color=3775A9&label=PyPI&logo=pypi" alt="PyPI" />
|
|
55
|
+
</a><br />
|
|
56
|
+
<a href="https://pypi.org/project/httptap/">
|
|
57
|
+
<img src="https://img.shields.io/pypi/pyversions/httptap?logo=python" alt="Python Versions" />
|
|
58
|
+
</a>
|
|
59
|
+
</td>
|
|
60
|
+
<td>
|
|
61
|
+
<a href="https://github.com/ozeranskii/httptap/actions/workflows/ci.yml">
|
|
62
|
+
<img src="https://github.com/ozeranskii/httptap/actions/workflows/ci.yml/badge.svg" alt="CI" />
|
|
63
|
+
</a><br />
|
|
64
|
+
<a href="https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml">
|
|
65
|
+
<img src="https://github.com/ozeranskii/httptap/actions/workflows/codeql.yml/badge.svg" alt="CodeQL" />
|
|
66
|
+
</a><br />
|
|
67
|
+
<a href="https://codecov.io/github/ozeranskii/httptap">
|
|
68
|
+
<img src="https://codecov.io/github/ozeranskii/httptap/graph/badge.svg?token=OFOHOI1X5J" alt="Coverage" />
|
|
69
|
+
</a>
|
|
70
|
+
</td>
|
|
71
|
+
<td>
|
|
72
|
+
<a href="https://github.com/astral-sh/uv">
|
|
73
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="Build Tool" />
|
|
74
|
+
</a><br />
|
|
75
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
76
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Lint" />
|
|
77
|
+
</a><br />
|
|
78
|
+
<a href="https://github.com/ozeranskii/httptap/blob/main/LICENSE">
|
|
79
|
+
<img src="https://img.shields.io/github/license/ozeranskii/httptap?color=2E7D32" alt="License" />
|
|
80
|
+
</a>
|
|
81
|
+
</td>
|
|
82
|
+
</tr>
|
|
83
|
+
</table>
|
|
84
|
+
|
|
85
|
+
`httptap` is a rich-powered CLI that dissects an HTTP request into every meaningful phase-DNS, TCP connect, TLS
|
|
86
|
+
handshake, server wait, and body transfer and renders the results as a timeline table, compact summary, or
|
|
87
|
+
machine-friendly metrics. It is designed for interactive troubleshooting, regression analysis, and recording of
|
|
88
|
+
performance baselines.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Highlights
|
|
93
|
+
|
|
94
|
+
- **Phase-by-phase timing** – precise measurements built from httpcore trace hooks (with sane fallbacks when metal-level
|
|
95
|
+
data is unavailable).
|
|
96
|
+
- **IPv4/IPv6 aware** – the resolver and TLS inspector report both the address and its family.
|
|
97
|
+
- **TLS insights** – certificate CN, expiry countdown, cipher suite, and protocol version are captured automatically.
|
|
98
|
+
- **Multiple output modes** – rich waterfall view, compact single-line summaries, or `--metrics-only` for scripting.
|
|
99
|
+
- **JSON export** – persist full step data (including redirect chains) for later processing.
|
|
100
|
+
- **Extensible** – clean Protocol interfaces for DNS, TLS, timing, visualization, and export so you can plug in custom
|
|
101
|
+
behavior.
|
|
102
|
+
|
|
103
|
+
> 📣 <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.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Requirements
|
|
108
|
+
|
|
109
|
+
- Python 3.10-3.14 (CPython)
|
|
110
|
+
- macOS, Linux, or Windows (tested on CPython)
|
|
111
|
+
- No system dependencies beyond standard networking
|
|
112
|
+
- Code must follow the Google Python Style Guide (docstrings, formatting). See
|
|
113
|
+
[Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Installation
|
|
118
|
+
|
|
119
|
+
### Using Homebrew (macOS/Linux)
|
|
120
|
+
|
|
121
|
+
```shell
|
|
122
|
+
brew install httptap
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Using `uv`
|
|
126
|
+
|
|
127
|
+
```shell
|
|
128
|
+
uv pip install httptap
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Using `pip`
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
pip install httptap
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### From source
|
|
138
|
+
|
|
139
|
+
```shell
|
|
140
|
+
git clone https://github.com/ozeranskii/httptap.git
|
|
141
|
+
cd httptap
|
|
142
|
+
uv venv
|
|
143
|
+
uv pip install .
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Shell completions
|
|
149
|
+
|
|
150
|
+
#### Homebrew Installation
|
|
151
|
+
|
|
152
|
+
If you installed httptap via Homebrew, shell completions are automatically available after installation. Just restart your shell:
|
|
153
|
+
|
|
154
|
+
```shell
|
|
155
|
+
# Restart your shell or reload configuration
|
|
156
|
+
exec $SHELL
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Homebrew automatically installs completions to:
|
|
160
|
+
- Bash: `$(brew --prefix)/etc/bash_completion.d/`
|
|
161
|
+
- Zsh: `$(brew --prefix)/share/zsh/site-functions/`
|
|
162
|
+
|
|
163
|
+
#### Python Package Installation
|
|
164
|
+
|
|
165
|
+
If you installed httptap via `pip` or `uv`, you need to install the optional completion extras:
|
|
166
|
+
|
|
167
|
+
1. Install the completion extras:
|
|
168
|
+
|
|
169
|
+
```shell
|
|
170
|
+
uv pip install "httptap[completion]"
|
|
171
|
+
# or
|
|
172
|
+
pip install "httptap[completion]"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
2. Activate your virtual environment:
|
|
176
|
+
|
|
177
|
+
```shell
|
|
178
|
+
source .venv/bin/activate
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
3. Run the global activation script for argument completions:
|
|
182
|
+
|
|
183
|
+
```shell
|
|
184
|
+
activate-global-python-argcomplete
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
4. Restart your shell. Completions should now work in both bash and zsh.
|
|
188
|
+
|
|
189
|
+
**Note:** The global activation script provides argument completions for bash and zsh only. Other shells are not covered by the script and must be configured separately.
|
|
190
|
+
|
|
191
|
+
#### Usage Examples
|
|
192
|
+
|
|
193
|
+
Once completions are installed, you can use `Tab` to autocomplete commands and options:
|
|
194
|
+
|
|
195
|
+
```shell
|
|
196
|
+
# Complete command options
|
|
197
|
+
httptap --<TAB>
|
|
198
|
+
# Shows: --follow, --timeout, --no-http2, --ignore-ssl, --proxy, --header, --compact, --metrics-only, --json, --version, --help
|
|
199
|
+
|
|
200
|
+
# Complete after typing partial option
|
|
201
|
+
httptap --fol<TAB>
|
|
202
|
+
# Completes to: httptap --follow
|
|
203
|
+
|
|
204
|
+
# Complete multiple options
|
|
205
|
+
httptap --follow --time<TAB>
|
|
206
|
+
# Completes to: httptap --follow --timeout
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Quick Start
|
|
212
|
+
|
|
213
|
+
Currently `httptap` issues a `GET` request and streams the entire response body. Other HTTP methods and payloads are not
|
|
214
|
+
supported yet; this keeps the interface simple and avoids exposing sensitive request data in output. If you need to
|
|
215
|
+
profile `POST`/`PUT` workloads, you can wrap `httptap` and override the request executor to plug in custom behavior.
|
|
216
|
+
|
|
217
|
+
Run a single request and display a rich waterfall:
|
|
218
|
+
|
|
219
|
+
```shell
|
|
220
|
+
httptap https://httpbin.io
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Add custom headers (repeat `-H` for multiple values):
|
|
224
|
+
|
|
225
|
+
```shell
|
|
226
|
+
httptap \
|
|
227
|
+
-H "Accept: application/json" \
|
|
228
|
+
-H "Authorization: Bearer super-secret" \
|
|
229
|
+
https://httpbin.io/bearer
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Follow redirect chains and dump metrics to JSON:
|
|
233
|
+
|
|
234
|
+
```shell
|
|
235
|
+
httptap --follow --json out/report.json https://httpbin.io/redirect/2
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Collect compact (single-line) timings suitable for logs:
|
|
239
|
+
|
|
240
|
+
```shell
|
|
241
|
+
httptap --compact https://httpbin.io/get
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Expose raw metrics for scripts:
|
|
245
|
+
|
|
246
|
+
```shell
|
|
247
|
+
httptap --metrics-only https://httpbin.io/get | tee timings.log
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Programmatic users can inject a custom executor for advanced scenarios. The
|
|
251
|
+
default analyzer accepts either a modern `RequestExecutor` implementation or a
|
|
252
|
+
legacy callable wrapped with `CallableRequestExecutor`, so new request flags
|
|
253
|
+
remain backward compatible.
|
|
254
|
+
|
|
255
|
+
Bypass TLS verification when troubleshooting self-signed endpoints:
|
|
256
|
+
|
|
257
|
+
```shell
|
|
258
|
+
httptap --ignore-ssl https://self-signed.badssl.com
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The flag disables certificate validation and relaxes many handshake
|
|
262
|
+
constraints so that legacy endpoints (expired/self-signed/hostname
|
|
263
|
+
mismatches, weak hashes, older TLS versions) still complete. Some
|
|
264
|
+
algorithms removed from modern OpenSSL builds (for example RC4 or
|
|
265
|
+
3DES) may remain unavailable. Use this mode only on trusted networks.
|
|
266
|
+
|
|
267
|
+
Route traffic through an HTTP/SOCKS proxy (explicit override takes precedence over env vars `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`):
|
|
268
|
+
|
|
269
|
+
```shell
|
|
270
|
+
httptap --proxy socks5h://proxy.local:1080 https://example.com
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The output and JSON export include the proxy URI so you can confirm what
|
|
274
|
+
path was used.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
## Releasing
|
|
280
|
+
|
|
281
|
+
### Prerequisites
|
|
282
|
+
|
|
283
|
+
- GitHub Environment `pypi` must be configured in repository settings
|
|
284
|
+
- PyPI Trusted Publishing configured for `ozeranskii/httptap`
|
|
285
|
+
|
|
286
|
+
### Steps
|
|
287
|
+
|
|
288
|
+
1. Trigger the **Release** workflow from GitHub Actions:
|
|
289
|
+
- Provide exact version (e.g., `0.3.0`), OR
|
|
290
|
+
- Select bump type: `patch`, `minor`, or `major`
|
|
291
|
+
2. The workflow will:
|
|
292
|
+
- Update version in `pyproject.toml` using `uv version`
|
|
293
|
+
- Generate changelog with `git-cliff` and update `CHANGELOG.md`
|
|
294
|
+
- Commit changes and create a git tag
|
|
295
|
+
- Run full test suite on the tagged version
|
|
296
|
+
- Build wheel and source distribution
|
|
297
|
+
- Publish to PyPI via Trusted Publishing (OIDC)
|
|
298
|
+
- Create GitHub Release with generated notes
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Sample Output
|
|
303
|
+
|
|
304
|
+

|
|
305
|
+
|
|
306
|
+
The redirect summary includes a total row:
|
|
307
|
+

|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## JSON Export Structure
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"initial_url": "https://httpbin.io/redirect/2",
|
|
316
|
+
"total_steps": 3,
|
|
317
|
+
"steps": [
|
|
318
|
+
{
|
|
319
|
+
"url": "https://httpbin.io/redirect/2",
|
|
320
|
+
"step_number": 1,
|
|
321
|
+
"timing": {
|
|
322
|
+
"dns_ms": 8.947208058089018,
|
|
323
|
+
"connect_ms": 96.97712492197752,
|
|
324
|
+
"tls_ms": 194.56583401188254,
|
|
325
|
+
"ttfb_ms": 445.9513339679688,
|
|
326
|
+
"total_ms": 447.3437919514254,
|
|
327
|
+
"wait_ms": 145.46116697601974,
|
|
328
|
+
"xfer_ms": 1.392457983456552,
|
|
329
|
+
"is_estimated": false
|
|
330
|
+
},
|
|
331
|
+
"network": {
|
|
332
|
+
"ip": "44.211.11.205",
|
|
333
|
+
"ip_family": "IPv4",
|
|
334
|
+
"tls_version": "TLSv1.2",
|
|
335
|
+
"tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
|
|
336
|
+
"cert_cn": "httpbin.io",
|
|
337
|
+
"cert_days_left": 143
|
|
338
|
+
},
|
|
339
|
+
"response": {
|
|
340
|
+
"status": 302,
|
|
341
|
+
"bytes": 0,
|
|
342
|
+
"content_type": null,
|
|
343
|
+
"server": null,
|
|
344
|
+
"date": "2025-10-23T19:20:36+00:00",
|
|
345
|
+
"location": "/relative-redirect/1",
|
|
346
|
+
"headers": {
|
|
347
|
+
"access-control-allow-credentials": "true",
|
|
348
|
+
"access-control-allow-origin": "*",
|
|
349
|
+
"location": "/relative-redirect/1",
|
|
350
|
+
"date": "Thu, 23 Oct 2025 19:20:36 GMT",
|
|
351
|
+
"content-length": "0"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"error": null,
|
|
355
|
+
"note": null
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"url": "https://httpbin.io/relative-redirect/1",
|
|
359
|
+
"step_number": 2,
|
|
360
|
+
"timing": {
|
|
361
|
+
"dns_ms": 2.6895420160144567,
|
|
362
|
+
"connect_ms": 97.51500003039837,
|
|
363
|
+
"tls_ms": 193.99016606621444,
|
|
364
|
+
"ttfb_ms": 400.2034160075709,
|
|
365
|
+
"total_ms": 400.60841606464237,
|
|
366
|
+
"wait_ms": 106.00870789494365,
|
|
367
|
+
"xfer_ms": 0.4050000570714474,
|
|
368
|
+
"is_estimated": false
|
|
369
|
+
},
|
|
370
|
+
"network": {
|
|
371
|
+
"ip": "44.211.11.205",
|
|
372
|
+
"ip_family": "IPv4",
|
|
373
|
+
"tls_version": "TLSv1.2",
|
|
374
|
+
"tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
|
|
375
|
+
"cert_cn": "httpbin.io",
|
|
376
|
+
"cert_days_left": 143
|
|
377
|
+
},
|
|
378
|
+
"response": {
|
|
379
|
+
"status": 302,
|
|
380
|
+
"bytes": 0,
|
|
381
|
+
"content_type": null,
|
|
382
|
+
"server": null,
|
|
383
|
+
"date": "2025-10-23T19:20:36+00:00",
|
|
384
|
+
"location": "/get",
|
|
385
|
+
"headers": {
|
|
386
|
+
"access-control-allow-credentials": "true",
|
|
387
|
+
"access-control-allow-origin": "*",
|
|
388
|
+
"location": "/get",
|
|
389
|
+
"date": "Thu, 23 Oct 2025 19:20:36 GMT",
|
|
390
|
+
"content-length": "0"
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
"error": null,
|
|
394
|
+
"note": null
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"url": "https://httpbin.io/get",
|
|
398
|
+
"step_number": 3,
|
|
399
|
+
"timing": {
|
|
400
|
+
"dns_ms": 2.643457963131368,
|
|
401
|
+
"connect_ms": 97.36416593659669,
|
|
402
|
+
"tls_ms": 197.3062080796808,
|
|
403
|
+
"ttfb_ms": 403.2038329169154,
|
|
404
|
+
"total_ms": 403.9644579170272,
|
|
405
|
+
"wait_ms": 105.89000093750656,
|
|
406
|
+
"xfer_ms": 0.7606250001117587,
|
|
407
|
+
"is_estimated": false
|
|
408
|
+
},
|
|
409
|
+
"network": {
|
|
410
|
+
"ip": "52.70.33.41",
|
|
411
|
+
"ip_family": "IPv4",
|
|
412
|
+
"tls_version": "TLSv1.2",
|
|
413
|
+
"tls_cipher": "ECDHE-RSA-AES128-GCM-SHA256",
|
|
414
|
+
"cert_cn": "httpbin.io",
|
|
415
|
+
"cert_days_left": 143
|
|
416
|
+
},
|
|
417
|
+
"response": {
|
|
418
|
+
"status": 200,
|
|
419
|
+
"bytes": 389,
|
|
420
|
+
"content_type": "application/json; charset=utf-8",
|
|
421
|
+
"server": null,
|
|
422
|
+
"date": "2025-10-23T19:20:37+00:00",
|
|
423
|
+
"location": null,
|
|
424
|
+
"headers": {
|
|
425
|
+
"access-control-allow-credentials": "true",
|
|
426
|
+
"access-control-allow-origin": "*",
|
|
427
|
+
"content-type": "application/json; charset=utf-8",
|
|
428
|
+
"date": "Thu, 23 Oct 2025 19:20:37 GMT",
|
|
429
|
+
"content-length": "389"
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"error": null,
|
|
433
|
+
"note": null
|
|
434
|
+
}
|
|
435
|
+
],
|
|
436
|
+
"summary": {
|
|
437
|
+
"total_time_ms": 1251.916665933095,
|
|
438
|
+
"final_status": 200,
|
|
439
|
+
"final_url": "https://httpbin.io/get",
|
|
440
|
+
"final_bytes": 389,
|
|
441
|
+
"errors": 0
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Metrics-only scripting
|
|
447
|
+
|
|
448
|
+
```shell
|
|
449
|
+
httptap --metrics-only https://httpbin.io/get
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
```terminaloutput
|
|
453
|
+
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
|
|
454
|
+
tls_version=TLSv1.2
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Advanced Usage
|
|
460
|
+
|
|
461
|
+
### Custom Implementations
|
|
462
|
+
|
|
463
|
+
Swap in your own resolver or TLS inspector (anything satisfying the Protocol from `httptap.interfaces`):
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
from httptap import HTTPTapAnalyzer, SystemDNSResolver
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class HardcodedDNS(SystemDNSResolver):
|
|
470
|
+
def resolve(self, host, port, timeout):
|
|
471
|
+
return "93.184.216.34", "IPv4", 0.1
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
analyzer = HTTPTapAnalyzer(dns_resolver=HardcodedDNS())
|
|
475
|
+
steps = analyzer.analyze_url("https://httpbin.io")
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Development
|
|
481
|
+
|
|
482
|
+
```shell
|
|
483
|
+
git clone https://github.com/ozeranskii/httptap.git
|
|
484
|
+
cd httptap
|
|
485
|
+
uv sync
|
|
486
|
+
uv run pytest
|
|
487
|
+
uv run ruff check
|
|
488
|
+
uv run ruff format .
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Tests expect outbound network access; you can mock `SystemDNSResolver` / `SocketTLSInspector` when running offline.
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Contributing
|
|
496
|
+
|
|
497
|
+
1. Fork and clone the repo.
|
|
498
|
+
2. Create a feature branch.
|
|
499
|
+
3. Run `pytest` and `ruff` before committing.
|
|
500
|
+
4. Submit a pull request with a clear description and any relevant screenshots or benchmarks.
|
|
501
|
+
|
|
502
|
+
We welcome bug reports, feature proposals, doc improvements, and creative new visualizations or exporters.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## License
|
|
507
|
+
|
|
508
|
+
Apache License 2.0 © Sergei Ozeranskii. See [LICENSE](https://github.com/ozeranskii/httptap/blob/main/LICENSE) for
|
|
509
|
+
details.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Acknowledgements
|
|
514
|
+
|
|
515
|
+
- Built on the shoulders of fantastic
|
|
516
|
+
libraries: [httpx](https://www.python-httpx.org/), [httpcore](https://github.com/encode/httpcore),
|
|
517
|
+
and [Rich](https://github.com/Textualize/rich).
|
|
518
|
+
- Inspired by the tooling ecosystem around web performance (e.g., DevTools waterfalls, `curl --trace`).
|
|
519
|
+
- Special thanks to everyone who opens issues, shares ideas, or contributes patches.
|