| """Shared outbound HTTP client factory. |
| |
| Deliberately plain: the default dual-stack resolver and a moderate timeout. |
| |
| History: v3.8.2 pinned outbound sockets to IPv4 (local_address="0.0.0.0") to |
| chase a suspected IPv6 routing problem. Analytics writes had been succeeding |
| before that change and stopped after it, so the pin is reverted. Forcing a |
| local address can break egress in containerised hosts, and the original |
| diagnosis was not supported by evidence: a 12s GET failed while an 8s POST to |
| the same host succeeded, which rules out plain slowness. |
| |
| Set FORCE_IPV4_LOCAL_ADDRESS to "0.0.0.0" only to test that hypothesis again. |
| """ |
| import logging |
| from typing import Optional |
|
|
| import httpx |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| FORCE_IPV4_LOCAL_ADDRESS: Optional[str] = None |
|
|
| DEFAULT_TIMEOUT = 10.0 |
|
|
|
|
| def make_transport(retries: int = 1) -> httpx.AsyncHTTPTransport: |
| kwargs = {"retries": retries} |
| if FORCE_IPV4_LOCAL_ADDRESS: |
| kwargs["local_address"] = FORCE_IPV4_LOCAL_ADDRESS |
| try: |
| return httpx.AsyncHTTPTransport(**kwargs) |
| except Exception as exc: |
| logger.warning(f"Custom transport unavailable ({exc}); using default") |
| return httpx.AsyncHTTPTransport() |
|
|
|
|
| def make_client(timeout: float = DEFAULT_TIMEOUT, retries: int = 1) -> httpx.AsyncClient: |
| """AsyncClient with a moderate timeout and one transport-level retry.""" |
| return httpx.AsyncClient( |
| timeout=timeout, |
| transport=make_transport(retries=retries), |
| follow_redirects=True, |
| ) |
|
|