Java HttpRequest timeout unexpectedly throwing HttpConnectTimeoutException - java

Using the new java.net.http package released with JDK 11, an HttpRequest has been assembled with a deliberately low response timeout:
HttpRequest.Builder builder = HttpRequest.newBuilder(getEndpointUri());
addRequestHeaders(builder);
builder.POST(HttpRequest.BodyPublishers.ofString(rawXml));
builder.timeout(Duration.ofMillis(1));
HttpRequest httpRequest = builder.build();
The aim is to test that HttpTimeoutException outcomes are handled correctly, but unexpectedly this response timeout value is leading to an HttpConnectionTimeoutException, which is being caught by this code:
try {
HttpResponse<InputStream> httpResponse = completableExchange.join();
} catch (CompletionException ce) {
if (ce.getCause() instanceof HttpConnectTimeoutException) {
System.out.println("Connection timeout occurred!");
} else {
throw ce;
}
}
This means that a response timeout is causing the code to act as though a connection timeout has occurred. To the best of my understanding, the connection timeout and response timeout should be separate concepts, which should be possible to catch and handle separately.
The stack trace attached to the HttpConnectionTimeoutException looks like this:
java.net.http.HttpConnectTimeoutException: HTTP connect timed out
at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:68)
at java.net.http/jdk.internal.net.http.HttpClientImpl.purgeTimeoutsAndReturnNextDeadline(HttpClientImpl.java:1248)
at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:877)
Caused by: java.net.ConnectException: HTTP connect timed out
at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:69)
... 2 more
Am I misunderstanding the timeout concepts? Does the HttpRequest timeout value simply provide an alternative to the default of the HttpClient timeout value? Is there a reliable way to catch connection and response timeout as distinct events?
For what it's worth, the Javadoc for HttpRequest.Builder.timeout(Duration) says the following:
Sets a timeout for this request. If the response is not received within the specified timeout then an HttpTimeoutException is thrown from HttpClient::send or HttpClient::sendAsync completes exceptionally with an HttpTimeoutException. The effect of not setting a timeout is the same as setting an infinite Duration, ie. block forever.
To make things confusing, HttpConnectionTimeoutException is a subclass of HttpTimeoutException so technically the contract of the timeout(Duration) method is being satisfied. But this seems unhelpful.
(Before you ask: yes, the value passed to HttpRequest.Builder.timeout(Duration) is the deciding factor in whether or not an exception is thrown. So the exception is not based on the connection timeout value being used to create the HttpClient instance.)

IIRC you will get a HttpConnectionTimeoutException if the connection is not connected at the time the timeout is raised, or if the connect timeout is raised before the connection has finished connecting.
When sending a request - the underlying connection might already be connected or not - depending on whether a suitable existing connection was found in the pool. The request timeout starts immediately - independently of the state of the underlying connection. If the underlying connection is not connected yet, and the request timeout expires before it gets connected, then you will get a HttpConnectionTimeoutException because the connection could not be connected within the time allocated for the response to the request to be delivered. You could see it as the request timeout clipping the connect timeout.
Do you have any specific use case in mind for distinguishing the two cases:
HttpConnectionTimeoutException is raised because the connection could not be connected within the time specified by the connection timeout,
HttpConnectionTimeoutException is raised because the request timeout expired before the connection could be connected?

Related

Getting exception while connecting a server through SSH

I am trying to connect to an external server from my Solaris server using SSH in my Java application. Some how we are getting exception while authenticating the user but after 60min. How can we decrease the timing to get the exception to 5min or so.
There was a problem while connecting to IP:PORT
java.io.IOException: There was a problem while connecting to IP:PORT
The time difference between time2 and time3 are around 60min. I want to decrease this time.
Please find below the code snippet that we are using.
try
{
timeout = 1;
if (connection == null)
{
//time1
connection = new ch.ethz.ssh2.Connection(getIpAddress(), Integer.parseInt(getPort()));
if (connection == null)
{
System.out.println("connection is null");
}
else
{
//time2
connection.connect(null, timeout, 0);
}
}
}
catch (Exception t)
{
//time3
System.out.println(t.getLocalizedMessage());
System.out.println(t.toString());
}
Edit1: After checking SSH related configuration files I found KeyRegenerationInterval having a value of 3600s. Is this useful to resolve this issue. What might be the outcome if I decrease its value to some 30min or 5min.
Let me summarize your problem, please correct me if something wrong.
You can not connect to SSH server.
An exception raised after 60 mins.
According to below Throws section of method connect(ServerHostKeyVerifier, int, int), I guess you might be facing issue due to a buggy proxy which doesn't return proper HTTP response. Using direct internet connection to see whether the issue is gone.
Throws:
java.io.IOException - If any problem occurs, e.g., the server's host key is not accepted by the verifier or there is problem during
the initial crypto setup (e.g., the signature sent by the server is
wrong).
In case of a timeout (either connectTimeout or kexTimeout) a SocketTimeoutException is thrown.
An exception may also be thrown if the connection was already successfully connected (no matter if the connection broke in the mean
time) and you invoke connect() again without having called close()
first.
If a HTTP proxy is being used and the proxy refuses the connection, then a HTTPProxyException may be thrown, which contains
the details returned by the proxy. If the proxy is buggy and does not
return a proper HTTP response, then a normal IOException is thrown
instead.
Try setting the "kexTimeout" (Timeout for complete connection establishment (non-negative, in milliseconds). Zero means no timeout.) to non-zero
connection.connect(null, timeout, timeout);
Also, in your catch print the full stacktrace to verify where the timeout occurs
catch (Exception t) {
//time3
t.printStackTrace();
}

