Unable to catch an exception if connection was dropped by the server
mspanc opened this issue · comments
There's no way to properly catch exception if there's an attempt to issue a command via run
on connection that was closed on server side.
** (EXIT from #PID<0.330.0>) an exception was raised:
** (UndefinedFunctionError) undefined function: :closed.exception/1 (module :closed is not available)
:closed.exception([])
lib/sshex.ex:57: SSHEx.open_channel/2
lib/sshex.ex:28: SSHEx.run/5
(plumber) web/backbone/architecture/computing_node/wrapper.ex:82: Wrapper.handle_call/3
lib/connection.ex:475: Connection.handle_call/3
(stdlib) gen_server.erl:607: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:639: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:237: :proc_lib.init_p_do_apply/3
Context: I am using https://github.com/fishcakez/connection to wrap SSH connection logic (connecting/disconnecting etc.). This library gracefully handles reconnecting etc. It has similar pattern to GenServer, so if I want to issue a command on the server, I send Connection.call
(equivalent of GenServer.call
) and then synchronously call SSHEx.run
. When unhandled exception happens such as mentioned above the process crashes and supervisor has to restart it. It works, but I prefer to handle this more gracefully, and schedule reconnect.
IMO this line: https://github.com/rubencaro/sshex/blob/master/lib/sshex.ex#L57 should raise an error that has predictable module name
Hi @mspanc ,
It looks like a simple bug with the reason
argument passed to raise
. It should perform some inspect
to the reason
before raising, maybe. With that, the exception you see would be more understandable. It would be a generic exception showing the very same message that :ssh
sent back as the reason
for failing.
The Right Way would be not to use raise
at all (like :ssh
does). We should not use exceptions for flow control. That would be up for 2.0, as it would break backwards compatibility.
To grow specific exception vocabulary (such a well organized exception module hierarchy) seems too much to me, as we expect the raise itself to disappear soon enough.
For your use case, I would fix the initial bug:
{ :error, reason } -> raise inspect(reason)
Then catch any raising coming from SSHEx.run
as a generic :ssh
failure. The message will be everything :ssh
gave as reason anyway.
From 2.0 on, there should be no exceptions (if any should rise then the supervising mechanism would be the right way to go), and SSHEx.run
will return {:error, reason}
.
If that sounds ok for you, I can make a release with the fix. Does it?
Hello @rubencaro
Honestly speaking, due to time pressure I've already rewritten this part in my app using raw :ssh
exactly in a way you describe for 2.0. So if it's just a matter of adding inspect(...)
, you will probably satisfy a few users, and it's really quick fix. But in my case I have already made this differently (but shhex gave me a lot of inspiration!).
Just released 1.2.0 mostly improving and documenting error handling. And clean {:error, reason}
return values are added to the TODO list for 2.0.
Closing.
Thanks!