Commit Graph

24 Commits

Author SHA1 Message Date
user
918d03cc58 feat: skip pool hops for .onion destinations
Onion addresses require Tor to resolve, so pool proxies after Tor
would break connectivity. Detect .onion targets and use the static
chain only (Tor), skipping pool selection and retries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 02:28:34 +01:00
user
c191942712 feat: add bypass rules, weighted pool selection, integration tests
Per-listener bypass rules skip the chain for local/private destinations
(CIDR, exact IP/hostname, domain suffix). Weighted multi-candidate pool
selection biases toward pools with more alive proxies. End-to-end
integration tests validate the full client->s5p->hop->target path using
mock SOCKS5 proxies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:58:12 +01:00
user
ef0d8f347b feat: add per-hop pool references in listener chains
Allow listeners to mix named pools in a single chain using pool:name
syntax. Bare "pool" continues to use the listener's default pool.
Replaces pool_hops field with pool_seq list; pool_hops is now a
backward-compatible property. Each hop draws from its own pool and
failure reporting targets the correct source pool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:50:17 +01:00
user
29b4a36863 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>
2026-02-18 11:33:53 +01:00
user
288bd95f62 feat: multi-Tor round-robin via tor_nodes config
New top-level tor_nodes list distributes traffic across multiple Tor
SOCKS proxies. First hop is replaced at connection time by round-robin
selection; health tests also rotate across all nodes. FirstHopPools
are created for each node when pool_size > 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:12:58 +01:00
user
b3966c9a9f feat: dynamic health test concurrency
Auto-scale test concurrency to ~10% of proxy count, capped by
test_concurrency config ceiling (default raised from 5 to 25).
Prevents saturating upstream Tor when pool size varies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:09:44 +01:00
user
d4e3638143 feat: per-listener latency tracking
Each listener now tracks chain setup latency independently via a
dict[str, LatencyTracker] on Metrics. The global aggregate stays for
summary output. /status embeds per-listener latency on each listener
entry; /metrics includes a listener_latency map keyed by host:port.
2026-02-18 08:14:09 +01:00
user
b8f7217e43 feat: connection rate and chain latency metrics
Add RateTracker (rolling deque, events/sec) and LatencyTracker (circular
buffer, p50/p95/p99 in ms) to the Metrics class.  Both are recorded in
_handle_client and exposed via summary(), to_dict(), /status, and /metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 00:16:46 +01:00
user
7dc3926f48 feat: multi-listener with configurable proxy chaining
Each listener binds to its own port with an independent chain.
The "pool" keyword in a chain appends a random alive proxy from
the shared pool; multiple pool entries = multiple hops.

  :1080 -> Tor only (0 pool hops)
  :1081 -> Tor + 1 pool proxy
  :1082 -> Tor + 2 pool proxies

Shared resources (ProxyPool, Tor, metrics, semaphore, API) are
reused across listeners. FirstHopPool is shared per unique first
hop. Backward compatible: old listen/chain format still works.
2026-02-17 22:03:37 +01:00
user
e78fc8dc3c feat: replace HTTP health check with TLS handshake
Replace _http_check (HTTP GET to httpbin.org) with _tls_check that
performs a TLS handshake through the proxy chain. Multiple targets
(google, cloudflare, amazon) rotated round-robin eliminate the single
point of failure. Lighter, faster, harder to block than HTTP.

- Add test_targets config field (replaces test_url)
- Backward compat: legacy test_url extracts hostname automatically
- Add ssl.create_default_context() and round-robin index to ProxyPool
- Update docs (example.yaml, USAGE.md, CHEATSHEET.md)
2026-02-17 18:26:21 +01:00
user
f0281c4069 test: Tor controller and API endpoint tests
Covers: password/cookie/bare auth, auth failure, connect failure,
NEWNYM success/rate-limiting/reconnect, GETINFO multi-line parsing,
start/stop lifecycle, GET /tor status, POST /tor/newnym dispatch,
and TorConfig YAML parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:09:05 +01:00
user
4ee2cf5bb0 test: add control API tests
29 tests covering request parsing, JSON response format, all GET/POST
handlers with mock context, 404/405 error routing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:05:28 +01:00
user
5418b30441 test: add tests for extracted parse_api_proxies
Cover valid entries, invalid proto/port, missing keys, and
mixed valid/invalid input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:51:55 +01:00
user
248f5c3306 feat: pre-warmed TCP connection pool to first hop
Add FirstHopPool that maintains a deque of pre-established TCP
connections to chain[0]. Connections idle beyond pool_max_idle are
evicted; a background task refills to pool_size. build_chain() tries
the pool first, falls back to open_connection. Enabled with
pool_size > 0 in config. Only pools the TCP handshake -- SOCKS/HTTP
tunnels are consumed, not returned.
2026-02-15 17:56:03 +01:00
user
903cb38b9f feat: async HTTP client and parallel source fetching
Replace blocking urllib with a minimal async HTTP/1.1 client (http.py)
using asyncio streams. Pool source fetches now run in parallel via
asyncio.gather. Dead proxy reporting uses async POST. Handles
Content-Length, chunked transfer-encoding, and connection-close bodies.
No new dependencies.
2026-02-15 17:55:56 +01:00
user
714e8efb3d feat: cap concurrent connections with semaphore
Add max_connections config (default 256) with -m/--max-connections CLI
flag. Server wraps on_client in asyncio.Semaphore to prevent fd
exhaustion under load. Value reloads on SIGHUP; active connections
drain normally. Also adds pool_size/pool_max_idle config fields and
first_hop_pool wiring in server.py (used by next commits), and fixes
asyncio.TimeoutError -> TimeoutError lint warnings.
2026-02-15 17:55:50 +01:00
user
650db64d70 feat: add dead proxy reporting to source API
When report_url is configured, POST evicted proxy list as JSON after
each health test cycle. Fire-and-forget: failures are logged at debug
level. Payload format: {"dead": [{"proto": "socks5", "proxy": "host:port"}]}.
2026-02-15 16:04:19 +01:00
user
0816a7f0cb feat: add static chain health check before pool tests
Test the static chain (without pool proxy) before running pool health
tests. If the chain itself is unreachable, skip proxy testing and log a
clear warning. Prevents false mass-failure when the issue is upstream
(e.g., Tor is down), not the exit proxies.
2026-02-15 15:59:26 +01:00
user
8e2d6a654a feat: add fast warm start with deferred full health test
On warm start (state has alive proxies), only quick-test the
previously-alive subset before serving. Full health test runs in
background. Cold start behavior unchanged (test all before serving).
Reduces startup blocking from minutes to seconds on warm restarts.
2026-02-15 15:58:22 +01:00
user
e1403a67fc feat: add stale proxy expiry based on last_seen TTL
Evict proxies not returned by sources for >3 refresh cycles and not
currently alive. Cleans up proxies removed upstream faster than waiting
for max_fails consecutive health test failures.
2026-02-15 15:54:17 +01:00
user
4801e70b93 feat: add per-proxy backoff after connection failure
Track last_fail timestamp on ProxyEntry. When a connection attempt fails
in server.py, report_failure() records the time. The selection weight
multiplies by min(fail_age/60, 1.0), ramping back from floor over 60s.
Prevents wasting retries on proxies that just failed.
2026-02-15 15:53:39 +01:00
user
b60264b865 feat: add weighted proxy selection based on recency
Replace uniform random.choice with random.choices weighted by last_ok
recency. Proxies tested successfully more recently get higher selection
probability (weight = 1/(1 + age/300)), decaying over ~5 minutes.
2026-02-15 15:48:40 +01:00
user
72adf2f658 feat: add managed proxy pool with health testing
ProxyPool replaces ProxySource with:
- Multiple sources: HTTP APIs and text files (one proxy URL per line)
- Deduplication by proto://host:port
- Health testing: full chain test with configurable concurrency
- Mass-failure guard: skip eviction when >90% fail
- Background loops for periodic refresh and health checks
- JSON state persistence with atomic writes (warm starts)
- Backward compat: ProxySource still works for legacy configs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:11:19 +01:00
user
0710dda8da feat: initial SOCKS5 proxy with chain support
Asyncio-based SOCKS5 server that tunnels connections through
configurable chains of SOCKS5, SOCKS4/4a, and HTTP CONNECT proxies.
Tor integration via standard SOCKS5 hop.
2026-02-15 03:10:25 +01:00