I've updated TestAsyncTlsHandshakeTimeout to reproduce this bug. Specifically, see the two test cases that use FORCE_HTTP_2 (they will be marked as ignored in the test report, but they still run).
On Wed, Jan 21, 2026 at 3:13 PM Ryan Schmitt <[email protected]> wrote: > I got a report today that the `ioReactorExceptionCallback` is firing due > to a socket timeout: > > java.net.SocketTimeoutException: 2500 MILLISECONDS > at > org.apache.hc.core5.io.SocketTimeoutExceptionFactory.create(SocketTimeoutExceptionFactory.java:50) > at > org.apache.hc.core5.reactor.ssl.SSLIOSession$1.timeout(SSLIOSession.java:223) > at > org.apache.hc.core5.reactor.InternalDataChannel.onTimeout(InternalDataChannel.java:170) > at > org.apache.hc.core5.reactor.InternalChannel.checkTimeout(InternalChannel.java:67) > at > org.apache.hc.core5.reactor.SingleCoreIOReactor.checkTimeout(SingleCoreIOReactor.java:239) > at > org.apache.hc.core5.reactor.SingleCoreIOReactor.validateActiveChannels(SingleCoreIOReactor.java:166) > at > org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:128) > at > org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:92) > at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44) > at java.base/java.lang.Thread.run(Thread.java:840) > > Based on the code and the stack trace, 2500 milliseconds is actually the > connection timeout and thus the TLS handshake timeout. This is reproducible > in TestAsyncTlsHandshakeTimeout by setting `HttpVersionPolicy.FORCE_HTTP_2` > (but _not_ `NEGOTIATE`). I traced this change in behavior back to the > following commit: > > > https://github.com/apache/httpcomponents-core/commit/be9902826576413c694c59cc582fc38db8aeb772 > > This is a regression, as the contract for > `setIoReactorExceptionCallback()` is that it sets a callback for exceptions > that propagate all the way through the IOReactor *and kill the client*. The > default behavior (cf. LoggingExceptionCallback) is to log an ERROR, which > is bad enough, but in our case we supply a custom callback that logs a > FATAL, which then triggers alarms and stuff. > > How should we address this? What types of exceptions were intended to be > reported by be9902826? We probably (1) want to use a different exception > callback for these exceptions and (2) want to fire the callback only for > unexpected (internal) exceptions and not things like timeouts. It's also > possible that this exception callback should fire when > `HttpVersionPolicy.NEGOTIATE` is used; it currently doesn't, which may also > be a bug. >