Spymemcached and Connection Failures

I though Spymemcached does attempt to reestablish connection to the server when this connection get lost.
But I am seeing something different; wondering what I misunderstand or what I do wrong. Here is some sample code:
MemcachedClient c=new MemcachedClient(AddrUtil.getAddresses("server:11211"));
while(true)
try {
System.out.println(c.get("hatsts"));
Thread.sleep(10000);
} catch(Exception e) {
e.printStackTrace();
}
It runs initially without problem. Then I pull the network plug. Subsequently, the client detects a network failure and throws following exception:
net.spy.memcached.OperationTimeoutException: Timeout waiting for value
But then, when i re-establish the network, the client does not recover and continues throwing the exception; even after 5 min. I tried SpyMemcached 2.10.6 and 2.9.0.
What am I missing?
The problem here is that because you pulled the network cable the tcp socket on you client still thinks the connection is valid. The tcp keepalive varies from operating system to operating system and can be as high as 30 minutes. As a result the application (in this case Spymemcached) is not notified that the connection is no longer valid by the tcp layer and therefore Spymemcached doesn't try to reconnect.
The way Spymemcached detects this situation is by counting the amount of consecutive operation timeouts. The last time I checked the default value was 99. Once this many ops time out then Spymemcached will reconnect. You can change this value in the ConnectionFactory if you want to set it to some other value. There's a function called getContinuousTimeout() which is where the Spymemcached gets 99 from by default. You can construct your own ConnectionFactory with the ConnectionFactoryBuilder.
Hopefully this is enough information to answer your question and get you going in the right direction. If not let me know and I can add some more details.

How to understand SocketTimeout for http put?

From the javadoc of java.net.Socket#setSoTimeout, it says:
Enable/disable SO_TIMEOUT with the specified timeout, in
milliseconds. With this option set to a non-zero timeout,
a read() call on the InputStream associated with this Socket
will block for only this amount of time. If the timeout expires,
a <B>java.net.SocketTimeoutException</B> is raised, though the
Socket is still valid.
For a http put operation, the client may upload a huge file, that the client is always writing, and never reading data from server.
In this case, if I set the SocketTimeout for the http client, will it throw TimeoutException during uploading?
No it won't. It may throw a timeout exception when reading the response code however. If the peer closes the connection during the upload, it will throw an IOException 'connection reset by peer'.

Java: Connection Timedout and Socket Timedout

