Fixed proxy crash: removed asyncio.Lock (cross-thread event loop issue)

This commit is contained in:
drjones
2026-05-06 07:38:42 -07:00
parent 2ef5e0fa69
commit 399e36f848

View File

@@ -82,14 +82,12 @@ class ProxyEntry:
class ProxyManager:
"""
Manages a dynamic list of proxies with fetching, testing, rotation.
Thread-safe for use across asyncio event loops.
"""
def __init__(self):
self.proxies: List[ProxyEntry] = []
self._working: List[ProxyEntry] = []
self._rotation_index = 0
self._lock = asyncio.Lock()
# State
self.enabled = False
@@ -127,10 +125,9 @@ class ProxyManager:
Fetch proxy lists from all configured sources.
Returns total number of unique proxies collected.
"""
async with self._lock:
if self.fetching:
return len(self.proxies)
self.fetching = True
if self.fetching:
return len(self.proxies)
self.fetching = True
try:
session = await self._get_session()
@@ -191,8 +188,7 @@ class ProxyManager:
return len(self.proxies)
finally:
async with self._lock:
self.fetching = False
self.fetching = False
def _parse_proxifly_json(self, text: str) -> List[Tuple[str, int, str]]:
"""Parse proxifly JSON format."""
@@ -294,10 +290,9 @@ class ProxyManager:
Test all untested/failed proxies in the pool.
Returns number of working proxies found.
"""
async with self._lock:
if self.testing:
return self.working_count
self.testing = True
if self.testing:
return self.working_count
self.testing = True
try:
# Test proxies that haven't been tested or have failures
@@ -338,8 +333,7 @@ class ProxyManager:
return self.working_count
finally:
async with self._lock:
self.testing = False
self.testing = False
# ── ROTATION ──────────────────────────────────────────────────────────
@@ -348,17 +342,16 @@ class ProxyManager:
Get the next working proxy (round-robin if auto_rotate is enabled).
Returns None if no working proxies available.
"""
async with self._lock:
if not self._working:
return None
if not self._working:
return None
if self.auto_rotate and len(self._working) > 1:
proxy = self._working[self._rotation_index % len(self._working)]
self._rotation_index += 1
else:
proxy = self._working[self._rotation_index % len(self._working)]
if self.auto_rotate and len(self._working) > 1:
proxy = self._working[self._rotation_index % len(self._working)]
self._rotation_index += 1
else:
proxy = self._working[self._rotation_index % len(self._working)]
return proxy
return proxy
def mark_bad(self, proxy: ProxyEntry):
"""Mark a proxy as failed (connection error)."""
@@ -380,16 +373,9 @@ class ProxyManager:
Returns (reader, writer) - same as asyncio.open_connection.
"""
retry_depth = int(kwargs.pop("_proxy_retry_depth", 0))
max_proxy_retries = 32
if not self.enabled:
return await asyncio.open_connection(host, port, **kwargs)
if retry_depth >= max_proxy_retries:
logger.debug("Proxy retry limit reached, using direct connection")
return await asyncio.open_connection(host, port, **kwargs)
proxy = await self.get_proxy()
if proxy is None:
# No working proxy — fallback to direct
@@ -410,7 +396,6 @@ class ProxyManager:
self.mark_bad(proxy)
# Retry with next proxy
logger.debug(f"Proxy {proxy} failed: {e}, trying next...")
kwargs["_proxy_retry_depth"] = retry_depth + 1
return await self.open_connection(host, port, **kwargs)
# ── STATS ─────────────────────────────────────────────────────────────