JEP-353 - Socket.connect now throws NoRouteToHostException as against ConnectException previously
Jaikiran Pai
jai.forums2013 at gmail.com
Sat Aug 21 11:40:53 UTC 2021
Hello Alan,
On 21/08/21 2:21 pm, Alan Bateman wrote:
> On 21/08/2021 08:17, Jaikiran Pai wrote:
>> JEP-353[1] which got implemented and released in JDK13, states:
>>
>> "The java.net package defines many sub-classes of SocketException.
>> The new implementation will attempt to throw the same specific
>> SocketException as the old implementation but there may be cases
>> where they are not the same."
>>
>> In one of the projects I watch, a recent issue[2] shows that the
>> "Socket.connect(...)" call, in certain cases, now throws a
>> "java.net.NoRouteToHostException" exception as opposed to
>> "java.net.ConnectException" in previous versions before this change.
>> The "Socket.connect(...)" javadoc states that this API can throw an
>> "IOException", so this change, in theory, is still fine and doesn't
>> break any API contract. However, as noted in [2], certain libraries
>> (Apache HTTP client 4.5.x versions in this case) expect a certain
>> exception type when it's dealing with decision making for HTTP
>> request retries. Due to this change in the exception type being
>> thrown, the Apache HTTP client library now behaves differently in
>> Java 11 and Java 16.
>>
>> Is this change of exception type being thrown intentional? Or is
>> there interest in changing back to the previous exception type to
>> preserve backward compatibility? If not, I think the Apache HTTP
>> client library will have to perhaps do certain changes to have this
>> part of the code behave the same across Java versions.
>
> Thanks for the mail, I haven't seen any other reports on this.
>
> Can you say which operating system and say a bit more about the
> conditions where this is observed?
I was able to reproduce this on a MacOS. However, the continuous
integration setup project for Quarkus projects runs these tests against
Linux and Windows setups and they have run into this issue at least on
the Linux OS jobs (I will need to go and check if Windows jobs had
failed too). I can get the specific OS versions if necessary, but I
don't think that will be needed (due to the reproducer I explain below).
> When connecting to a host that is not reachable then it's possible for
> the underlying connect to fail with a "Connection timed out", "No
> route to host", or other errors.
>
> The reason I'm asking about the OS/conditions is that the old
> implementation did attempt to map specific errors to
> NoRouteToHostException. There's an example stack stack (Windows, with
> JDK 9) in this bug report:
> https://bugs.openjdk.java.net/browse/JDK-8042714
>
> In general, the mapping of connect errors to sub-classes of
> SocketException has always been best effort and I both both
> ConnectException and NoRouteToHostException are possible, all depends
> on the underlying error.
>
> So my initial reaction is that we shouldn't do anything right now, I
> think we need to know a bit more abut the environment/conditions as
> I'm puzzled as to why the HTTP client retry decision didn't run into
> this before with the old implementation.
Now that you mentioned it, I decided to try and replicate this in a
trivial Java program. My initial attempt didn't reproduce this. So I
looked into the Apache HTTP client code and I can now reproduce this
consistenly with the following trivial Java program (pasted at the end
of this mail). What this program does is:
- DNS resolves the IP addresses of "microprofile.io" (this is resolvable)
- For each of the returned IP address, it then constructs a
InetSocketAddress to port 1234. Nothing is listening on this port (on
the remote end), so we do expect the connection attempts to fail.
- It then instantiates a Socket instance out of this InetSocketAddress
and calls connect on the socket instance.
I ran this against Java 11, Java 16, Java 17 and latest upstream OpenJDK
code.
From what I see in the output of this program, the resolution of
microprofile.io returns 4 IP addresses. 2 of them are of type IPv4 and 2
are of type IPv6. Across all Java versions, for IPv4 addresses, the
connection attempts fail with the same "java.net.SocketTimeoutException:
Connect timed out". However, for the IPv6 addresses, in Java 11, the
connection attempts fail with "java.net.ConnectException: No route to
host (connect failed)" whereas in Java 16, 17 and upstream latest, the
connection attempts against the IPv6 addresses fails with
"java.net.NoRouteToHostException: No route to host".
Here's the trivial Java code which reproduces this for me. Let me know
if you need additional details.
import java.net.*;
public class ConnectTest {
public static void main(final String[] args) throws Exception {
final int timeout = 10000;
// our target host that DNS resolves correctly
final String host = "microprofile.io";
// a port where nothing listens on
final int port = 1234;
// DNS resolve by hostname
final InetAddress[] addrs = InetAddress.getAllByName(host);
// try connecting to each IP on port
for (final InetAddress addr : addrs) {
final String ip = addr.getHostAddress();
String type = "";
if (addr instanceof Inet4Address) {
type = "IPv4";
} else if (addr instanceof Inet6Address) {
type = "IPv6";
}
System.out.println("Trying to connect to " + type + " " +
ip + " on port " + port);
try {
new Socket().connect(new InetSocketAddress(addr, port),
timeout);
System.out.println("Connected to " + ip + " port " + port);
} catch (Exception e) {
// we expect it to fail since no one listens on that
port. We are interested
// in the exception type that gets thrown
System.out.println("Connection attempt to " + ip + "
failed with following error:");
e.printStackTrace(System.out);
}
}
}
}
-Jaikiran
More information about the net-dev
mailing list