I'm in the process of writing two Junit testcases for testing the timemouts
#Test
public void connectionTimeoutTest()
{
String myurl = "http://serverip:serverport/context";
URL url = new URL(myurl);
URLConnection con = url.openConnection();
//how to check connectionTimeout but not socket time out
//what I think
//is it good enough to check with a server ip that does not exist ?
}
#Test
public void socketTimeoutTest()
{
String myurl = "http://localhost:serverport/context";
URL url = new URL(myurl);
URLConnection con = url.openConnection();
//how to check successful connection and timesout at socket (or port)
//what I think
//IP should exist (so it is localhost) but should not be listening on the port
}
I'm confused here because I'm not sure if my approach is right. What are the exceptions in each case ? Also, is it possible to differentiate based on Exceptions ?
Are there any other time outs that I have missed ?
Thanks in advance
You need to call URLConnection.setConnectTimeout() before opening any streams or getting the response code. It will throw a ConnectException with the text 'connection timed out' if a connect timeout happens.
Note that, contrary to the Javadoc, the default connect timeout is about a minute (platform-dependent), not infinity, and that you can decrease the default connection timeout this way, but not increase it.
For a read timeout, which I assume is what you mean by 'socket timeout', you have to call URLConnection.setReadTimeout(). If it fires, you will get a SocketTimeoutException. In this case zero does mean infinity as per the Javadoc.
I am not too clear on your unit test requirements. If you are to test the connection (and socket read) timeout values of the server, the approach mentioned above will only allow you to validate a certain "maximum" threshold value. But you still won't be able to confirm TCP server side timeout settings. The TCP server's connection (and read) timeouts depend upon factors such as platform (OS) and TCP stack implementation.
Again, the timeout setting via setConnectTimeout or setReadTimeout() are only on the client socket, when communicating with the resource referenced by this URLConnection.
In the first test case, I think it will get java.net.UnknownHostException, because of non-exist ip address.
If you wanna cause a socket connect timeout exception (exactly get messages like "java.net.SocketTimeoutException: connect timed out") , just use URLConnection.setConnectTimeout(10), set the parameter as smaller as possible ,but positive;
The same in the second test case, you can use URLConnection.setReadTimeout(10) to cause a read timeout exception ("java.net.SocketTimeoutException: Read timed out").

Forcing socket.connect to wait a specific time before it decides a connection is unavailable

I'm issuing a socket connection, using the following snippet
Socket socket = new Socket();
InetSocketAddress endPoint = new InetSocketAddress("localhost", 1234);
try
{
socket.connect(endPoint, 30000);
}
catch (IOException e)
{
e.printStackTrace();
// Logging
}
The endpoint it is trying to connect to is offline, what I want it to do is to attempt to connect, and using the 30000ms timeout, wait for that period of time before it concludes a result
Currently, that 30000 parameter doesn't seem to be applied, as from the timestamp on my logging it appears that it is determining within 1 second that a connection failed.
How can I force the connect to wait for a set amount of time before giving up?
13:13:57,685 6235 DEBUG [Thread-7] - Unable to connect to [localhost:1234]
13:13:58,685 7235 DEBUG [Thread-7] - Unable to connect to [localhost:1234]
13:13:59,695 8245 DEBUG [Thread-7] - Unable to connect to [localhost:1234]
13:14:00,695 9245 DEBUG [Thread-7] - Unable to connect to [localhost:1234]
EDIT : The API does state Connects this socket to the server with a specified timeout value. A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs. however it appears I'm not experiencing such behaviour, or am not catering to it, most likely the latter
What you're getting here is correct. connect won't sit on a socket waiting until it sees a server, it will attempt to connect and wait for a response. if there is nothing to connect to, it returns. if there is something to connect to, it will wait timeout seconds for a response and fail if none is received.
You need to distinguish among several possible exception conditions.
ConnectException with the text 'connection refused', which means the host was up and reachable and nothing was listening at the port. This happens very quickly and cannot be subjected to a timeout.
NoRouteToHostException: this indicates a connectivity issue. Again it happens immediately and cannot be subjected to a timeout.
UnknownHostException: the host names cannot be resolved via DNS. This happens immediately, or rather after a generally short DNS delay, and cannot be subjected to a timeout.
ConnectException with any other text: this can indicate a failure to respond by the target system. Usually happens when firewalls are present. Can be subjected to a timeout.
You are doing the correct thing by calling Socket.connect() with a timeout parameter. If you don't do this, or if you specify a zero timeout, the default system timeout is used, which is of the order of 60-75 seconds depending on the platform. This is contrary to the Javadoc's statement about an 'infinite timeout', which is not correct. Also you cannot increase the timeout beyond this limit via Socket.connect() witha a timeout parameter. Alternatively you can use java.nio socket channels in non-blocking mode with a select() to administer the timeout for you, but you still can't increase the timeout beyond the platform default via this or any other method.
When the timeout occurs, a SocketTimeoutException exception is thrown which you do not catch and log. The IOException is fired when "an error occurs during the connection". The timeout is never applied because there's an error beforehand.
Edit: Just to clarify: TCP/IP as a suite has many specifics that could prevent a packet from reaching it's desired outcome (a SYN/ACK packet). If a computer responds to your SYN packet by an informing your application that the port is closed (i.e. there's no application running/listening there), it would fire an exception telling you that it is impossible to connect to that port. If you wish to send and re-send SYN packets either way with the knowledge that an application will come online listening on that port, this is done on a different network layer (and, as far as I know, is not accessible with Java out-of-the-box).
Try scocket.setSoTimeout(timeout) before connecting.

Categories