JDK-7141231 : SOCKS proxy failover using ProxySelector does not work.
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 7
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-01-31
  • Updated: 2012-03-20
Description
FULL PRODUCT VERSION :
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) Client VM (build 22.0-b10, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Windws 7 (Microsoft Windows [Version 6.1.7601])

A DESCRIPTION OF THE PROBLEM :
I have two proxy server.
One is HTTP proxy listening port 8081.
The other is SOCKS proxy listening port 1081.

ProxySelector returns proxy list for fail-over.
The First proxy is dead, The Seond proxy is alive.
When calling URL.getContent() using HTTP proxy list, fail-over was successful.
When calling socket.connect() using SOCK proxy list, fail-over did not work.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. compile sample code.
2. run the sample

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
* Test for Proxy Alive...
Connecting to /10.13.1.218:8080...
	 proxy is dead. (java.net.ConnectException: Connection refused: connect)
Connecting to /10.13.1.218:8081...
	 proxy is alive.
Connecting to /10.13.1.218:1080...
	 proxy is dead. (java.net.ConnectException: Connection refused: connect)
Connecting to /10.13.1.218:1081...
	 proxy is alive.

* Test for HTTP Proxy...
http://www.oracle.com/index.html, /10.13.1.218:8080 java.net.ConnectException: Connection refused: connect
Success

* Test for SOCKS Proxy...
socket://www.oracle.com:80, /10.13.1.218:1080 java.net.ConnectException: Connection refused: connect
Success

ACTUAL -
* Test for Proxy Alive...
Connecting to /10.13.1.218:8080...
	 proxy is dead. (java.net.ConnectException: Connection refused: connect)
Connecting to /10.13.1.218:8081...
	 proxy is alive.
Connecting to /10.13.1.218:1080...
	 proxy is dead. (java.net.ConnectException: Connection refused: connect)
Connecting to /10.13.1.218:1081...
	 proxy is alive.

* Test for HTTP Proxy...
http://www.oracle.com/index.html, /10.13.1.218:8080 java.net.ConnectException: Connection refused: connect
Success

* Test for SOCKS Proxy...
socket://www.oracle.com:80, /10.13.1.218:1080 java.net.ConnectException: Connection refused: connect
socket://www.oracle.com:80, /10.13.1.218:1081 java.net.SocketException: Socket closed
java.net.SocketException: Can't connect to SOCKS proxy:Socket closed
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at dmlim.net.SocksProxyBugTest.testSocks(SocksProxyBugTest.java:118)
	at dmlim.net.SocksProxyBugTest.main(SocksProxyBugTest.java:167)


ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: Can't connect to SOCKS proxy:Socket closed
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at dmlim.net.SocksProxyBugTest.testSocks(SocksProxyBugTest.java:118)
	at dmlim.net.SocksProxyBugTest.main(SocksProxyBugTest.java:167)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package dmlim.net;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

public class SocksProxyBugTest
{
	static InetSocketAddress DEAD__HTTP__PROXY_ADDR = new InetSocketAddress("10.13.1.218", 8080);
	static InetSocketAddress ALIVE_HTTP__PROXY_ADDR = new InetSocketAddress("10.13.1.218", 8081);
	
	static InetSocketAddress DEAD__SOCKS_PROXY_ADDR = new InetSocketAddress("10.13.1.218", 1080);
	static InetSocketAddress ALIVE_SOCKS_PROXY_ADDR = new InetSocketAddress("10.13.1.218", 1081);

	static LinkedList<Proxy> HTTP__PROXY_LIST = new LinkedList<Proxy>();
	static LinkedList<Proxy> SOCKS_PROXY_LIST = new LinkedList<Proxy>();
	
	static
	{
		HTTP__PROXY_LIST.add(new Proxy(Proxy.Type.HTTP,  DEAD__HTTP__PROXY_ADDR));
		HTTP__PROXY_LIST.add(new Proxy(Proxy.Type.HTTP,  ALIVE_HTTP__PROXY_ADDR));
		
		SOCKS_PROXY_LIST.add(new Proxy(Proxy.Type.SOCKS, DEAD__SOCKS_PROXY_ADDR));
		SOCKS_PROXY_LIST.add(new Proxy(Proxy.Type.SOCKS, ALIVE_SOCKS_PROXY_ADDR));
		
	}

	static class MyProxySelector extends ProxySelector
	{
		@Override
		public void connectFailed(URI uri, SocketAddress addr, IOException e)
		{
			System.err.println(uri + ", " + addr + " " + e);
		}

		@Override
		public List<Proxy> select(URI uri)
		{
			if ( uri.getScheme().equals("socket") )
				return SOCKS_PROXY_LIST;
			else
				return HTTP__PROXY_LIST;
		}
	}
	
	public static void testProxyAlive()
	{
		InetSocketAddress[] addrs = {
			DEAD__HTTP__PROXY_ADDR,	ALIVE_HTTP__PROXY_ADDR, DEAD__SOCKS_PROXY_ADDR, ALIVE_SOCKS_PROXY_ADDR
		};
		
		for ( InetSocketAddress addr : addrs )
		{
			System.err.println("Connecting to " + addr + "...");
			Socket sock = null;
			try
			{
				sock = new Socket(Proxy.NO_PROXY);
				sock.connect(addr);
				System.err.println("\t proxy is alive.");
			}
			catch (Exception e)
			{
				System.err.println("\t proxy is dead. (" + e + ")");
			}
			finally
			{
				if ( sock != null ) try { sock.close(); } catch (Exception e) {}
			}
		}
	}
	
	public static void testHttp() throws IOException
	{
		URL url = new URL("http://www.oracle.com/index.html");
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		InputStream is = null;
		
		try
		{
			is = (InputStream)url.getContent();
			byte[] buf = new byte[1024];
			for ( int nRead = 0 ; (nRead = is.read(buf)) != -1 ; )
			{
				os.write(buf, 0, nRead);
			}
		}
		finally
		{
			if ( is != null )
				is.close();
		}

		FileOutputStream fos = new FileOutputStream("index.http.html");
		fos.write(os.toByteArray());
		fos.close();
	}
	
	public static void testSocks() throws IOException
	{
		Socket sock = null;
		InputStream is = null;
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		
		try
		{
			sock = new Socket();
			sock.connect(new InetSocketAddress("www.oracle.com", 80));
			sock.getOutputStream().write("GET /index.html HTTP/1.0\r\nHost: www.oracle.com\r\nUser-Agent: Mozilla/1.0\r\n\r\n".getBytes());
			sock.getOutputStream().flush();
			is = sock.getInputStream();
			byte[] buf = new byte[1024];
			for ( int nRead = 0 ; (nRead = is.read(buf)) != -1 ; )
			{
				os.write(buf, 0, nRead);
			}
		}
		finally
		{
			if ( is != null )
				is.close();
			if ( sock != null )
				sock.close();
		}
		
		FileOutputStream fos = new FileOutputStream("index.socks.html");
		fos.write(os.toByteArray());
		fos.close();
	}
	
	public static void main(String[] args)
	{
		System.err.println("* Test for Proxy Alive...");

		testProxyAlive();
		
		System.err.println();

		ProxySelector.setDefault(new MyProxySelector());
		
		try
		{
			System.err.println("* Test for HTTP Proxy...");
			testHttp();
			System.err.println("Success");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		System.err.println();
		
		try
		{
			System.err.println("* Test for SOCKS Proxy...");
			testSocks();
			System.err.println("Success");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

---------- END SOURCE ----------

SUPPORT :
YES

Comments
EVALUATION Hmmm... I don't believe SOCKS proxy failover ever worked for java.net.Socket. In which case this could be considered a low priority RFE.
19-03-2012