Open
Conversation
Adds native SOCKS5 proxy support (RFC 1928, RFC 1929) via new socket classes.
## New Files
| File | Purpose |
|------|---------|
| `lib/excon/socks5.rb` | Shared SOCKS5 protocol module |
| `lib/excon/socks5_socket.rb` | HTTP connections through SOCKS5 |
| `lib/excon/socks5_ssl_socket.rb` | HTTPS connections through SOCKS5 |
## Usage
```ruby
# Basic SOCKS5 proxy
Excon.get('http://example.com', socks5_proxy: 'localhost:1080')
# With authentication
Excon.get('https://example.com', socks5_proxy: 'user:pass@proxy:1080')
# URL format also supported
Excon.get('https://example.com', socks5_proxy: 'socks5://proxy:1080')
```
## Features
- RFC 1928 compliant SOCKS5 implementation
- RFC 1929 username/password authentication
- Remote DNS resolution (proxy resolves hostnames)
- Full SSL/TLS support for HTTPS targets
- Timeout support during SOCKS5 handshake
- Hostname length validation (max 255 bytes per RFC)
## Why This Is Needed
SOCKS5 proxies are commonly used to:
- Access Kubernetes clusters through bastion hosts
- Route traffic through SSH tunnels (ssh -D)
- Use VPN services like Tailscale that expose SOCKS5 proxies
- Access resources behind firewalls
Unlike HTTP proxies (which Excon already supports), SOCKS5 operates at
the TCP layer and can tunnel any protocol without understanding it.
Replace custom TCP connect and duplicated SSL setup with a proxy-swap pattern: temporarily inject the SOCKS5 proxy as @DaTa[:proxy] so Socket#connect handles the TCP connection (inheriting DNS resolution, nonblock, retry, keepalive, reuseaddr, remote_ip tracking), then clear :proxy and run the SOCKS5 handshake on the raw socket. Key changes: - SOCKS5SSLSocket now inherits SSLSocket instead of Socket, eliminating ~100 lines of duplicated SSL context/handshake code - Remove connect_to_proxy and connect_nonblock_to_proxy from SOCKS5 module (base class handles TCP connect) - Register :socks5_proxy in VALID_CONNECTION_KEYS - Add 25 RSpec tests: parsing, hierarchy, HTTP/HTTPS e2e through a fake SOCKS5 server, auth, error cases, and edge cases
Excon::Error::Socket#initialize expects an exception object (calls .message and .backtrace), not a bare string. Wrap error messages in Exception.new() to match the pattern used throughout the codebase.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CI validation run