features/support/test_client.rb in ftpd-0.5.0 vs features/support/test_client.rb in ftpd-0.6.0
- old
+ new
@@ -4,17 +4,24 @@
class TestClient
extend Forwardable
include FileUtils
- def initialize(opts = {})
+ attr_accessor :tls_mode
+
+ def initialize
+ @tls_mode = :off
@temp_dir = Ftpd::TempDir.make
- @ftp = make_ftp(opts)
@templates = TestFileTemplates.new
end
+ def start
+ @ftp = make_ftp
+ end
+
def close
+ return unless @ftp
@ftp.close
end
def_delegators :@ftp,
:chdir,
@@ -34,10 +41,24 @@
:rename,
:rmdir,
:status,
:system
+ # Make a connection from a specific IP. Net::FTP doesn't have a way
+ # to force the local IP, so fake it here.
+
+ def connect_from(source_ip, host, port)
+ in_addr = Socket.pack_sockaddr_in(0, source_ip)
+ out_addr = Socket.pack_sockaddr_in(port, host)
+ socket = Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ socket.bind(in_addr)
+ socket.connect(out_addr)
+ decorate_socket socket
+ @ftp = make_ftp
+ @ftp.set_socket(socket)
+ end
+
def raw(*command)
@ftp.sendcmd command.compact.join(' ')
end
def get(mode, remote_path)
@@ -65,11 +86,11 @@
def file_contents(path)
File.open(temp_path(path), 'rb', &:read)
end
def xpwd
- response = raw('XPWD')
+ response = @ftp.sendcmd('XPWD')
response[/"(.+)"/, 1]
end
def store_unique(local_path, remote_path)
command = ['STOU', remote_path].compact.join(' ')
@@ -101,10 +122,14 @@
rescue EOFError
false
end
end
+ def set_option(option)
+ @ftp.sendcmd "OPTS #{option}"
+ end
+
private
RAW_METHOD_REGEX = /^send_(.*)$/
def local_path(remote_path)
@@ -113,21 +138,20 @@
def temp_path(path)
File.expand_path(path, @temp_dir)
end
- def make_ftp(opts)
- tls_mode = opts[:tls] || :off
- case tls_mode
+ def make_ftp
+ case @tls_mode
when :off
make_non_tls_ftp
when :implicit
make_tls_ftp(:implicit)
when :explicit
make_tls_ftp(:explicit)
else
- raise "Unknown TLS mode: #{tls_mode}"
+ raise "Unknown TLS mode: #{@tls_mode}"
end
end
def make_tls_ftp(ftps_mode)
ftp = DoubleBagFTPS.new
@@ -139,8 +163,39 @@
ftp
end
def make_non_tls_ftp
Net::FTP.new
+ end
+
+ # Ruby 2.0's Ftp class is expecting a TCPSocket, not a Socket. The
+ # trouble comes with Ftp#close, which closes sockets by first doing
+ # a shutdown, setting the read timeout, and doing a read. Plain
+ # Socket doesn't have those methods, so fake it.
+ #
+ # Plain socket _does_ have #close, but we short-circuit it, too,
+ # because it takes a few seconds. We're in a hurry when running
+ # tests, and can afford to be a little sloppy when cleaning up.
+
+ def decorate_socket(sock)
+
+ def sock.shutdown(how)
+ @shutdown = true
+ end
+
+ def sock.read_timeout=(seconds)
+ end
+
+ # Skip read after shutdown. Prevents 2.0 from hanging in
+ # Ftp#close
+
+ def sock.read(*args)
+ return if @shutdown
+ super(*args)
+ end
+
+ def close
+ end
+
end
end