Example code:
public static void main(String[] args) throws Exception {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1_000)
.setSocketTimeout(5_000)
.setConnectionRequestTimeout(60_000)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.disableAutomaticRetries()
.disableRedirectHandling()
.setDefaultRequestConfig(requestConfig)
.build();
HttpHead httpHead = new HttpHead("http://dns.google/a.mp4");
httpHead.setHeader("Accept-Encoding", "identity");
final long startTime = System.currentTimeMillis();
try (CloseableHttpResponse httpResponse = httpClient.execute(httpHead)) {
final long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println("Success after " + elapsedTime + " ms");
} catch (Exception e) {
final long elapsedTime = System.currentTimeMillis() - startTime;
System.err.println("Error after " + elapsedTime + " ms");
e.printStackTrace();
}
}
This HttpHead
is pointing to a URL that doesn't exist (http://dns.google/a.mp4
in this case), so this request should timeout after 1 second, right? But instead it's taking 2 seconds to timeout:
Error after 2064 ms
java.net.NoRouteToHostException: No route to host
at java.base/sun.nio.ch.Net.connect0(Native Method)
at java.base/sun.nio.ch.Net.connect(Net.java:579)
at java.base/sun.nio.ch.Net.connect(Net.java:568)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:593)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
No matter what I change the ConnectTimeout
to, it always takes 2x that amount of time to timeout. Why???
I'm using HttpClients version 4.5.13
So in the DefaultHttpClientConnectionOperator
class you’ll see this code:
final InetAddress[] addresses = host.getAddress() != null ?
new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
...
for (int i = 0; i < addresses.length; i++) {
...
try {
sock = sf.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
...
} catch (final SocketTimeoutException ex) {
if (last) {
throw new ConnectTimeoutException(ex, host, addresses);
}
} catch (final NoRouteToHostException ex) {
if (last) {
throw ex;
}
}
...
}
When you debug it, you'll see that this is the value of addresses
:
[dns.google/8.8.4.4, dns.google/8.8.8.8, dns.google/2001:4860:4860:0:0:0:0:8888, dns.google/2001:4860:4860:0:0:0:0:8844]
So it’s actually making 4 requests... the first 2 fail with a SocketTimeoutException
after 1 second each, and the second 2 fail with a NoRouteToHostException
pretty much immediately. I don’t know why the last 2 requests fail immediately, but they do.
So the end result is that it takes approximately 2 seconds to make all the requests, and the end result is a NoRouteToHostException