I am experimenting with Proxies in java networking. I have read the documentation regarding them and am currently testing ProxySelector.
I notice 2 types of behaviour of this ProxySelector when using with HttpURLConnection & when using with Socket class
When using with HttpUrlConnection with this code
class CustomSelector extends ProxySelector { @Override public List<Proxy> select(URI uri) { System.out.println("Selecting"); System.out.println("==============="); return List.of ( new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000)) ,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000)) ,Proxy.NO_PROXY ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { System.out.println("Failed:"+uri); System.out.println("Address:"+sa); System.out.println("Exception:"+sa); System.out.println("========================="); } } public static void main(String[] args)throws Exception { ProxySelector.setDefault(new CustomSelector()); HttpURLConnection con=(HttpURLConnection)new URL("http://192.168.1.2:2000/Test") .openConnection(); System.out.println(con.getResponseMessage()); con.disconnect(); }
I get the expected output
Selecting =============== Failed:http://192.168.1.2:2000/Test Address:localhost/127.0.0.1:5000 Exception:localhost/127.0.0.1:5000 ========================= Failed:http://192.168.1.2:2000/Test Address:localhost/127.0.0.1:8000 Exception:localhost/127.0.0.1:8000 ========================= Not-Implemented
This makes sense because ports 5000 & 8000 are just dummy ports with no server running on it hence connection fails on them and it finally goes to NO_PROXY which directly connects to my custom HttpServer running on port 2000 which returns not implemented for everything
Now i work with Sockets using the same procedure . Again i have verified that my server is running on port 2000
class CustomSelector extends ProxySelector { @Override public List<Proxy> select(URI uri) { System.out.println("Selecting"); System.out.println("==============="); return List.of ( new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",5000)) ,new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",8000)) ,Proxy.NO_PROXY ); } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { System.out.println("Failed:"+uri); System.out.println("Address:"+sa); System.out.println("Exception:"+sa); System.out.println("========================="); } } public static void main(String[] args)throws Exception { ProxySelector.setDefault(new CustomSelector()); try(Socket client=new Socket()) { System.out.println("Connecting"); client.connect(new InetSocketAddress(InetAddress.getLocalHost(),2000)); System.out.println("Connected"); } }
I get this output
Connecting Selecting =============== Failed:socket://DESKTOP-1N0I046:2000 Address:localhost/127.0.0.1:5000 Exception:localhost/127.0.0.1:5000 ========================= Failed:socket://DESKTOP-1N0I046:2000 Address:localhost/127.0.0.1:8000 Exception:localhost/127.0.0.1:8000 ========================= Exception in thread "main" java.net.SocketException: Socket closed at java.base/sun.nio.ch.NioSocketImpl.beginConnect(NioSocketImpl.java:498) at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:580) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) at java.base/java.net.Socket.connect(Socket.java:633) at java.base/java.net.Socket.connect(Socket.java:583) at n_networking.proxy.TCPClient.main(TCPClient.java:237)
This should not happen as the last option in the Proxy List is NO_PROXY which means direct connection without any proxy which should succeed but it seems that the ProxySelector never uses that last option in the list
Whats more bizarre is that if i change my ProxyType from SOCKS to HTTP as follows. I know it doesn’t make sense in this context but this is just for testing purposes
@Override public List<Proxy> select(URI uri) { System.out.println("Selecting"); System.out.println("==============="); return List.of ( new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000)) ,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000)) ,Proxy.NO_PROXY ); }
Then everything works
Output :
Connecting Selecting =============== Connected
For some reason it skips all HTTP proxy types and not even test then.
I have used Sockets with Proxy.HTTP and it works perfectly as it issues an CONNECT command first before sending data.
Here is my dummy server i use for both these test cases
public static void main(String[] args)throws Exception { try(ServerSocket server=new ServerSocket(2000,0,InetAddress.getLocalHost())) { System.out.println("Main Server Started"); try(Socket socket=server.accept()) { System.out.println("Accepted"); socket.getOutputStream().write("HTTP/1.1 501 Not-Implementedrnrn".getBytes()); socket.getOutputStream().flush(); } } }
Why these differences? I am using jdk 17.0.2 with windows 10
Advertisement
Answer
This seems to be a bug in the JDK: JDK-7141231
Despite java.net.SocksSocketImpl
in theory supporting proxy failover; in reality this does apparently not work because after the first failed connection attempt the socket is closed, but that same closed socket is used for any subsequent connection attemps, which therefore fail with “Socket closed” (which you are seeing).
The reason why changing the proxy type to HTTP “works” is because it performs a direct connection, ignoring all other specified proxies.