[
https://issues.apache.org/jira/browse/HTTPCLIENT-2386?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18032369#comment-18032369
]
Jens Popp edited comment on HTTPCLIENT-2386 at 10/24/25 10:20 AM:
------------------------------------------------------------------
{*}Update{*}:
I found a bug in my code, that explains the behavior. So the TlsConfig is not
necessary when connectionTimout in ConnectionConfig is set correctly! However
the change still affected the default behavior, if the connectionTimout is
default but the soTimeout is set in the SocketConfig.
----
This issue caused a change in behavior in a minor build that was difficult to
detect. Basically I have connectTimout and requestTimeout. I want to have a
fast fail if the connection including tls handshake cannot be established, but
the request can take very long. In 5.5 this worked with some settings as
described below. The commit and 5.5.1 cause a change in behavior that the tls
handshake failure is no longer covered and the request timeout is used in 5.5.1
Old code worked in 5.5:
{code:java}
final TlsSocketStrategy tlsSocketStrategey = sslContext != null
? new DefaultClientTlsStrategy(sslContext, verifier)
: null;
final PoolingHttpClientConnectionManagerBuilder
connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create().setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
.setConnPoolPolicy(PoolReusePolicy.LIFO).setTlsSocketStrategy(tlsSocketStrategey);
final ConnectionConfig.Builder connConfig =
ConnectionConfig.custom();
if (connectTimeout != null) {
connectionManagerBuilder
.setDefaultConnectionConfig(
ConnectionConfig.custom().setConnectTimeout(Timeout.of(connectTimeout))
.setSocketTimeout(Timeout.of(connectTimeout)).build())
.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Timeout.of(connectTimeout)).build())
.build();
}
// Request Timeout is set in interceptor
... {code}
New code required from 5.5.1
{code:java}
final TlsSocketStrategy tlsSocketStrategey = sslContext != null
? new DefaultClientTlsStrategy(sslContext, verifier)
: null;
// New fix due to changes in 5.5.1 ;
TlsConfig tlsConfig = sslContext != null
?
TlsConfig.custom().setHandshakeTimeout(Timeout.of(connectTimeout)) // TLS
handshake specific
.build()
: null;
final PoolingHttpClientConnectionManagerBuilder
connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create().setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
.setConnPoolPolicy(PoolReusePolicy.LIFO).setTlsSocketStrategy(tlsSocketStrategey)
.setDefaultTlsConfig(tlsConfig);
... same as above{code}
was (Author: JIRAUSER311288):
{*}Update{*}:
I found a bug in my code, that explains the behavior. So the TlsConfig is not
necessary when connectionTimout in ConnectionConfig is set correctly! However
the change still affected the default behavior, if the connectionTimout is
default but the soTimeout is set in the SocketConfig.
This issue caused a change in behavior in a minor build that was difficult to
detect. Basically I have connectTimout and requestTimeout. I want to have a
fast fail if the connection including tls handshake cannot be established, but
the request can take very long. In 5.5 this worked with some settings as
described below. The commit and 5.5.1 cause a change in behavior that the tls
handshake failure is no longer covered and the request timeout is used in 5.5.1
Old code worked in 5.5:
{code:java}
final TlsSocketStrategy tlsSocketStrategey = sslContext != null
? new DefaultClientTlsStrategy(sslContext, verifier)
: null;
final PoolingHttpClientConnectionManagerBuilder
connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create().setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
.setConnPoolPolicy(PoolReusePolicy.LIFO).setTlsSocketStrategy(tlsSocketStrategey);
final ConnectionConfig.Builder connConfig =
ConnectionConfig.custom();
if (connectTimeout != null) {
connectionManagerBuilder
.setDefaultConnectionConfig(
ConnectionConfig.custom().setConnectTimeout(Timeout.of(connectTimeout))
.setSocketTimeout(Timeout.of(connectTimeout)).build())
.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Timeout.of(connectTimeout)).build())
.build();
}
// Request Timeout is set in interceptor
... {code}
New code required from 5.5.1
{code:java}
final TlsSocketStrategy tlsSocketStrategey = sslContext != null
? new DefaultClientTlsStrategy(sslContext, verifier)
: null;
// New fix due to changes in 5.5.1 ;
TlsConfig tlsConfig = sslContext != null
?
TlsConfig.custom().setHandshakeTimeout(Timeout.of(connectTimeout)) // TLS
handshake specific
.build()
: null;
final PoolingHttpClientConnectionManagerBuilder
connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create().setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
.setConnPoolPolicy(PoolReusePolicy.LIFO).setTlsSocketStrategy(tlsSocketStrategey)
.setDefaultTlsConfig(tlsConfig);
... same as above{code}
> httpClient5 socketTimeoutException in dataChannel with connectTimeout value
> ---------------------------------------------------------------------------
>
> Key: HTTPCLIENT-2386
> URL: https://issues.apache.org/jira/browse/HTTPCLIENT-2386
> Project: HttpComponents HttpClient
> Issue Type: Improvement
> Components: HttpClient (async)
> Affects Versions: 5.4.4
> Environment: httpclient5 version: 5.4.4
> httpCore5 version: 5.3.4
> Java version: 17 (21 since recently)
> Reporter: Cenk Pekyaman
> Priority: Minor
> Fix For: 5.5.1, 5.6-alpha1
>
> Time Spent: 5h
> Remaining Estimate: 0h
>
> We are using http-client-5 under our api-client layer with async + TLS. Once
> in a while, we get a timeout exception like this:
> "{*}java.net.SocketTimeoutException: 2 SECONDS{*}" in some requests. and from
> the stack trace, we see that the exception is coming from
> "InternalDataChannel.onTimeout". but the value we see is actually the
> {*}connectTimeout{*}, not the readTimeout({*}socketTimeout{*}) we set for the
> request.
> we specify our *connectTimeout* as *2 seconds* in
> *PoolingAsyncClientConnectionManagerBuilder* during client creation. we
> specify our *socketTimeout* as *10 seconds* in *RequestConfig* of
> *HttpClientContext* during request execution.
>
> this is how we make the call (a bit simplified):
> {{// the last parameter is our class implementing FutureCallback}}
> {{Future<Message<HttpResponse, RES>> clientFuture =
> asyncHttpClient.execute(asyncRequestProducer, responseConsumer, null,
> httpClientContext, this);}}
> {{// callTimeout is essentially connectTimeout + socketTimeout by default.}}
> {{return clientFuture.get(callTimeout, TimeUnit.MILLISECONDS);}}
>
> We suspect that the client completes the initial connect step with
> "InternalConnectChannel" and then the socket is handed over to
> "InternalDataChannel" to do the TLS handshake in "{*}startTls{*}", which
> either takes too long to complete, or hangs. Since we don't specify
> handshakeTimeout in our tlsconfig, *DefaultAsyncClientConnectionOperator*
> seems to be using *connectTimeout* for this stage (in the callback called
> after connection is established ?).
>
> Code flow is essentially correct but getting a generic socketTimeoutException
> with the value of connectTimeout seems a bit confusing, as we normally expect
> some ConnectTimeoutException if we really have a connection timeout, or the
> other way around.
>
> This is an example stack trace for such a case:
> {{ at org.apache.hc.core5.concurrent.BasicFuture.failed(BasicFuture.java:166)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.ComplexFuture.failed(ComplexFuture.java:79)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.client5.http.impl.async.InternalAbstractHttpAsyncClient$2.failed(InternalAbstractHttpAsyncClient.java:364)
> [httpclient5-5.4.4.jar:5.4.4] at
> org.apache.hc.client5.http.impl.async.AsyncRedirectExec$1.failed(AsyncRedirectExec.java:246)
> [httpclient5-5.4.4.jar:5.4.4] at
> org.apache.hc.client5.http.impl.async.AsyncProtocolExec$1.failed(AsyncProtocolExec.java:295)
> [httpclient5-5.4.4.jar:5.4.4] at
> org.apache.hc.client5.http.impl.async.AsyncConnectExec$2.failed(AsyncConnectExec.java:233)
> [httpclient5-5.4.4.jar:5.4.4] at
> org.apache.hc.core5.concurrent.CallbackContribution.failed(CallbackContribution.java:52)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.BasicFuture.failed(BasicFuture.java:166)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.ComplexFuture.failed(ComplexFuture.java:79)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager$4.failed(PoolingAsyncClientConnectionManager.java:482)
> [httpclient5-5.4.4.jar:5.4.4] at
> org.apache.hc.core5.concurrent.BasicFuture.failed(BasicFuture.java:166)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.ComplexFuture.failed(ComplexFuture.java:79)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.FutureContribution.failed(FutureContribution.java:52)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.concurrent.CallbackContribution.failed(CallbackContribution.java:52)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.ssl.SSLIOSession$1.exception(SSLIOSession.java:233)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.ssl.SSLIOSession$1.timeout(SSLIOSession.java:223)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.InternalDataChannel.onTimeout(InternalDataChannel.java:170)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.InternalChannel.checkTimeout(InternalChannel.java:67)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.SingleCoreIOReactor.checkTimeout(SingleCoreIOReactor.java:239)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.SingleCoreIOReactor.validateActiveChannels(SingleCoreIOReactor.java:166)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:128)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:92)
> [httpcore5-5.3.4.jar:5.3.4] at
> org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
> [httpcore5-5.3.4.jar:5.3.4] at java.lang.Thread.run(Thread.java:840) [?:?]
> Caused by: java.net.SocketTimeoutException: 2 SECONDS at
> org.apache.hc.core5.io.SocketTimeoutExceptionFactory.create(SocketTimeoutExceptionFactory.java:50)
> ~[httpcore5-5.3.4.jar:5.3.4]}}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]