Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/python-test-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ on:
pull_request:
branches: [master]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
poetry-version: ["2.3.2"]

steps:
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
default_language_version:
# `python3` (any 3.x) so contributors on any of the supported Python
# versions (3.10-3.14) can run pre-commit without installing an extra
# versions (3.10-3.15) can run pre-commit without installing an extra
# interpreter. Pinning a specific minor breaks the others.
python: python3
fail_fast: false
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Python 3.15 on the supported matrix (now 3.10-3.15). CPython 3.15.0
shipped on 2026-10-09. The published image stays on `python:3.14-slim`;
the executable and publish workflows still build with 3.14.

### Fixed
- User-facing dependency floors now match `setup.py`. The docs homepage
still said aiohttp >= 3.8.0, aiodns >= 3.0.0, and maxminddb >= 2.0.0.
`docs/requirements.txt` and `environment.yml` carried older, mismatched floors
(aiohttp >= 3.12.0, aiodns >= 3.4.0, maxminddb >= 2.7.0).

### Security
- Raise the aiohttp constraint floor to `^3.14.4` (`setup.py`: `>=3.14.4`).
`^3.14.1` still allowed 3.14.1–3.14.3, which miss the October 2026 fixes
shipped in 3.14.4: GHSA-2g87-pp6c-29x5, GHSA-9xw5-w5jj-pvcg, and
GHSA-5wr8-gcqf-gqf3. OSV rates them HIGH (CVSS 4.0); aiohttp's GitHub
advisories label them medium, medium, and low. `poetry.lock` stays on 3.14.5.

## [2.0.0b3] - 2026-05-09

🌐 **Full IPv6 support — first-class across the stack**
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
ProxyBroker2 is an async proxy finder, checker, and server. It discovers public proxies from 50+ sources, validates them against judge servers, and can operate as a rotating proxy server.

**Repository**: `bluet/proxybroker2` (GitHub)
**Python**: 3.10-3.14
**Key Dependencies**: aiohttp 3.12.0+, aiodns 3.4.0+, attrs 25.3.0+, asyncio
**Python**: 3.10-3.15
**Key Dependencies**: aiohttp >=3.14.4, aiodns >=4.0.4, attrs >=26.1.0,<27, maxminddb >=3.1.1, cachetools >=7.1.4, click >=8.4.1, PyYAML >=6.0.3

## Common Development Commands

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ProxyBroker2

*🚀 Production-Ready Async Proxy Management*

