Skip to content

feat: support SOCKS5 proxies in the HTTP client - #1579

Merged
jlouvel merged 3 commits into
3.0from
feat/netty-socks-proxy
Nov 1, 2026
Merged

jlouvel merged 3 commits into
3.0from
feat/netty-socks-proxy

Conversation

@jlouvel

@jlouvel jlouvel commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1573. Based on #1578.

The Netty client only supported HTTP proxies. Some environments only provide a SOCKS proxy, such as SSH dynamic port forwarding (ssh -D) or some corporate networks.

Changes

  • NettyClientHelper:
    • New connector parameters, each falling back to the JDK system property of the same purpose:

      Parameter System property Default
      socksProxyHost socksProxyHost none
      socksProxyPort socksProxyPort 1080
      socksProxyUsername java.net.socks.username none
      socksProxyPassword java.net.socks.password none
    • Requests without an HTTP proxy for their scheme go through Netty's Socks5ProxyHandler, for both HTTP and HTTPS. The HTTP proxy takes precedence. Requests use origin-form targets, and the proxy resolves the host names (like socks5h). nonProxyHosts applies to the SOCKS proxy too, and HTTP/3 is not used through it, since QUIC would need UDP ASSOCIATE.

    • initConnection() now builds either a Socks5ProxyHandler or an HttpProxyHandler, and shares the setup from fix: send proxy credentials with the CONNECT request of HTTPS tunnels #1574: protocol handlers are added only once the proxy connection is established, and proxy failures are reported through the ready promise.

    • New public getters and Javadoc rows for the parameters.

  • SocksProxyTestCase: a minimal SOCKS5 test proxy (RFC 1928 / RFC 1929). Tests cover HTTP with connection reuse, HTTPS with username/password, refused credentials (connector error), nonProxyHosts bypass, and the system properties.
  • changes.md: entry under Enhancements.

netty-handler-proxy is already a dependency, and it brings netty-codec-socks, so no new library is needed. Only SOCKS5 is supported, not SOCKS4/4a.

Behavior change (accepted)

A JVM with the socksProxyHost system property set now sends the requests of the Restlet client through that SOCKS proxy, as java.net.Socket already does. Before, the Netty client ignored the property. As with the HTTP proxy, the loopback addresses bypass it unless nonProxyHosts says otherwise (see #1577).

Validation

ProxyTestCase (11 tests) and SocksProxyTestCase (5 tests) pass.

🤖 Generated with Claude Code

@jlouvel

jlouvel commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator Author

Full module verify passed at the top of the stack (#1580 branch, which includes this PR): 2,228 tests, 0 failures; JaCoCo checks met; SpotBugs 8 findings (threshold 9).

@jlouvel
jlouvel requested a balanced review from Copilot October 30, 2026 16:11
@jlouvel jlouvel self-assigned this Oct 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

SOCKS-routed requests can leak HTTP proxy credentials to origin servers, and an empty SOCKS host incorrectly routes traffic to a loopback proxy instead of connecting directly.

2 open issues
What changed in this PR

Adds SOCKS5 proxy support to the Netty HTTP client for environments such as SSH dynamic forwarding, while preserving HTTP proxy precedence.

Changes:

  • Adds SOCKS configuration parameters, system-property fallbacks, authentication, and proxy-side hostname resolution.
  • Applies proxy bypass rules to SOCKS and avoids HTTP/3 on SOCKS routes.
  • Adds integration tests and a changelog entry.
File Description
org.restlet/​src/​test/​java/​org/​restlet/​engine/​connector/​SocksProxyTestCase.java Adds a test SOCKS5 proxy and coverage for routing, authentication, reuse, bypass, and system properties.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​netty/​NettyClientHelper.java Implements SOCKS5 configuration and connection handling for HTTP and HTTPS.
changes.md Documents SOCKS5 support and configuration options.

Review effort: Balanced

@jlouvel
jlouvel force-pushed the feat/netty-socks-proxy branch from 1b3068f to 8a20e06 Compare October 31, 2026 07:44
@jlouvel
jlouvel force-pushed the feat/netty-socks-proxy branch from 8a20e06 to b9f1a5b Compare November 1, 2026 14:56
@jlouvel
jlouvel force-pushed the feat/netty-socks-proxy branch from b9f1a5b to d27f926 Compare November 1, 2026 16:18
Base automatically changed from fix/netty-http2-forward-proxy to fix/netty-non-proxy-hosts November 1, 2026 20:09
@jlouvel
jlouvel added this pull request to stack #1626 November 1, 2026 20:12
@jlouvel
jlouvel force-pushed the feat/netty-socks-proxy branch 2 times, most recently from 200f1b0 to 1711d9c Compare November 1, 2026 21:24
Base automatically changed from fix/netty-non-proxy-hosts to 3.0 November 1, 2026 21:24
jlouvel and others added 3 commits November 1, 2026 16:24
The Netty client only supported HTTP proxies, while some environments
only provide a SOCKS proxy, such as SSH dynamic port forwarding.

- NettyClientHelper: new "socksProxyHost", "socksProxyPort",
  "socksProxyUsername" and "socksProxyPassword" parameters, defaulting to
  the JDK system properties. Requests without HTTP proxy connect through
  Netty's Socks5ProxyHandler, for both HTTP and HTTPS, with origin-form
  request targets; the proxy resolves the host names. The nonProxyHosts
  list applies, and HTTP/3 is not used through the SOCKS proxy.
- SocksProxyTestCase: minimal SOCKS5 test proxy, HTTP and HTTPS requests,
  username/password authentication, refused credentials, bypass and
  system properties.

Closes #1573.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… hosts

- NettyClientCall: the removal of the Proxy-Authorization header of the
  requests that neither go through a CONNECT tunnel nor to a forward proxy
  also covers the requests connected through a SOCKS proxy, so the HTTP
  proxy credentials don't reach the origin server. Document it.
- NettyClientHelper: an empty SOCKS proxy host means no SOCKS proxy,
  instead of a SOCKS proxy on the loopback address.
- SocksProxyTestCase: no proxy credentials received by HTTP and HTTPS
  origins reached through SOCKS, direct connection for an empty host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jlouvel
jlouvel force-pushed the feat/netty-socks-proxy branch from 1711d9c to eaefba7 Compare November 1, 2026 21:24
@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@jlouvel
jlouvel merged commit ccb5693 into 3.0 Nov 1, 2026
3 checks passed
@jlouvel
jlouvel deleted the feat/netty-socks-proxy branch November 1, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants