[ 
https://issues.apache.org/jira/browse/SSHD-1295?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17602860#comment-17602860
 ] 

Thomas Wolf commented on SSHD-1295:
-----------------------------------

PR 242 works now. It looks OK to me, but I'm not sure about it's the right way. 
I'd appreciate your opinions, [~lgoldstein] and [~gnodet].

I'm not sure it fully resolves this problem. It certainly works if there is 
only one thread calling {{{}ConnectFuture.verify(){}}}.

What is the programming model and contract here anyway? It _is_ theoretically 
possible that several threads would call {{verify(timeout)}} on the same 
{{ConnectFuture}} object, possibly even with different time-outs. The current 
code in PR 242 will abort the connection (or rather, close the established 
connection and session) if _any_ of them times out before the {{ClientSession}} 
is established (set on the future).

Another programming model would be to abort the connection only if the future 
is _canceled_ and close the session only on cancellation. That would leave it 
up to the application to decide under which circumstances it wants to treat a 
time-out as a connection abort, and if so call {{cancel()}} explicitly, but it 
will make using the {{ConnectFuture}} correctly even in the 
single-thread-calls-verify case much more complicated.

Alternatively we could specify the contract that {{verify()}} must not be 
called by two threads concurrently. If so, we might even enforce that contract.

I also see that cancellations are only propagated upwards (MINA or Netty future 
-> {{IoConnectFuture}} -> {{{}ConnectFuture{}}}, or in the NIO2 completion 
listener {{IoConnectFuture}} -> {{{}ConnectFuture{}}}, but in the latter case I 
see no place where {{IoConnectFuture.cancel()}} is called at all, and in the 
MINA/Netty cases, I don't know where the MINA or Netty future might be 
cancelled – perhaps inside the MINA/Netty libraries). I see no downward 
propagations (Application calls {{ConnectFuture.cancel()}} -> 
{{IoConnectFuture}} is not cancelled). So I suspect we could get the same 
established connection and session behind the scenes on cancellations. PR 242 
does not handle that case. Should it?

Another shortcoming of the current approach in PR 242 is that it does create 
the {{ClientSession}} first, which already sends messages, and only then 
decides to close the session again. However, avoiding that would be a much more 
invasive change that would have to re-define the way a {{ClientSession}} is 
started: basically, don't send messages in the constructor already but have a 
separate {{start()}} method that would have to be called after the session 
object was fully created. I would suggest that any attempts to fix that should 
be done in separate changes. First let's get this right on 
{{{}ClientSession{}}}/{{{}ConnectFuture{}}} level and think about the 
{{{}IoSession{}}}/{{{}IoConnectFuture{}}} level and the NIO2/MINA/Netty levels 
later on.

> Connection attempt not canceled when a connection timeout occurs
> ----------------------------------------------------------------
>
>                 Key: SSHD-1295
>                 URL: https://issues.apache.org/jira/browse/SSHD-1295
>             Project: MINA SSHD
>          Issue Type: Bug
>    Affects Versions: 2.9.2
>            Reporter: Thomas Wolf
>            Priority: Major
>
> This is not a new bug; it's also present in earlier versions.
> When {{client.connect().verify(timeout)}} times out, the underlying 
> asynchronous connection attempt still continues _and may still succeed_. If 
> that happens, there is a connection and session that the user cannot use or 
> close. (Short of calling verify().getSession() again, but that is a pattern I 
> have never seen, and it also makes using a timeout rather useless.)
> This means that timeouts are inherently unreliable.
> Here's a simple test case illustrating this problem (for 
> {{org.apache.sshd.client.ClientTest}}):
> {code:java}
> @Test
> public void testConnectTimeout() throws Exception {
>     client.start();
>     try {
>         ConnectFuture future = client.connect(getCurrentTestName(), 
> TEST_LOCALHOST, port);
>         try {
>             future.verify(1);
>             fail("Timeout expected");
>         } catch (InterruptedIOException | SshException e) {
>             ClientSession session = null;
>             try {
>                 session = future.verify(CONNECT_TIMEOUT).getSession();
>             } catch (SshException e2) {
>                 assertTrue("Expected a timeout, got " + e2, 
> e2.getMessage().contains("timeout"));
>             }
>             if (session != null) {
>                 assertTrue("Session should be closed", session.isClosed() || 
> session.isClosing());
>             }
>         }
>     } finally {
>         client.stop();
>     }
> }
> {code}
> This test fails.
> When the {{ConnectFuture}} times out (or is canceled, or waiting is 
> interrupted), the underlying {{IoConnectFuture}} must be canceled. If we do 
> get an {{IoSession}} all the same, but the {{ConnectFuture}} is already 
> canceled or timed out, the {{IoSession}} must be closed immediately. 
> Preferrably before a {{Session}} is created at all; otherwise, that SSH 
> session must be closed, too. Creating a {{ClientSession}} already starts the 
> application-level protocol (sending the SSH ident, or if a proxy is used, the 
> proxy connect request).
> The test also highlights another problem: there is no clear indication that 
> the failure of {{verify()}} indicates a timeout. Probably there should be a 
> dedicated exception for this, or at least the {{SshException}} should have a 
> {{TimeoutException}} as cause.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@mina.apache.org
For additional commands, e-mail: dev-h...@mina.apache.org

Reply via email to