Advanced Features¶
This guide covers advanced usage patterns and customization options for httptap.
Custom DNS Resolution¶
You can provide custom DNS resolver implementations by using the Python API. httptap always dials the resolved IP address (IPv4/IPv6) while preserving the original hostname for the Host header and TLS SNI. IPv6 literals are bracketed automatically, so custom resolvers only need to return the correct IP/family tuple.
from httptap import HTTPTapAnalyzer, SystemDNSResolver
class CustomDNSResolver(SystemDNSResolver):
"""Custom DNS resolver with hardcoded responses."""
def resolve(self, host: str, port: int, timeout: float):
# Override with custom logic
if host == "httpbin.io":
return "44.211.11.205", "IPv4", 0.1
return super().resolve(host, port, timeout)
# Use custom resolver
analyzer = HTTPTapAnalyzer(dns_resolver=CustomDNSResolver())
steps = analyzer.analyze_url("https://httpbin.io")
Custom TLS Inspection¶
Implement custom TLS inspection logic to extract additional certificate information.
from httptap import HTTPTapAnalyzer
from httptap.interfaces import TLSInspector
from httptap.models import NetworkInfo
class CustomTLSInspector:
"""Custom TLS inspector with extended certificate checks."""
def inspect(self, host: str, port: int, timeout: float) -> NetworkInfo:
# Custom TLS inspection logic
# Return: NetworkInfo with TLS version, cipher, and certificate data
...
analyzer = HTTPTapAnalyzer(tls_inspector=CustomTLSInspector())
Programmatic Usage¶
Use httptap as a Python library for integration into your applications.
Basic Analysis¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
for step in steps:
print(f"URL: {step.url}")
print(f"Status: {step.response.status}")
print(f"Total time: {step.timing.total_ms:.2f}ms")
With Custom Headers¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
headers = {
"Authorization": "Bearer token123",
"Accept": "application/json"
}
steps = analyzer.analyze_url(
"https://httpbin.io/bearer",
headers=headers
)
Following Redirects¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer(follow_redirects=True)
steps = analyzer.analyze_url("https://httpbin.io/redirect/3")
print(f"Total steps in redirect chain: {len(steps)}")
Sending Request Body¶
from httptap import HTTPTapAnalyzer
from httptap.constants import HTTPMethod
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url(
"https://httpbin.io/post",
method=HTTPMethod.POST,
content=b'{"key": "value"}',
headers={"Content-Type": "application/json"},
)
Ignoring TLS Verification¶
When troubleshooting staging environments or hosts with self-signed certificates, you can skip TLS validation:
The request still records TLS metadata, but certificate errors are suppressed so you can focus on the protocol flow. Only use this flag in trusted environments because it disables protection against man-in-the-middle attacks. The client relaxes many cipher and protocol requirements (weak hashes, older TLS versions, small DH groups) so that legacy endpoints are more likely to complete the handshake. Extremely deprecated algorithms that OpenSSL removes entirely (e.g., RC4, 3DES on some platforms) may still fail even in this mode.
Using Proxies¶
Direct requests through an outbound proxy (HTTP, HTTPS, SOCKS5/SOCKS5H):
The Rich output and JSON export include the proxy URI when one is used.
Proxy Protocols and DNS Resolution¶
httptap supports four proxy protocols, each with different DNS resolution behavior:
| Protocol | DNS Resolved By | Use Case |
|---|---|---|
socks5h:// |
Proxy server | Privacy, corporate networks, access to internal DNS |
http:// |
Proxy server | Standard HTTP proxies (CONNECT method) |
https:// |
Proxy server | Encrypted connection to proxy |
socks5:// |
Client (local) | When you need to control DNS resolution |
The h suffix in socks5h stands for "hostname" (a curl convention). With socks5h://, the hostname is sent to the proxy which resolves it. With socks5://, the client resolves DNS locally and sends the IP to the proxy.
Environment Variable Proxies¶
When no --proxy flag is provided, httptap checks environment variables:
no_proxy/NO_PROXY- Comma-separated list of hosts to bypass (lowercase takes priority)https_proxy/HTTPS_PROXY- Proxy for HTTPS requests (lowercase takes priority)http_proxy/HTTP_PROXY- Proxy for HTTP requests (lowercase takes priority)all_proxy/ALL_PROXY- Fallback proxy for all protocols
The --proxy flag always takes precedence over environment variables.
NO_PROXY patterns:
*- Bypass proxy for all hostsexample.com- Exact hostname match.example.com- All subdomains of example.comsub.example.com- Exact subdomain match
Custom CA Bundles¶
For internal endpoints signed by a private CA, supply a PEM bundle with --cacert:
CLI output will show TLS CA: custom bundle to indicate the non-system trust store was used. JSON exports include network.tls_custom_ca: true so downstream tools can detect custom trust. The flag is mutually exclusive with --ignore-ssl.
Custom Request Executors¶
For fully customized behavior you can provide your own request executor.
Executors receive all parameters packaged inside RequestOptions, so new
flags added by httptap remain backward compatible.
from httptap import HTTPTapAnalyzer, RequestExecutor, RequestOptions, RequestOutcome
class RecordingExecutor(RequestExecutor):
def __init__(self) -> None:
self.last_options: RequestOptions | None = None
def execute(self, options: RequestOptions) -> RequestOutcome:
self.last_options = options
# Call the built-in client (or your preferred HTTP library)
from httptap.http_client import make_request
timing, network, response = make_request(
options.url,
options.timeout,
http2=options.http2,
verify_ssl=options.verify_ssl,
dns_resolver=options.dns_resolver,
tls_inspector=options.tls_inspector,
timing_collector=options.timing_collector,
force_new_connection=options.force_new_connection,
headers=options.headers,
)
return RequestOutcome(timing=timing, network=network, response=response)
executor = RecordingExecutor()
analyzer = HTTPTapAnalyzer(request_executor=executor)
analyzer.analyze_url("https://httpbin.io/get", headers={"X-Debug": "1"})
print(executor.last_options.headers) # {'X-Debug': '1'}
Custom Visualization¶
Create your own visualization by implementing the Visualizer protocol.
from httptap.models import StepMetrics
class CustomVisualizer:
"""Custom visualizer for request steps."""
def render(self, step: StepMetrics) -> None:
print(f"Step {step.step_number}: {step.timing.total_ms}ms")
# Use custom visualizer
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
visualizer = CustomVisualizer()
for step in steps:
visualizer.render(step)
Custom Export Formats¶
Implement custom export formats beyond JSON.
from collections.abc import Sequence
from httptap.models import StepMetrics
import csv
class CSVExporter:
"""Export request data to CSV format."""
def export(self, steps: Sequence[StepMetrics], initial_url: str, output_path: str) -> None:
with open(output_path, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["url", "status", "dns_ms", "connect_ms",
"tls_ms", "ttfb_ms", "total_ms"])
for step in steps:
writer.writerow([
step.url,
step.response.status,
step.timing.dns_ms,
step.timing.connect_ms,
step.timing.tls_ms,
step.timing.ttfb_ms,
step.timing.total_ms,
])
# Usage
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
exporter = CSVExporter()
exporter.export(steps, "https://httpbin.io", "output.csv")
Performance Monitoring¶
Use httptap for continuous performance monitoring.
import time
from httptap import HTTPTapAnalyzer
def monitor_endpoint(url: str, interval: int = 60):
"""Monitor endpoint every interval seconds."""
analyzer = HTTPTapAnalyzer()
while True:
steps = analyzer.analyze_url(url)
step = steps[0]
# Log metrics
print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - "
f"TTFB: {step.timing.ttfb_ms:.2f}ms, "
f"Total: {step.timing.total_ms:.2f}ms, "
f"Status: {step.response.status}")
time.sleep(interval)
# Monitor API endpoint every minute
monitor_endpoint("https://httpbin.io/status/200", interval=60)
Batch Analysis¶
Analyze multiple URLs concurrently.
from concurrent.futures import ThreadPoolExecutor
from httptap import HTTPTapAnalyzer
def analyze_url(url: str):
"""Analyze a single URL."""
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url(url)
return url, steps[0].timing.total_ms
# List of URLs to analyze
urls = [
"https://httpbin.io",
"https://httpbin.io/delay/1",
"https://httpbin.io/gzip"
]
# Analyze concurrently
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(analyze_url, urls))
# Print results
for url, total_ms in results:
print(f"{url}: {total_ms:.2f}ms")
Error Handling¶
Handle errors gracefully when analyzing URLs.
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io/status/500")
step = steps[0]
if step.has_error:
print(f"Error: {step.error}")
else:
print(f"Status: {step.response.status}")
Integration with Testing Frameworks¶
Use httptap in your test suites to verify performance requirements.
import pytest
from httptap import HTTPTapAnalyzer
def test_api_response_time():
"""Test that API responds within acceptable time."""
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io/delay/0")
# Assert TTFB is under 500ms
assert steps[0].timing.ttfb_ms < 500, \
f"TTFB too high: {steps[0].timing.ttfb_ms}ms"
# Assert total time is under 1 second
assert steps[0].timing.total_ms < 1000, \
f"Total time too high: {steps[0].timing.total_ms}ms"
def test_tls_configuration():
"""Verify TLS configuration meets security standards."""
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
# Assert TLS 1.2 or higher
assert steps[0].network.tls_version in ["TLSv1.2", "TLSv1.3"], \
f"Insecure TLS version: {steps[0].network.tls_version}"
# Assert certificate is valid for at least 30 days
assert steps[0].network.cert_days_left > 30, \
f"Certificate expiring soon: {steps[0].network.cert_days_left} days"
Environment-Specific Configuration¶
Configure httptap differently for various environments.
import os
from httptap import HTTPTapAnalyzer
# Environment-specific settings
config = {
"production": {
"timeout": 30,
"follow_redirects": True,
},
"staging": {
"timeout": 60,
"follow_redirects": True,
},
"development": {
"timeout": 120,
"follow_redirects": False,
},
}
env = os.getenv("ENVIRONMENT", "development")
settings = config[env]
analyzer = HTTPTapAnalyzer(
timeout=settings["timeout"],
follow_redirects=settings["follow_redirects"],
)
steps = analyzer.analyze_url("https://httpbin.io/status/200")
Debugging Tips¶
Enable Detailed Logging¶
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
Inspect Raw HTTP Traffic¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
# Inspect response headers
step = steps[0]
print("Response headers:")
for key, value in step.response.headers.items():
print(f" {key}: {value}")
What's Next?¶
-
Detailed interface documentation
-
Extend httptap and contribute
-
How releases work