Daniel Azuma
2007-Oct-23 11:36 UTC
[Net-ssh-users] Problems with asynchronous Net::SFTP operations
This question is probably specifically for Jamis, but I thought I''d
put it on the list to see if anyone else has a solution.
I have some code that uses Net::SSH operations asynchronously.
Specifically, I run a single session.loop that handles communication
for multiple mutually unrelated channels. Those channels are
implemented as series of callbacks separate and (as best I can)
decoupled from the core session.loop.
One of those channels is a Net::SFTP session, which simply opens a
file, writes some data to it, and closes it. Again, because
session.loop is being called elsewhere, globally for all channels on
the session, this sftp channel is implemented simply as a collection
of asynchronous calls and callbacks. Thus, the code looks something
like this:
sftp = Net::SFTP::Session.new(my_global_ssh_session)
sftp.connect # BUG: this calls session.loop and blocks!
sftp.open(my_file_path, IO::WRONLY | IO::CREAT | IO::TRUNC) do |
status, handle|
sftp.write(handle, my_data_to_write) do |status|
sftp.close_handle(handle) do |status|
sftp.close_channel
end
end
end
Now, I have two problems.
(1) Is there a way to "connect" asynchronously? As is, the call to
sftp.connect (second line in the code snippet) calls session.loop and
blocks internally waiting for the driver to open, which is not what
I''d like. I cannot pass it a block because then it will automatically
close the channel at the end of the block, which I can''t afford
because I want to kick off the operations asynchronously. I also
cannot pass a block to the Net::SFTP::Session initializer, because
that causes the initializer to call session.loop and block internally.
What I''d like is some way to "connect" in the same way the
other
asynchronous operations behave-- calling a callback once the driver
is open, but not closing the channel on me. I can''t determine a way
to do that.
(2) Handling of non-FX_OK status messages seems to not work for
asynchronous operations. That is, if my sftp.open call (third line in
the code snippet) fails (e.g. due to permissions), my callback never
gets executed. Instead, a StatusException is thrown out of
session.loop. As far as I can tell so far, this is because
Net::SFTP::Operations::Abstract#do_status throws StatusException if
it receives any status that is not FX_OK, rather than passing it to
the callback. And none of the provided operations overrides this
behavior.
I''m trying to keep my shared session.loop agnostic to the individual
channels that are running, and instead handle errors at the callback
level. I can''t determine a way to do that without overriding the
operation implementations.
I could probably monkeypatch Net::SFTP to support the two items
above, but before I resort to that, I was wondering if anyone knows
something I''m missing. Or, if the preferred procedure is to write a
patch to the net-sftp gem, let me know and I can try to come up with
something.
Thanks,
Daniel Azuma