From 0509d4104df5cb869635ab52a9b024ca41128197 Mon Sep 17 00:00:00 2001 From: Dr Jones Date: Thu, 16 Apr 2026 01:12:56 -0700 Subject: [PATCH] Fix validation: 0 valid proxies (httpx shared client bug) httpx.AsyncClient.get() does not accept proxy= per-request. Shared client caused every check_one call to raise TypeError, silently caught as False, so 0/N proxies ever passed. Reverted to per-proxy AsyncClient(proxy=url) in _check_one. Semaphore concurrency + wall-clock cap preserved. Also re-adds _check_one helper that was accidentally removed. Made-with: Cursor --- proxy_chain_manager/validator.py | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/proxy_chain_manager/validator.py b/proxy_chain_manager/validator.py index 1bbb6ca..ab08da5 100644 --- a/proxy_chain_manager/validator.py +++ b/proxy_chain_manager/validator.py @@ -65,49 +65,33 @@ async def validate_proxies( (check_url[:70] + "…") if len(check_url) > 70 else check_url, ) - t = httpx.Timeout(timeout_seconds, connect=min(8.0, timeout_seconds)) - lim = max(32, min(256, c * 8)) - limits = httpx.Limits(max_connections=lim, max_keepalive_connections=max(16, c * 4)) - last_prog_t = 0.0 prog_step = max(1, total // 120) - async def _run_all(client: httpx.AsyncClient) -> None: + async def one(u: str) -> None: nonlocal done, last_prog_t + async with sem: + good = await _check_one(u, check_url, timeout_seconds) + async with lock: + done += 1 + if good: + ok.append(u) + if on_progress: + now = time.monotonic() + if ( + done == total + or done == 1 + or done % prog_step == 0 + or (now - last_prog_t) >= 0.1 + ): + last_prog_t = now + on_progress(done, total) - async def one(u: str) -> None: - nonlocal done, last_prog_t - async with sem: - try: - r = await client.get(check_url, proxy=u) - good = r.status_code == 200 and len(r.content) > 0 - except Exception: - good = False - async with lock: - done += 1 - if good: - ok.append(u) - if on_progress: - now = time.monotonic() - if ( - done == total - or done == 1 - or done % prog_step == 0 - or (now - last_prog_t) >= 0.1 - ): - last_prog_t = now - on_progress(done, total) - + async def _run_all() -> None: await asyncio.gather(*(one(u) for u in proxy_urls)) try: - async with httpx.AsyncClient( - timeout=t, - verify=False, - follow_redirects=True, - limits=limits, - ) as client: - await asyncio.wait_for(_run_all(client), timeout=cap) + await asyncio.wait_for(_run_all(), timeout=cap) except asyncio.TimeoutError: log.warning( "validate_proxies wall-clock cap %.0fs exceeded (%d URLs) — returning partial results", @@ -118,6 +102,22 @@ async def validate_proxies( return ok +async def _check_one(proxy_url: str, check_url: str, timeout_seconds: float) -> bool: + """Check a single proxy by fetching check_url through it. True = live and returns 200.""" + t = httpx.Timeout(timeout_seconds, connect=min(8.0, timeout_seconds)) + try: + async with httpx.AsyncClient( + proxy=proxy_url, + timeout=t, + verify=False, + follow_redirects=True, + ) as c: + r = await c.get(check_url) + return r.status_code == 200 and len(r.content) > 0 + except Exception: + return False + + async def check_chain_exit_ip( listen_proxy: str, check_url: str,