高度な機能¶
このガイドでは、httptap の高度な利用パターンとカスタマイズオプションについて説明します。
カスタムな名前解決¶
Python API を使うことで、カスタムの DNS リゾルバー実装を提供できます。httptap は常に解決された IP アドレス(IPv4/IPv6)にダイヤルしつつ、Host ヘッダーと TLS SNI のために元のホスト名を保持します。IPv6 リテラルは自動的にブラケットで囲まれるため、カスタムリゾルバーは正しい IP/ファミリのタプルを返すだけで済みます。
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")
このサブクラスは resolve() のみをオーバーライドしているため、httptap は継承された resolve_all() ではなくこのメソッドを呼び出します。そのため、各ホストは単一のアドレスに解決され、接続に失敗した場合に次のアドレスへフォールバックすることはありません。このフォールバックを維持するには、resolve_all() もオーバーライドしてください。resolve_all() によるアドレスのフォールバックを参照してください。名前を解決できない場合は、カスタムリゾルバーから DNSResolutionError を送出してください。そうすることで、その失敗は内部エラーではなくネットワークエラーとして報告されます。
カスタムな TLS インスペクション¶
httptap は TLS バージョン、暗号スイート、証明書の詳細を、レスポンスを返したライブ接続から 直接読み取ります。そのため、カスタムの TLSInspector がこれらのデータを置き換えることはありません。 カスタムインスペクターはフォールバックにすぎず、HTTPS リクエストでライブ接続から TLS データが 得られず、かつプロキシを使用していない場合にのみ呼び出され、その結果で TLS フィールドが埋められます。 インスペクターはホスト名、ポート、残りのタイムアウトを受け取り、自身で接続を開きます。 インスペクションに失敗した場合は httptap.TLSInspectionError を送出してください。そうすれば ステップは TLS の詳細なしで引き続き報告されます。証明書の検証失敗後に証明書の詳細を収集する 診断プローブは、常に組み込みの SocketTLSInspector を使用し、カスタムインスペクターは使用しません。
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())
プログラムからの利用¶
httptap を Python ライブラリとして使用し、アプリケーションに統合します。
基本的な分析¶
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")
カスタムヘッダー付き¶
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
headers = {"Authorization": "Bearer token123", "Accept": "application/json"}
steps = analyzer.analyze_url("https://httpbin.io/bearer", headers=headers)
リダイレクトの追跡¶
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)}")
各リダイレクトホップには CLI と同じヘッダーとメソッドのルールが適用されます。認証ヘッダーは別のオリジンには送信されず、303(または POST 後の 301/302)はボディなしの GET に切り替わります。詳細は --follow を参照してください。
リクエストボディの送信¶
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"},
)
TLS 検証の無視¶
ステージング環境や自己署名証明書を持つホストのトラブルシューティングでは、TLS 検証をスキップできます:
リクエストは引き続き TLS メタデータを記録しますが、証明書エラーは抑制されるため、プロトコルフローに集中できます。このフラグは man-in-the-middle 攻撃に対する保護を無効にするため、信頼できる環境でのみ使用してください。 クライアントは多くの暗号とプロトコルの要件(弱いハッシュ、古い TLS バージョン、小さい DH グループ)を緩和し、レガシーなエンドポイントがハンドシェイクを完了しやすくなるようにします。OpenSSL が完全に削除した極端に非推奨のアルゴリズム(例: 一部のプラットフォームでの RC4、3DES)は、このモードでも失敗する場合があります。
プロキシの使用¶
アウトバウンドのプロキシ(HTTP、HTTPS、SOCKS5/SOCKS5H)経由でリクエストを送ります:
すべてのプロキシ環境変数を無視して直接接続する:
リッチ出力と JSON エクスポートには、プロキシ URI とそのソース(例: (from arg --proxy)、(from env HTTPS_PROXY)、(bypassed by env no_proxy))が含まれるため、どの経路が使われたかを確認できます。
プロキシプロトコルと名前解決¶
httptap は 4 つのプロキシプロトコルをサポートしており、それぞれ名前解決の挙動が異なります:
| プロトコル | DNS の解決者 | ユースケース |
|---|---|---|
socks5h:// | プロキシサーバー | プライバシー、企業ネットワーク、内部 DNS へのアクセス |
http:// | プロキシサーバー | 標準的な HTTP プロキシ(CONNECT メソッド) |
https:// | プロキシサーバー | プロキシへの暗号化された接続 |
socks5:// | クライアント(ローカル) | 名前解決を制御する必要がある場合 |
socks5h の h サフィックスは "hostname"(curl の慣例)を表します。socks5h:// では、ホスト名がプロキシに送信され、プロキシがそれを解決します。socks5:// では、クライアントがローカルで DNS を解決し、IP をプロキシに送信します。
socks5:// の場合、ホストが複数のアドレスに解決され、プロキシがそのうちの 1 つに接続できないと応答したときは、httptap は次のアドレスを試します。プロキシ自体への接続や認証の失敗は、どのアドレスでも同じ結果になるため再試行されません。IPv6 リテラルのターゲット(https://[2001:db8::1]/)はすべての種類のプロキシで使用できます。
環境変数によるプロキシ¶
--proxy フラグが指定されない場合、httptap は環境変数をチェックします:
no_proxy/NO_PROXY- バイパスするホストのカンマ区切りリスト(小文字が優先)https_proxy/HTTPS_PROXY- HTTPS リクエスト用のプロキシ(小文字が優先)http_proxy/HTTP_PROXY- HTTP リクエスト用のプロキシ(小文字が優先)all_proxy/ALL_PROXY- すべてのプロトコル用のフォールバックプロキシ
--proxy フラグは常に環境変数よりも優先されます。
スキームのない変数(proxy.internal:3128)は、curl と同様に http:// プロキシとして扱われます。選択された変数が有効なプロキシ URL でない場合(サポートされていないスキーム、ホストの欠落、無効なポート)、リクエストはネットワークエラー(終了コード 75)で失敗します。エラーには変数名と、パスワードをマスクした URL が表示されます。
NO_PROXY のパターン:
*- すべてのホストでプロキシをバイパスするexample.com- そのホスト自体とすべてのサブドメイン.example.com- example.com のサブドメインのみ(example.com 自体は含まない)sub.example.com- sub.example.com とそのサブドメイン
照合では大文字と小文字を区別しません。CIDR 範囲やポート指定のエントリはサポートされていません。
カスタム CA バンドル¶
プライベート CA によって署名された内部エンドポイントのために、--cacert で PEM バンドルを指定します:
CLI 出力には TLS CA: custom bundle と表示され、システム以外のトラストストアが使用されたことを示します。JSON エクスポートには network.tls_custom_ca: true が含まれるため、下流のツールがカスタムトラストを検出できます。このフラグは --ignore-ssl とは相互排他です。
カスタムリクエストエグゼキューター¶
完全にカスタマイズされた挙動のために、独自のリクエストエグゼキューターを提供できます。エグゼキューターはすべてのパラメーターを RequestOptions にパッケージ化した形で受け取るため、httptap によって追加される新しいフラグは後方互換のままです。トランスポートがサポートするすべてのフィールドを転送してください。1 つでも落とすと、リクエストが暗黙のうちに変わってしまいます(たとえば、method と content を無視すると POST がボディなしの GET になり、proxy や ca_bundle_path を無視すると --proxy や --cacert の設定が失われます)。
from httptap import HTTPTapAnalyzer, RequestExecutor, RequestOptions, RequestOutcome
from httptap.http_client import make_request
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)
timing, network, response = make_request(
options.url,
options.timeout,
deadline=options.deadline,
method=options.method,
content=options.content,
http2=options.http2,
verify_ssl=options.verify_ssl,
ca_bundle_path=options.ca_bundle_path,
proxy=options.proxy,
noproxy=options.noproxy,
dns_resolver=options.dns_resolver,
tls_inspector=options.tls_inspector,
timing_collector=options.timing_collector,
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'}
force_new_connection は非推奨で無視されるため、転送していません。デフォルトの挙動を再実装する代わりにラップしたい場合は、HTTPClientRequestExecutor().execute(options) に委譲してください。トランスポートの障害には httptap.http_client.HTTPClientError を送出してください。そうすることで、アナライザーはそれを部分的なデータ付きのネットワークエラー(終了コード 75)として記録します。それ以外の例外は内部エラー(終了コード 70)として報告されます。
カスタムな可視化¶
Visualizer プロトコルを実装して、独自の可視化を作成します。
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)
カスタムなエクスポート形式¶
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")
パフォーマンス監視¶
httptap を継続的なパフォーマンス監視に使用します。
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)
バッチ分析¶
複数の URL を並行して分析します。
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")
エラー処理¶
URL を分析する際に、エラーを適切に処理します。has_error が設定されるのはリクエスト自体が 失敗した場合(DNS、接続、TLS、タイムアウト)のみです。HTTP 4xx/5xx レスポンスは完了した ステップなので、response.status を別途確認してください。
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://nonexistent.invalid")
step = steps[0]
if step.has_error:
print(f"Error: {step.error}")
elif step.response.status >= 400:
print(f"HTTP error: {step.response.status}")
else:
print(f"Status: {step.response.status}")
テストフレームワークとの統合¶
パフォーマンス要件を検証するために、テストスイートで httptap を使用します。
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"
環境ごとの設定¶
さまざまな環境に合わせて httptap を設定します。
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")
デバッグのヒント¶
詳細なロギングを有効にする¶
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
from httptap import HTTPTapAnalyzer
analyzer = HTTPTapAnalyzer()
steps = analyzer.analyze_url("https://httpbin.io")
生の HTTP トラフィックを検査する¶
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}")
次のステップ¶
-
詳細なインターフェースのドキュメント
-
httptap を拡張してコントリビュートする
-
リリースの仕組み