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