I have a Ruby on Rails (Rails 3.2.14 and ruby 1.9.3) application that uploads 2 files to a remote SFTP server. The SFTP code is:
require 'net/sftp'
Rails.logger.info("Creating SFTP connection")
uri = URI.parse('sftp://'+ host)
Net::SFTP.start(uri.host,'user', :password=>'password',:port=>port) do |sftp|
Rails.logger.info("SFTP Connection created, uploading files.")
sftp.upload!("public/file1.txt", "./file1.txt")
Rails.logger.info("First file uploaded.")
sftp.upload!("file2.txt", "./file2.txt")
Rails.logger.info("Both files uploaded, terminating connection.")
end
Rails.logger.info("Connection terminated.")
Both files are uploading properly to the remote server, but the connection doesn't seem to close. I keep getting an error when I execute this function and on analyzing my console, I see that the "Both files uploaded, terminating connection." logger message is running, but nothing after that. I've tried using
sftp.close(:handle)
sftp.close!(:handle)
#and
sftp.close_connection()
but none of them are working. Any idea on why this is happening and how I can rectify it? I'm running this through a single instance Engine Yard cloud server.
EDIT These are the last few lines in my log: Creating SFTP connection SFTP Connection created, uploading files. First file uploaded. Both files uploaded, terminating connection.
After that, nothing. When viewing my log with the 'tail -f' command, the log goes up to that last line, and the app redirects to the internal server error page.
Net::SFTP.start('host', 'user', password: 'pass', port: 22) do |sftp|
# Do stuff
end
is equivalent to :
session = Net::SSH.start('host', 'user', password: 'pass', port: 22)
sftp = Net::SFTP::Session.new(session)
sftp.connect!
# Do stuff
sftp.close_channel unless sftp.nil?
session.close unless session.nil?
For people that couldn't find a way to actually close the connection, without using auto-closing blocks, here is how I you can achieve this :
require 'net/ssh'
require 'net/sftp'
begin
# Instance SSH/SFTP session :
session = Net::SSH.start('host', 'user', password: 'pass', port: 22)
sftp = Net::SFTP::Session.new(session)
# Always good to timeout :
Timeout.timeout(10) do
sftp.connect! # Establish connection
# Do stuff
end
rescue Timeout::Error => e
# Do some custom logging
puts e.message
ensure
# Close SSH/SFTP session
sftp.close_channel unless sftp.nil? # Close SFTP
session.close unless session.nil? # Then SSH
# If you really really really wanna make sure it's closed,
# and raise after 10 seconds delay
Timeout.timeout(10) do
sleep 1 until (sftp.nil? or sftp.closed?) and (session.nil? or session.closed?)
end
end
If you don't close the connection before performing some other task, you might sometimes experience errors like this in rails for instance :
IOError (not opened for reading) # Not closed when rendering controller action
ActionView::Template::Error (not opened for reading) # Not closed when rendering template