**The modernized, actively-maintained successor to ProxyBroker (v0.3.2, abandoned 2018) — Python 3.10-3.14, comprehensive test coverage, first-class IPv6.**
**The modernized, actively-maintained successor to ProxyBroker (v0.3.2, abandoned 2018) — Python 3.10-3.15, comprehensive test coverage, first-class IPv6.**
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbluet%2Fproxybroker2.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbluet%2Fproxybroker2?ref=badge_shield)
Expand Down Expand Up @@ -41,7 +41,7 @@ ProxyBroker2 is the actively-maintained successor to the original ProxyBroker (v
### 🚀 What you get over v0.3.2

- **Active maintenance** — regular security updates and feature work
- **Modern Python support** — Python 3.10-3.14 (v0.3.2 was Python 3.5+ only)
- **Modern Python support** — Python 3.10-3.15 (v0.3.2 was Python 3.5+ only)
- **Zero critical bugs** — fixed deadlocks, memory leaks, signal handler leaks, race conditions that existed in v0.3.2
- **First-class IPv6** — RFC 5952 canonical form, SOCKS5 `ATYP=0x04`, bracketed URI authority. v0.3.2 was IPv4-only.
- **Custom providers** — drop YAML/JSON config files into a folder, no code required (see [Custom Proxy Providers](#-custom-proxy-providers) below)
Expand Down Expand Up @@ -72,7 +72,7 @@ $ docker run --rm bluet/proxybroker2 --help
Requirements
------------

- **Python 3.10-3.14**
- **Python 3.10-3.15**

That's it — all Python dependencies are installed automatically by pip / Poetry / uv.

Expand Down
10 changes: 7 additions & 3 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ sphinx>=8.0
myst-parser>=4.0
alabaster>=1.0.0
linkify-it-py>=2.0.0
aiohttp>=3.12.0
aiodns>=3.4.0
maxminddb>=2.7.0
aiohttp>=3.14.4
aiodns>=4.0.4
attrs>=26.1.0,<27.0.0
maxminddb>=3.1.1
cachetools>=7.1.4
click>=8.4.1
pyyaml>=6.0.3
19 changes: 13 additions & 6 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ProxyBroker2
:target: https://github.com/bluet/proxybroker2/releases
.. image:: https://img.shields.io/github/actions/workflow/status/bluet/proxybroker2/python-test-versions.yml?style=flat-square
:target: https://github.com/bluet/proxybroker2/actions
.. image:: https://img.shields.io/badge/python-3.10--3.13-blue.svg?style=flat-square
.. image:: https://img.shields.io/badge/python-3.10--3.15-blue.svg?style=flat-square
:target: https://github.com/bluet/proxybroker2
.. image:: https://img.shields.io/github/license/bluet/proxybroker2.svg?style=flat-square
:target: https://github.com/bluet/proxybroker2/blob/master/LICENSE
Expand All @@ -25,7 +25,7 @@ ProxyBroker2 is a **production-ready** async proxy finder, checker, and server t

* ✅ **Zero Critical Bugs** - All signal handler leaks, deadlocks, and heap corruption fixed
* ✅ **100% Test Coverage** - All 131 tests passing with comprehensive validation
* ✅ **Modern Python Support** - Full compatibility with Python 3.10-3.13
* ✅ **Modern Python Support** - Full compatibility with Python 3.10-3.15
* ✅ **Production Ready** - Thoroughly tested and optimized for real-world usage

.. image:: _static/index_find_example.gif
Expand All @@ -46,10 +46,17 @@ Features
Requirements
------------

* Python **3.10** or higher
* `aiohttp <https://pypi.python.org/pypi/aiohttp>`_ >= 3.8.0
* `aiodns <https://pypi.python.org/pypi/aiodns>`_ >= 3.0.0
* `maxminddb <https://pypi.python.org/pypi/maxminddb>`_ >= 2.0.0
* Python **3.10** or higher (tested on 3.10–3.15)
* `aiohttp <https://pypi.python.org/pypi/aiohttp>`_ >= 3.14.4
* `aiodns <https://pypi.python.org/pypi/aiodns>`_ >= 4.0.4
* `attrs <https://pypi.python.org/pypi/attrs>`_ >= 26.1.0, < 27
* `maxminddb <https://pypi.python.org/pypi/maxminddb>`_ >= 3.1.1
* `cachetools <https://pypi.python.org/pypi/cachetools>`_ >= 7.1.4
* `click <https://pypi.python.org/pypi/click>`_ >= 8.4.1
* `PyYAML <https://pypi.python.org/pypi/PyYAML>`_ >= 6.0.3

These are the minimum versions ``pip`` will accept. Poetry uses the same floors
and caps each dependency before the next major version.


Installation
Expand Down
13 changes: 7 additions & 6 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ dependencies:
- sphinx>=8.0
- myst-parser>=4.0
- alabaster>=1.0.0
- aiohttp>=3.12.0
- aiodns>=3.4.0
- maxminddb>=2.7.0
- attrs>=25.3.0
- cachetools>=5.5.2
- click>=8.2.1
- aiohttp>=3.14.4
- aiodns>=4.0.4
- maxminddb>=3.1.1
- attrs>=26.1.0,<27.0.0
- cachetools>=7.1.4
- click>=8.4.1
- pyyaml>=6.0.3
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ classifiers = [
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Programming Language :: Python :: 3.15',
'Operating System :: POSIX',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
Expand All @@ -32,15 +33,17 @@ classifiers = [
# Lower bounds bumped from prior version to clear known vulnerabilities at
# the constraint floor (snyk scans the lower bound when no specific version
# is locked). Notable:
# - click ^8.3.3 fixes SNYK-PYTHON-CLICK-16347201 (HIGH, command injection
# in click.edit() filename param)
# - aiohttp ^3.13.5 fixes multiple HIGH vulns (SSRF on Windows static
# handler, memory exhaustion in multipart/Request.post/ZLibDecompressor,
# request smuggling)
# - click ^8.4.1. 8.3.3 fixes SNYK-PYTHON-CLICK-16347201 (HIGH, command
# injection in click.edit() filename param); the floor tracks the lock.
# - aiohttp ^3.14.4. No current OSV advisory is fixed only after 3.14.4.
# 3.14.1–3.14.3 still miss the October 2026 fixes GHSA-2g87-pp6c-29x5,
# GHSA-9xw5-w5jj-pvcg, and GHSA-5wr8-gcqf-gqf3. OSV rates them HIGH
# (CVSS 4.0); aiohttp's GitHub advisories label them medium, medium, low.
# poetry.lock resolves 3.14.5.
# Other deps bumped to match poetry.lock current resolution for consistency.
[tool.poetry.dependencies]
python = "^3.10"
aiohttp = "^3.14.1"
aiohttp = "^3.14.4"
Comment thread
bluet marked this conversation as resolved.
Resolved
aiodns = "^4.0.4"
attrs = ">=26.1.0,<27.0.0"
maxminddb = "^3.1.1"
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_version():
INFO["long_description"] = f.read()

REQUIRES = [
"aiohttp>=3.14.1",
"aiohttp>=3.14.4",
"aiodns>=4.0.4",
"attrs>=26.1.0,<27.0.0",
"maxminddb>=3.1.1",
Expand Down Expand Up @@ -76,6 +76,7 @@ def get_version():
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
Expand Down
60 changes: 33 additions & 27 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,45 @@ class TestUserWorkflows:
"""Test real user workflows based on examples/ directory."""

@pytest.mark.asyncio
async def test_basic_find_workflow(self):
"""Test the basic find workflow from examples/basic.py.

Users depend on this workflow pattern working consistently.
async def test_basic_find_workflow(self, mocker):
"""find() accepts the example kwargs and finishes an empty run.

``examples/basic.py`` stops when the proxy queue yields ``None``.
``find()`` discovers this host's external IP before it creates the
checker. That probe has no overall deadline (each candidate gets
the full timeout), so a live call can sit there longer than any
fixed poll when CI cannot reach the IP services. The probe and
the judge/provider fan-out are covered elsewhere; this test stubs
them and uses ``providers=[]``, which is the supported "no bundled
feeds" contract.
"""
proxies = asyncio.Queue()
broker = Broker(
proxies, timeout=2, max_conn=5, max_tries=1, stop_broker_on_sigint=False
proxies,
timeout=2,
max_conn=5,
max_tries=1,
stop_broker_on_sigint=False,
providers=[],
)
discovered = frozenset({"203.0.113.10"})
mocker.patch.object(
broker._resolver,
"get_real_ext_ips",
new=mocker.AsyncMock(return_value=discovered),
)
# Default judges are real URLs. ``check_judges`` is what dials them.
mocker.patch("proxybroker.checker.Checker.check_judges", new=mocker.AsyncMock())

# Test that the basic API contract works - we don't need to complete the operation
# Just verify the API accepts the expected parameters without errors.
await broker.find(types=["HTTP", "HTTPS"], limit=1)
try:
find_task = asyncio.create_task(
broker.find(types=["HTTP", "HTTPS"], limit=1)
)
# Wait for the broker to actually initialize its checker rather
# than relying on a fixed sleep (which races on slow CI runners).
for _ in range(50): # up to 5s
if broker._checker is not None:
break
await asyncio.sleep(0.1)
find_task.cancel()
try:
await find_task
except asyncio.CancelledError:
pass
except Exception as e:
# If it fails immediately, that indicates an API contract issue
pytest.fail(f"find() API contract broken: {e}")

# Verify broker was properly configured for the find operation.
assert broker._checker is not None # Checker should be created for find()
assert broker._checker is not None
assert broker._checker._real_ext_ips == discovered
# ``_grab`` pushes this sentinel after an empty provider pass.
# A 2s cap only covers local scheduling, not network probes.
assert await asyncio.wait_for(proxies.get(), timeout=2) is None
finally:
broker.stop()

@pytest.mark.asyncio
async def test_proxy_pool_workflow(self):
Expand Down
Loading