feat: named proxy pools with per-listener assignment
Add proxy_pools: top-level config (dict of name -> pool config) so
listeners can draw from different proxy sources. Each pool has
independent sources, health testing, state persistence, and refresh
cycles.
- PoolSourceConfig gains mitm: bool|None for API ?mitm=0/1 filtering
- ListenerConfig gains pool_name for named pool assignment
- ProxyPool gains name param with prefixed log messages and
per-name state file derivation (pool-{name}.json)
- server.py replaces single proxy_pool with proxy_pools dict,
validates listener pool references at startup, per-listener closure
- API /pool merges all pools (with pool field on multi-pool entries),
/status and /config expose per-pool summaries
- Backward compat: singular proxy_pool: registers as "default"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,95 @@ from s5p.config import ChainHop, PoolSourceConfig, ProxyPoolConfig
|
||||
from s5p.pool import ProxyEntry, ProxyPool
|
||||
|
||||
|
||||
class TestProxyPoolName:
|
||||
"""Test pool name and state path derivation."""
|
||||
|
||||
def test_default_name(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0)
|
||||
assert pool.name == "default"
|
||||
assert pool._log_prefix == "pool"
|
||||
|
||||
def test_named_pool(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0, name="clean")
|
||||
assert pool.name == "clean"
|
||||
assert pool._log_prefix == "pool[clean]"
|
||||
|
||||
def test_state_path_default(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0)
|
||||
assert pool._state_path.name == "pool.json"
|
||||
|
||||
def test_state_path_named(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0, name="clean")
|
||||
assert pool._state_path.name == "pool-clean.json"
|
||||
|
||||
def test_state_path_explicit_overrides_name(self):
|
||||
cfg = ProxyPoolConfig(sources=[], state_file="/data/custom.json")
|
||||
pool = ProxyPool(cfg, [], timeout=10.0, name="clean")
|
||||
assert str(pool._state_path) == "/data/custom.json"
|
||||
|
||||
|
||||
class TestProxyPoolMitmQuery:
|
||||
"""Test mitm query parameter in API fetch."""
|
||||
|
||||
def test_mitm_false(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0)
|
||||
src = PoolSourceConfig(url="http://api:8081/proxies/all", mitm=False)
|
||||
|
||||
async def run():
|
||||
from unittest.mock import AsyncMock, patch
|
||||
mock_ret = {"proxies": []}
|
||||
with patch(
|
||||
"s5p.pool.http_get_json",
|
||||
new_callable=AsyncMock, return_value=mock_ret,
|
||||
) as mock:
|
||||
await pool._fetch_api(src)
|
||||
call_url = mock.call_args[0][0]
|
||||
assert "mitm=0" in call_url
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
def test_mitm_true(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0)
|
||||
src = PoolSourceConfig(url="http://api:8081/proxies/all", mitm=True)
|
||||
|
||||
async def run():
|
||||
from unittest.mock import AsyncMock, patch
|
||||
mock_ret = {"proxies": []}
|
||||
with patch(
|
||||
"s5p.pool.http_get_json",
|
||||
new_callable=AsyncMock, return_value=mock_ret,
|
||||
) as mock:
|
||||
await pool._fetch_api(src)
|
||||
call_url = mock.call_args[0][0]
|
||||
assert "mitm=1" in call_url
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
def test_mitm_none_omitted(self):
|
||||
cfg = ProxyPoolConfig(sources=[])
|
||||
pool = ProxyPool(cfg, [], timeout=10.0)
|
||||
src = PoolSourceConfig(url="http://api:8081/proxies/all", mitm=None)
|
||||
|
||||
async def run():
|
||||
from unittest.mock import AsyncMock, patch
|
||||
mock_ret = {"proxies": []}
|
||||
with patch(
|
||||
"s5p.pool.http_get_json",
|
||||
new_callable=AsyncMock, return_value=mock_ret,
|
||||
) as mock:
|
||||
await pool._fetch_api(src)
|
||||
call_url = mock.call_args[0][0]
|
||||
assert "mitm" not in call_url
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
class TestProxyEntry:
|
||||
"""Test ProxyEntry defaults."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user