Describe the bug
IoSessionInitiator.pollConnectFuture() does not correctly handle two failure
modes that occur specifically when connecting through an HTTP proxy
(ProxyType=http):
-
Exceptional future completion is silently ignored in the "still pending" path.
When a ConnectFuture completes with getSession() == null but
getException() != null (e.g. proxy returns 407, or the CONNECT handshake
is rejected), the original code path only checked getSession() != null
before falling through to logic that treats the future as still pending.
This left connectFuture non-null indefinitely, which blocks
shouldReconnect() from ever returning true again — the initiator never
retries.
-
A stalled proxy CONNECT handshake with no exception and no session
never times out. MINA's proxy filter can get stuck mid-handshake
(no session resolved, no exception thrown) if the proxy accepts the TCP
connection but never completes the CONNECT response. There is no watchdog
to cancel this and force a reconnect.
This matches the underlying pattern described in #295 (authenticating HTTP
proxy connections) and the older reconnection issues QFJ-868 / QFJ-895, but
those were not addressing the exception-completion and stall-timeout cases
specifically for proxy connects.
To Reproduce
-
Configure an initiator session with:
ProxyType=http
ProxyHost=
ProxyPort=
ProxyUser=
ProxyPassword=
-
Point the proxy at an endpoint that either:
- rejects the CONNECT with a 407/4xx (simulates auth failure), or
- accepts the TCP connection but never responds to the CONNECT request
(simulates a hung/misbehaving proxy).
- Observe that after the first failed connect attempt, the initiator never
attempts to reconnect again, even though ReconnectInterval is configured.
Expected behavior
- On an exceptional
ConnectFuture completion, connectFuture should be
cleared immediately and handleConnectException() should fire so normal
reconnect backoff resumes.
- On a stalled proxy handshake (no session, no exception) that exceeds
connectTimeoutMillis (max pending wait), the pending attempt should be
cancelled, the ProxyConnector recreated, and the reconnect timers reset
so the next scheduled reconnect fires correctly instead of firing
immediately or never.
system information:
- OS: [Linux/Windows]
- Java version [JDK8 and above]
- QFJ Version [3.0.1]
Additional context
Reconnect is not attempted even after endpoint becomes available.
App restart fixes it.
Related issues
Proposed fix
I've patched pollConnectFuture() to:
- Check
future.getException() explicitly and route it through
handleConnectException(), clearing connectFuture right away.
- Add a
maxPendingMillis (connectTimeoutMillis) watchdog: if a connect
attempt has been pending longer than this, call a new
cancelAndResetPendingConnectAttempt() helper that:
- force-closes any half-open
IoSession and cancels the future,
- if
ioConnector instanceof ProxyConnector, cancels the proxy connector's
internal connect future and recreates the connector via
setupIoConnector(),
- resets
lastConnectTime and lastReconnectAttemptTime to the current
time so the next reconnect timer tick doesn't fire immediately (since
pendingMillis would otherwise already exceed ReconnectInterval).
I have a working patch and a JUnit test suite covering the changes.
Happy to open a PR with the diff and tests attached if maintainers confirm
this is the right approach — wanted to raise the issue first in case there's
a preferred design (e.g. exposing connectTimeoutMillis as a new session
setting rather than reusing an existing timeout field).
Describe the bug
IoSessionInitiator.pollConnectFuture()does not correctly handle two failuremodes that occur specifically when connecting through an HTTP proxy
(ProxyType=http):
Exceptional future completion is silently ignored in the "still pending" path.
When a
ConnectFuturecompletes withgetSession() == nullbutgetException() != null(e.g. proxy returns 407, or the CONNECT handshakeis rejected), the original code path only checked
getSession() != nullbefore falling through to logic that treats the future as still pending.
This left
connectFuturenon-null indefinitely, which blocksshouldReconnect()from ever returning true again — the initiator neverretries.
A stalled proxy CONNECT handshake with no exception and no session
never times out. MINA's proxy filter can get stuck mid-handshake
(no session resolved, no exception thrown) if the proxy accepts the TCP
connection but never completes the CONNECT response. There is no watchdog
to cancel this and force a reconnect.
This matches the underlying pattern described in #295 (authenticating HTTP
proxy connections) and the older reconnection issues QFJ-868 / QFJ-895, but
those were not addressing the exception-completion and stall-timeout cases
specifically for proxy connects.
To Reproduce
Configure an initiator session with:
ProxyType=http
ProxyHost=
ProxyPort=
ProxyUser=
ProxyPassword=
Point the proxy at an endpoint that either:
(simulates a hung/misbehaving proxy).
attempts to reconnect again, even though
ReconnectIntervalis configured.Expected behavior
ConnectFuturecompletion,connectFutureshould becleared immediately and
handleConnectException()should fire so normalreconnect backoff resumes.
connectTimeoutMillis(max pending wait), the pending attempt should becancelled, the
ProxyConnectorrecreated, and the reconnect timers resetso the next scheduled reconnect fires correctly instead of firing
immediately or never.
system information:
Additional context
Reconnect is not attempted even after endpoint becomes available.
App restart fixes it.
Related issues
Proposed fix
I've patched
pollConnectFuture()to:future.getException()explicitly and route it throughhandleConnectException(), clearingconnectFutureright away.maxPendingMillis(connectTimeoutMillis) watchdog: if a connectattempt has been pending longer than this, call a new
cancelAndResetPendingConnectAttempt()helper that:IoSessionand cancels the future,ioConnector instanceof ProxyConnector, cancels the proxy connector'sinternal connect future and recreates the connector via
setupIoConnector(),lastConnectTimeandlastReconnectAttemptTimeto the currenttime so the next reconnect timer tick doesn't fire immediately (since
pendingMilliswould otherwise already exceedReconnectInterval).I have a working patch and a JUnit test suite covering the changes.
Happy to open a PR with the diff and tests attached if maintainers confirm
this is the right approach — wanted to raise the issue first in case there's
a preferred design (e.g. exposing
connectTimeoutMillisas a new sessionsetting rather than reusing an existing timeout field).