Core Components¶
This page documents the core class and data models of httptap, rendered directly from the source docstrings so signatures and defaults always match the installed version.
HTTPTapAnalyzer¶
The main analyzer class that orchestrates HTTP request analysis, including redirect following and per-step metric collection.
from httptap import HTTPTapAnalyzer
from httptap.constants import HTTPMethod
analyzer = HTTPTapAnalyzer(follow_redirects=True)
steps = analyzer.analyze_url(
"https://httpbin.io",
headers={"Accept": "application/json"},
)
HTTPTapAnalyzer ¶
HTTPTapAnalyzer(
*,
follow_redirects: bool = False,
timeout: float = DEFAULT_TIMEOUT_SECONDS,
http2: bool = True,
verify_ssl: bool = True,
ca_bundle_path: str | None = None,
max_redirects: int = 10,
request_executor: RequestExecutor | None = None,
proxy: ProxyTypes | None = None,
noproxy: bool = False,
dns_resolver: DNSResolver | None = None,
tls_inspector: TLSInspector | None = None,
timing_collector_factory: type[TimingCollector]
| None = None,
)
Orchestrates HTTP request analysis with redirect following.
This class manages the high-level flow of analyzing HTTP requests, including following redirect chains and collecting metrics at each step.
Attributes:
-
follow_redirects–Whether to follow HTTP redirects.
-
timeout–Request timeout in seconds.
-
http2–Whether to enable HTTP/2 support.
-
max_redirects–Maximum number of redirects to follow.
Parameters:
-
follow_redirects(bool, default:False) –Whether to follow 3xx redirects.
-
timeout(float, default:DEFAULT_TIMEOUT_SECONDS) –Request timeout in seconds.
-
http2(bool, default:True) –Enable HTTP/2 support.
-
verify_ssl(bool, default:True) –Whether to verify TLS certificates.
-
ca_bundle_path(str | None, default:None) –Path to custom CA certificate bundle (PEM format). Only used when verify_ssl is True. If None, uses system CA bundle.
-
max_redirects(int, default:10) –Maximum number of redirects to follow.
-
request_executor(RequestExecutor | None, default:None) –Object responsible for performing HTTP requests. Must implement the RequestExecutor protocol. Defaults to the built-in httpx implementation.
-
proxy(ProxyTypes | None, default:None) –Optional proxy URL (http/https/socks5/socks5h) applied to all requests in the analysis chain.
-
noproxy(bool, default:False) –When True, ignore proxy environment variables and connect directly. Triggered by --proxy "".
-
dns_resolver(DNSResolver | None, default:None) –Custom DNS resolver implementation. If None, make_request will use its default (SystemDNSResolver).
-
tls_inspector(TLSInspector | None, default:None) –Custom TLS inspector implementation. If None, make_request will use its default (SocketTLSInspector).
-
timing_collector_factory(type[TimingCollector] | None, default:None) –Factory class for creating timing collectors. If None, make_request will use its default (PerfCounterTimingCollector). Note: This should be a class, not an instance, as a new collector is created for each request.
Methods:
-
analyze_url–Analyze URL with optional redirect following.
Source code in httptap/analyzer.py
analyze_url ¶
analyze_url(
url: str,
*,
method: HTTPMethod = GET,
content: bytes | None = None,
headers: Mapping[str, str] | None = None,
) -> list[StepMetrics]
Analyze URL with optional redirect following.
Performs HTTP request(s) and collects comprehensive metrics. If follow_redirects is enabled and server returns 3xx with Location, continues following redirects up to max_redirects.
Parameters:
-
url(str) –Initial URL to analyze. Must be valid HTTP/HTTPS URL.
-
method(HTTPMethod, default:GET) –HTTP method to use (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS).
-
content(bytes | None, default:None) –Optional request body as bytes.
-
headers(Mapping[str, str] | None, default:None) –Optional mapping of request headers applied to every step.
Returns:
-
list[StepMetrics]–List of StepMetrics, one per request in the chain. Each step contains
-
list[StepMetrics]–timing, network, and response information. Returns at least one step
-
list[StepMetrics]–even if request fails. When a redirect points at a URL that cannot
-
list[StepMetrics]–be requested (malformed authority, invalid port, missing host or a
-
list[StepMetrics]–non-http(s) scheme), the redirect step is kept as is and the chain
-
list[StepMetrics]–ends with an extra failed step for that target: it carries the
-
list[StepMetrics]–target URL and a network-kind error but no timing or response, just
-
list[StepMetrics]–like a hop whose connection failed.
Examples:
Basic usage without redirects: >>> analyzer = HTTPTapAnalyzer() >>> steps = analyzer.analyze_url("https://example.com") >>> print(f"Total time: {steps[0].timing.total_ms}ms") Total time: 234.5ms
Following redirect chain: >>> analyzer = HTTPTapAnalyzer(follow_redirects=True) >>> steps = analyzer.analyze_url("http://example.com") >>> for i, step in enumerate(steps, 1): ... print(f"Step {i}: {step.response.status}") Step 1: 301 Step 2: 200
Source code in httptap/analyzer.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | |
Data models¶
All models are @dataclass(slots=True) and expose to_dict() for JSON export.
StepMetrics dataclass ¶
StepMetrics(
url: str = "",
step_number: int = 1,
timing: TimingMetrics = TimingMetrics(),
network: NetworkInfo = NetworkInfo(),
response: ResponseInfo = ResponseInfo(),
error: str | None = None,
note: str | None = None,
proxied_via: str | None = None,
request_method: str | None = None,
request_headers: dict[str, str] = dict(),
request_body_bytes: int = 0,
redirect_limit_reached: bool = False,
*,
error_kind: str | None = None,
)
Complete metrics for a single HTTP request step.
Represents all collected data for one request in the chain, including timing, network, response information, and any errors.
Attributes:
-
url(str) –The URL that was requested.
-
step_number(int) –Step number in redirect chain (1-indexed).
-
timing(TimingMetrics) –Timing metrics.
-
network(NetworkInfo) –Network and security information.
-
response(ResponseInfo) –HTTP response information.
-
error(str | None) –Error message if request failed.
-
error_kind(str | None) –Internal error classification; not part of the JSON export (the HAR export carries it as
response._errorKind). -
note(str | None) –Additional notes or context.
-
proxied_via(str | None) –Proxy URL used for this request, if any.
-
request_method(str | None) –HTTP method used (GET, POST, PUT, etc.).
-
request_headers(dict[str, str]) –Request headers (sanitized).
-
request_body_bytes(int) –Size of request body in bytes.
-
redirect_limit_reached(bool) –Whether redirect following stopped at the configured limit on this step.
Methods:
-
to_dict–Convert step metrics to dictionary for JSON export.
has_error property ¶
Check if this step encountered an error.
Returns:
-
bool–True if error occurred, False otherwise.
is_redirect property ¶
Check if this step is a redirect response.
Returns:
-
bool–True if status is 3xx and Location header present.
to_dict ¶
Convert step metrics to dictionary for JSON export.
Returns:
-
dict[str, Any]–Dictionary containing all step information organized by category.
Source code in httptap/models.py
TimingMetrics dataclass ¶
TimingMetrics(
dns_ms: float = 0.0,
connect_ms: float = 0.0,
tls_ms: float = 0.0,
ttfb_ms: float = 0.0,
total_ms: float = 0.0,
wait_ms: float = 0.0,
xfer_ms: float = 0.0,
is_estimated: bool = False,
)
Timing metrics for HTTP request phases.
All timing values are in milliseconds.
Attributes:
-
dns_ms(float) –DNS resolution time.
-
connect_ms(float) –Connection establishment time. Through an HTTP CONNECT proxy this covers TCP to the proxy and the tunnel setup.
-
tls_ms(float) –TLS handshake time (0 for HTTP).
-
ttfb_ms(float) –Time to first byte (headers received).
-
total_ms(float) –Total request time from start to finish.
-
wait_ms(float) –Server processing time (derived metric).
-
xfer_ms(float) –Response body transfer time (derived metric).
-
is_estimated(bool) –Whether connect/TLS timing was estimated vs measured.
Methods:
-
calculate_derived–Calculate derived timing metrics.
-
to_dict–Convert timing metrics to dictionary.
calculate_derived ¶
Calculate derived timing metrics.
This method computes timing values that are derived from the raw measurements. It should be called after all raw timing values (dns_ms, connect_ms, tls_ms, ttfb_ms, total_ms) are populated.
Computes
wait_ms: Time server spent processing the request before sending the first byte of the response. Calculated as: max(0, ttfb_ms - (dns_ms + connect_ms + tls_ms))
xfer_ms: Time spent transferring the response body after headers were received. Calculated as: max(0, total_ms - ttfb_ms)
Note
Both values are clamped to 0 to handle edge cases where timing measurements may have slight inconsistencies due to measurement precision or system clock adjustments.
Examples:
>>> timing = TimingMetrics(
... dns_ms=10.0,
... connect_ms=20.0,
... tls_ms=50.0,
... ttfb_ms=100.0,
... total_ms=150.0
... )
>>> timing.calculate_derived()
>>> print(f"Wait: {timing.wait_ms}ms, Transfer: {timing.xfer_ms}ms")
Wait: 20.0ms, Transfer: 50.0ms
Source code in httptap/models.py
to_dict ¶
Convert timing metrics to dictionary.
Returns:
-
dict[str, float | bool]–Dictionary with all timing metrics.
Source code in httptap/models.py
NetworkInfo dataclass ¶
NetworkInfo(
ip: str | None = None,
ip_family: str | None = None,
http_version: str | None = None,
tls_version: str | None = None,
tls_cipher: str | None = None,
cert_cn: str | None = None,
cert_days_left: int | None = None,
cert_sans: list[str] = list(),
cert_issuer: str | None = None,
cert_serial: str | None = None,
cert_not_before: datetime | None = None,
cert_not_after: datetime | None = None,
tls_verified: bool | None = None,
tls_custom_ca: bool | None = None,
proxy_url: str | None = None,
proxy_source: str | None = None,
)
Network and security information for a connection.
Attributes:
-
ip(str | None) –Resolved IP address.
-
ip_family(str | None) –Address family label (e.g., 'IPv4' or 'IPv6').
-
http_version(str | None) –HTTP protocol version negotiated (e.g., 'HTTP/2.0').
-
tls_version(str | None) –TLS protocol version (e.g., 'TLSv1.3').
-
tls_cipher(str | None) –TLS cipher suite used.
-
cert_cn(str | None) –Certificate Common Name.
-
cert_days_left(int | None) –Days until certificate expiration.
-
cert_sans(list[str]) –Subject Alternative Names (DNS entries) from the leaf certificate.
-
cert_issuer(str | None) –Issuer Common Name of the leaf certificate.
-
cert_serial(str | None) –Certificate serial number (hex string).
-
cert_not_before(datetime | None) –Start of the certificate validity window.
-
cert_not_after(datetime | None) –End of the certificate validity window.
-
tls_verified(bool | None) –Whether TLS certificate verification was enforced.
-
tls_custom_ca(bool | None) –True when a custom CA bundle was configured.
-
proxy_url(str | None) –Effective proxy URL used for this request, or None.
-
proxy_source(str | None) –Origin of the proxy setting (see PROXY_SOURCE_* constants). None when no proxy is configured.
Methods:
-
to_dict–Convert network info to dictionary.
to_dict ¶
Convert network info to dictionary.
Returns:
-
dict[str, Any]–Dictionary with all network information.
Source code in httptap/models.py
ResponseInfo dataclass ¶
ResponseInfo(
status: int | None = None,
bytes: int = 0,
content_type: str | None = None,
server: str | None = None,
date: datetime | None = None,
location: str | None = None,
headers: dict[str, str] = dict(),
)
HTTP response information.
Attributes:
-
status(int | None) –HTTP status code.
-
bytes(int) –Response body size in bytes, before Content-Encoding decoding.
-
content_type(str | None) –Content-Type header value.
-
server(str | None) –Server header value.
-
date(datetime | None) –Date header parsed as datetime.
-
location(str | None) –Location header for redirects.
-
headers(dict[str, str]) –Sanitized response headers (secrets masked).
Methods:
-
to_dict–Convert response info to dictionary.
to_dict ¶
Convert response info to dictionary.
Returns:
-
dict[str, Any]–Dictionary with all response information.
Source code in httptap/models.py
Utility functions¶
validate_url ¶
Validate URL format.
Requires an http/https scheme and a non-empty host. A scheme alone is not enough: https:///path and https://?query parse to an empty host and would fail at connection time with an opaque error, so they are rejected here where the message can be actionable.
Parameters:
-
url(str) –URL string to validate.
Returns:
-
bool–True if URL is valid HTTP/HTTPS URL, False otherwise.
Examples:
>>> validate_url("https://example.com")
True
>>> validate_url("ftp://example.com")
False
>>> validate_url("https://")
False
>>> validate_url("https:///path")
False
>>> validate_url("http://127.0.0.1:99999/")
False
Source code in httptap/utils.py
sanitize_headers ¶
Sanitize HTTP headers by masking sensitive values.
Parameters:
-
headers(Mapping[str, str]) –Dictionary of HTTP headers.
Returns:
-
dict[str, str]–New dictionary with sensitive values masked and URL credentials in
-
dict[str, str]–Location-style headers redacted.
Examples:
>>> sanitize_headers({"Authorization": "Bearer secret"})
{'Authorization': 'Bear****cret'}
>>> sanitize_headers({"Location": "https://user:secret@example.com/"})
{'Location': 'https://user:****@example.com/'}
Source code in httptap/utils.py
parse_http_date ¶
Parse HTTP date header to datetime.
Supports RFC 7231 HTTP-date format.
Parameters:
-
date_str(str) –Date string from HTTP Date header.
Returns:
-
datetime | None–Parsed datetime in UTC or None if parsing fails.
Examples:
>>> parse_http_date("Mon, 22 Oct 2025 12:00:00 GMT")
datetime.datetime(2025, 10, 22, 12, 0, tzinfo=UTC)
Source code in httptap/utils.py
create_ssl_context ¶
create_ssl_context(
*,
verify_ssl: bool,
ca_bundle_path: str | None = None,
accept_bracketed_server_names: bool = False,
) -> SSLContext
Return an SSL context honoring the requested verification policy.
Parameters:
-
verify_ssl(bool) –Whether to enforce certificate validation and modern security defaults.
-
ca_bundle_path(str | None, default:None) –Path to custom CA certificate bundle file (PEM format). Only used when verify_ssl is True. If None, uses system CA bundle.
-
accept_bracketed_server_names(bool, default:False) –Strip the brackets of an IPv6 literal passed as the TLS server name, for IPv6 targets behind HTTP proxies.
Returns:
-
SSLContext–Configured
ssl.SSLContextinstance.
Source code in httptap/utils.py
Constants¶
Selected values from httptap.constants (see the module source for the full set):
Timeouts and limits¶
from httptap.constants import (
DEFAULT_TIMEOUT_SECONDS, # 20.0 seconds
TLS_PROBE_MAX_TIMEOUT_SECONDS, # 5.0 seconds
HTTP_DEFAULT_PORT, # 80
HTTPS_DEFAULT_PORT, # 443
)
Exit codes¶
from httptap.constants import (
EXIT_CODE_HTTP_FAILURE, # 22 - HTTP 4xx/5xx response with --fail
EXIT_CODE_OK, # 0 - Success (os.EX_OK)
EXIT_CODE_SLO_VIOLATION, # 4 - SLO threshold violation
EXIT_CODE_TOO_MANY_REDIRECTS, # 47 - Maximum redirects followed
EXIT_CODE_USAGE, # 64 - Invalid arguments (os.EX_USAGE)
EXIT_CODE_SOFTWARE, # 70 - Internal error (os.EX_SOFTWARE)
EXIT_CODE_CANTCREAT, # 73 - --json or --har output could not be written (os.EX_CANTCREAT)
EXIT_CODE_TEMPFAIL, # 75 - Network/TLS error (os.EX_TEMPFAIL)
)
Example: complete usage¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer(follow_redirects=True, timeout=30.0, http2=True)
steps = analyzer.analyze_url(
"https://httpbin.io/bearer",
headers={
"Authorization": "Bearer token123",
"Accept": "application/json",
"User-Agent": "MyApp/1.0",
},
)
for step in steps:
print(f"Step {step.step_number}: {step.url}")
print(f" Status: {step.response.status}")
print(f" DNS: {step.timing.dns_ms:.2f}ms")
print(f" Connect: {step.timing.connect_ms:.2f}ms")
print(f" TLS: {step.timing.tls_ms:.2f}ms")
print(f" TTFB: {step.timing.ttfb_ms:.2f}ms")
print(f" Total: {step.timing.total_ms:.2f}ms")
if step.network.ip:
print(f" IP: {step.network.ip} ({step.network.ip_family})")
if step.network.cert_cn:
print(f" Certificate: {step.network.cert_cn} (expires in {step.network.cert_days_left} days)")
What's Next?¶
-
Implement custom DNS, TLS, timing, and more
-
Patterns for monitoring, testing, batch analysis
-
Extend httptap and contribute back