JDK-6275099 : URL connections attempt only one IP address for a hostname
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.4.2_04,5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux,solaris_9
  • CPU: x86,sparc
  • Submitted: 2005-05-24
  • Updated: 2011-02-16
  • Resolved: 2009-03-25
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
1.5.0_03

ADDITIONAL OS VERSION INFORMATION :
2.6.8-24.11-default #1 Fri Jan 14 13:01:26 UTC 2005 i686 i686 i386 GNU/Linux
(SuSE 9.2), Mac OS X 10.4 and XP SP 2

A DESCRIPTION OF THE PROBLEM :
When opening a URL where the hostname maps to multiple IP addresses, java.net.URL only attempts to connect to the first IP address.  In contrast, the following web browsers will attempt to connect to the second address when the first one fails:

Safari 2.0 (412), Mac OS X 10.4 (Darwin Kernel 8.0.0)
FireFox 1.0.2, SuSE Linux 9.2
IE 6.0.2900.2180, WinXP SP 2

In my particular situation, I am using this as part of a test framework and I don't have to worry about connecting to virtual servers.  However, this prevents the use of java.net.URL classes for writing a robust, real-world web browser.

The problem is not structural to the format of the URL class, in 
particular the host portion of the URL is always exposed as a string, 
not a java.net.InetAddress.  Thus the underlying IP address is never 
made public.

This suggests a simple fix:  rotate through all possibilites when 
connection.  When opening a URLConnection, there is presumably similar to:

     Socket socket = new Socket(url.getHost(), url.getPort());

It can be replaced with code similar to:

     for (int addrPos = 0; addrPos < 
InetAddress.getAllByName(url.getHost()).length; addrPos++) {
         InetAddress addr = InetAddress.getAllByName(url.getHost())[i];
         try {
             Socket socket = new Socket(url.getHost(), url.getPort());

         }
     catch (IOException ex) {
         if (addrPos == 1-InetAddress.getAllByName(url.getHost()).length) {
             throw ex;
         }
     }
I have reproduced it in Java 1.4 on Mac OS X 10.4 and I just tested it on XP SP 2 (Home Edition) and it works exactly as described.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.  Set up two IP addresses with a single name, e.g.:

vvv.vocalabs.com.       86400   IN      A       192.168.11.120
vvv.vocalabs.com.       86400   IN      A       192.168.11.23

2.  Call InetAddress.getAllByName to see that both hosts show up.

InetAddress.getAllByName("vvv.vocalabs.com")

3.  Open a URL stream

new URL("http://vvv.vocalabs.com:8080/panelist/login.jsp").openStream();

4.  Shut down the server which answered the request.  Note that on the OSes I have tested (OS X 10.4, SuSE Linux 9.2, WinXP SP2), addresses seem to have a preferential order, even though the DNS server is shuffling the order-- this may be a bug in my test environment.

4.  Open the URL stream again;  a "no route to host" or "cannot connect" error should result, depending on how thoroughly disconnected the server.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I tried two configurations, (a) unplugging the server's Ethernet cable; and (b) shutting the server's Tomcat instance down.  In both cases, web browsers (IE, FF, Safari) switched to the secondary server.  I expected Java to do the same.
ACTUAL -
I tried two configurations, (a) via HttpUnit 1.6 and unplugging the server's Ethernet cable; and (b) via Jython and shutting the server's Tomcat instance down.

In once case, I got a "no route to host"; in the second case I got a "Connection refused".

ERROR MESSAGES/STACK TRACES THAT OCCUR :
One stack trace for each configuration:

java.net.NoRouteToHostException: No route to host
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
        at sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1202)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1196)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:885)
        at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:367)
        at com.meterware.httpunit.HttpWebResponse.readResponseHeader(HttpWebResponse.java:162)
        at com.meterware.httpunit.HttpWebResponse.readHeaders(HttpWebResponse.java:200)
        at com.meterware.httpunit.HttpWebResponse.<init>(HttpWebResponse.java:56)
        at com.meterware.httpunit.HttpWebResponse.<init>(HttpWebResponse.java:67)
        at com.meterware.httpunit.WebConversation.newResponse(WebConversation.java:76)
        at com.meterware.httpunit.WebWindow.getResource(WebWindow.java:164)
        at com.meterware.httpunit.WebWindow.getSubframeResponse(WebWindow.java:128)
        at com.meterware.httpunit.WebWindow.getResponse(WebWindow.java:121)
        at com.meterware.httpunit.WebClient.getResponse(WebClient.java:113)
        at com.vocalabs.functional.SurveyRunner.run(SurveyRunner.java:258)
        at java.lang.Thread.run(Thread.java:595)
Caused by: java.net.NoRouteToHostException: No route to host
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
        at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
        at java.net.Socket.connect(Socket.java:507)
        at java.net.Socket.connect(Socket.java:457)
        at sun.net.NetworkClient.doConnect(NetworkClient.java:157)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:365)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:477)
        at sun.net.www.http.HttpClient.<init>(HttpClient.java:214)
        at sun.net.www.http.HttpClient.New(HttpClient.java:287)
        at sun.net.www.http.HttpClient.New(HttpClient.java:299)
        at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:792)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:744)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:669)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:913)
        at sun.net.www.protocol.http.HttpURLConnection.getHeaderFieldKey(HttpURLConnection.java:1919)
        at com.meterware.httpunit.HttpWebResponse.loadHeaders(HttpWebResponse.java:216)
        at com.meterware.httpunit.HttpWebResponse.readHeaders(HttpWebResponse.java:198)
        ... 9 more


java.net.ConnectException: Connection refused
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
        at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
        at java.net.Socket.connect(Socket.java:507)
        at java.net.Socket.connect(Socket.java:457)
        at sun.net.NetworkClient.doConnect(NetworkClient.java:157)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:365)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:477)
        at sun.net.www.http.HttpClient.<init>(HttpClient.java:214)
        at sun.net.www.http.HttpClient.New(HttpClient.java:287)
        at sun.net.www.http.HttpClient.New(HttpClient.java:299)
        at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:792)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:744)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:669)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:913)
        at java.net.URL.openStream(URL.java:1007)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java)
        at org.python.core.PyMethod.__call__(PyMethod.java)
        at org.python.core.PyObject.__call__(PyObject.java)
        at org.python.core.PyInstance.invoke(PyInstance.java)
        at org.python.pycode._pyx6.f$0(<console>:1)
        at org.python.pycode._pyx6.call_function(<console>)
        at org.python.core.PyTableCode.call(PyTableCode.java)
        at org.python.core.PyCode.call(PyCode.java)
        at org.python.core.Py.runCode(Py.java)
        at org.python.core.Py.exec(Py.java)
        at org.python.util.PythonInterpreter.exec(PythonInterpreter.java)
        at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java)
        at org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java)
        at org.python.util.InteractiveInterpreter.runsource(InteractiveInterpreter.java)
        at org.python.util.InteractiveConsole.push(InteractiveConsole.java)
        at org.python.util.InteractiveConsole.interact(InteractiveConsole.java)
        at org.python.util.jython.main(jython.java)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
new java.net.URL(MY_URL_STRING).openStream();
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Rewrite URL to use an IP address found via InetAddress.getAllByName(String).  Note that this will not work for connecting to virtual servers.
###@###.### 2005-05-24 09:50:01 GMT

Comments
EVALUATION Given the availability of InetAddress.isReachable api, this should can be done, e.g. when a connectioon is refused, URLConnection may fall back to connect to the second ip address, etc. Will consider for Mustang. ###@###.### 2005-06-07 09:23:38 GMT
07-06-2005