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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With