1

NOTE: This is not a duplicate of https proxy with JDK11 client, which is about a HTTP proxy. My question is about a socks proxy, which requires a different solution. Neither is it a duplicate of How can I use HttpClient-4.5 against a SOCKS proxy?, which is about the Apache HttpClient instead of the JDK HttpClient.

How can I use a socks 5 proxy with java.net.http.HttpClient?

I tried the code below, but it results in a following exception:

Exception in thread "main" java.io.IOException: HTTP/1.1 header parser received no bytes
    at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:586)

Code:

import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.List;

public class ProxyApp {
    public static void main(String[] args) throws Exception {
        run1();
    }

    public static void run1() throws Exception {
        HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .proxy(ProxySelector.of(new InetSocketAddress("localhost", 8181)))
                .build();

        HttpRequest request = HttpRequest
                .newBuilder(new URI("http://example.org/"))
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        String verificationText = "Example Domain";
        // Should print "true" otherwise the request failed
        System.out.println(response.body().contains(verificationText));
    }
}
Devabc
  • 4,782
  • 5
  • 27
  • 40

1 Answers1

3

Problem 1: JDK HttpClient doesn't support socks proxies

JDK HttpClient doesn't support socks proxies, only HTTP(S) proxies, even though types like java.net.Proxy.Type.SOCKS do exist. This problem isn't documented at all in the Java JDK, which causes Java to silently disregard the socks proxy when you try to use it, by using a non-proxy connection.

This problem is reported as a bug in 2018 to OpenJDK, but hasn't been fixed yet: https://bugs.openjdk.java.net/browse/JDK-8214516 The code that is responsible for this silent disregard is: https://github.com/openjdk/jdk/blob/29e552c03a2825f9526330072668a1d63ac68fd4/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java#L299

    private static Proxy retrieveProxy(ProxySelector ps, URI uri) {
        Proxy proxy = null;
        List<Proxy> pl = ps.select(uri);
        if (!pl.isEmpty()) {
            Proxy p = pl.get(0);
            if (p.type() == Proxy.Type.HTTP)
                proxy = p;
        }
        return proxy;
    }

So if the ProxySelector provides a Proxy with type Proxy.Type.SOCKS as first proxy, then it is ignored, and null is returned, which causes HttpRequestImpl to not use a proxy at all.

Problem 2: Unclear API

Even if the missing JDK implementation problem would be solved, then there is also an API problem in unclarity:

The unclarity problem is caused by java.net.ProxySelector.of(InetSocketAddress), which doesn't document what kind of proxy it creates. A proxy can either be a web (http) proxy or a socks proxy. The source code of ProxySelector.of() shows that it creates a private StaticProxySelector object, which creates a web proxy (Proxy.Type.HTTP) instead of a socks proxy (Proxy.Type.SOCKS).

So the solution (if the JDK internal code would be fixed) is to create your own ProxySelector. This is shown in the solution below.

import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.List;

public class ProxyApp {
    public static void main(String[] args) throws Exception {
        run2();
    }

    public static void run2() throws Exception {
        ProxySelector proxySelector = new ProxySelector() {
            private List<Proxy> proxies;

            {
                Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("localhost", 8181));
                proxies = Collections.singletonList(proxy);
            }

            @Override
            public List<Proxy> select(URI uri) {
                return proxies;
            }

            @Override
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
                throw new RuntimeException(ioe);
            }
        };

        HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .proxy(proxySelector)
                .build();

        HttpRequest request = HttpRequest
                .newBuilder(new URI("http://example.org/"))
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        String verificationText = "Example Domain";
        // Should print "true" otherwise the request failed
        System.out.println(response.body().contains(verificationText));
    }
}
Devabc
  • 4,782
  • 5
  • 27
  • 40