socksio

A sans-I/O implementation of the SOCKS4, SOCKS4A, and SOCKS5 client protocols for Python.

Library
PyPI
v1.0.0
57stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity64
Maintenance16
Community40
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
81/100Excellent
Architecture88
Code Quality90
Innovation60
Learning Curve85

socksio is a sans-I/O library that implements the SOCKS4, SOCKS4A, and SOCKS5 proxy protocols entirely as pure data transformations. Instead of opening sockets itself, it exposes Connection classes per protocol version that pack outgoing request objects into raw bytes and parse incoming reply bytes back into typed reply objects, leaving all actual network I/O to the caller’s socket, asyncio, trio, or anyio code.

This design mirrors sans-I/O libraries like h11 and h2: the protocol state machine (authentication negotiation, username/password auth, command replies) lives entirely inside SOCKS5Connection/SOCKS4Connection, tracked via an explicit state enum, while byte-level encoding of IPv4, IPv6, and domain-name addresses is handled by shared utility functions. That separation makes socksio usable inside any I/O model without adapting or subclassing anything.

It’s best known as the SOCKS proxy engine underneath httpx/httpcore’s socksio extra, giving async and sync HTTP clients SOCKS4/4A/5 proxy support without those clients having to implement the wire protocol themselves.

What You Get

  • Per-protocol Connection classes - SOCKS4Connection and SOCKS5Connection track proxy handshake state and turn request objects into bytes to send / bytes received into typed reply objects.
  • Typed request/reply objects - SOCKS4Request, SOCKS4ARequest, SOCKS5CommandRequest, SOCKS5AuthMethodsRequest, SOCKS5UsernamePasswordRequest, and their reply counterparts are NamedTuples with dumps()/loads() methods.
  • Address encoding/decoding helpers - encode_address/decode_address transparently handle IPv4, IPv6, and domain-name addressing, including a "host:port" string convenience form via from_address().
  • No I/O, no dependencies - the library has zero runtime dependencies and never touches a socket, so it works identically under blocking sockets, asyncio, trio, curio, or any other I/O model.
  • Explicit protocol errors - malformed or out-of-sequence proxy replies raise ProtocolError (a SOCKSError subclass) instead of failing silently or crashing on bad bytes.
  • SOCKS5 auth negotiation support - no-auth and username/password authentication flows are modeled as explicit connection states (CLIENT_AUTH_REQUIREDSERVER_AUTH_REPLYCLIENT_AUTHENTICATEDTUNNEL_READY).

Common Use Cases

  • Adding SOCKS proxy support to an HTTP client - libraries like httpx/httpcore use socksio to implement their SOCKS4/4A/5 proxy transport without writing protocol code themselves.
  • Building a custom async proxy client - developers writing an asyncio, trio, or anyio-based network tool use socksio’s Connection objects to drive the SOCKS handshake over their own transport.
  • Testing or simulating SOCKS proxy behavior - the pure encode/decode API makes it straightforward to unit-test proxy request/reply framing without a real network connection.
  • Tunneling arbitrary TCP traffic through a SOCKS proxy - any tool that needs to CONNECT through a SOCKS4, SOCKS4A, or SOCKS5 proxy (Tor being a common SOCKS5 case) can reuse socksio instead of reimplementing the protocol.

Under The Hood

Architecture socksio follows the sans-I/O pattern popularized by h11/h2: socks4.py and socks5.py each define request/reply NamedTuples with dumps()/loads() methods, plus a Connection class (SOCKS4Connection, SOCKS5Connection) that owns an internal send buffer and, for SOCKS5, an explicit SOCKS5State enum tracking handshake progress (CLIENT_AUTH_REQUIRED through TUNNEL_READY). SOCKS5Connection.send() uses functools.singledispatchmethod to route different request types to protocol-appropriate packing logic, while receive_data() branches on the current state to decide which reply type to parse. Address handling (IPv4/IPv6/domain-name detection and encoding) is factored into shared utils.py functions used by both protocol modules, and a _types.py module centralizes shared type aliases. Because no method ever touches a socket, the entire library is a stateless-except-for-buffers transformation layer; changing how bytes are actually sent only requires the caller’s I/O glue code, not socksio itself.

Tech Stack The project targets Python 3.10+ with zero runtime dependencies, built and packaged via flit_core per pyproject.toml. Development tooling is orchestrated through nox (noxfile.py) with sessions for linting (autoflake, isort, black, flake8 with flake8-bugbear/flake8-comprehensions, mypy --strict), multi-version testing (pytest across Python 3.10 through 3.15), and Sphinx documentation builds. A docker/docker-compose.yml setup runs a real Dante SOCKS server for integration-style proxy testing rather than relying solely on unit tests against synthetic bytes.

Code Quality Tests live under tests/ (test_socks4.py, test_socks5.py, test_utils.py, roughly 566 lines total) and are heavily parametrized with pytest.mark.parametrize, covering both success paths and malformed/out-of-order protocol error cases via pytest.raises. Every public class, method, and function carries a docstring with Args/Returns/Raises sections. Typing is exhaustive and enforced by mypy --strict in CI; flake8 plus flake8-bugbear/flake8-comprehensions and black/isort formatting are enforced in the same nox -s check session that CI runs on every push, alongside a matrix test job across six Python versions with Codecov coverage upload.

What Makes It Unique socksio’s distinguishing choice is committing fully to the sans-I/O discipline for a protocol (SOCKS proxying) that is usually implemented ad hoc, inline, inside whatever HTTP or network client needs it. By isolating the SOCKS4/4A/5 state machine and byte framing behind a small, dependency-free, I/O-agnostic API, it lets any transport — blocking sockets, asyncio, trio, curio — reuse the exact same protocol implementation, which is precisely why httpx/httpcore adopted it as their proxy engine rather than writing SOCKS handling themselves.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search