class FakeFtp::Server
Attributes
client[RW]
command_state[RW]
data_server[RW]
options[R]
passive_port[RW]
path[RW]
port[RW]
store[RW]
workdir[RW]
Public Class Methods
new(control_port = 21, data_port = nil, options = {})
click to toggle source
# File lib/fake_ftp/server.rb, line 14 def initialize(control_port = 21, data_port = nil, options = {}) @port = control_port @passive_port = data_port @store = {} @workdir = '/pub' @options = options @command_state = {} @connection = nil @data_server = nil @server = nil @client = nil raise Errno::EADDRINUSE, port.to_s if !control_port.zero? && running? if passive_port && !passive_port.zero? && running?(passive_port) raise Errno::EADDRINUSE, passive_port.to_s end self.mode = options.fetch(:mode, :active) self.absolute = options.fetch(:absolute, false) end
Public Instance Methods
absolute=(value)
click to toggle source
# File lib/fake_ftp/server.rb, line 146 def absolute=(value) unless [true, false].include?(value) raise ArgumentError, "invalid absolute #{value}" end options[:absolute] = value end
absolute?()
click to toggle source
# File lib/fake_ftp/server.rb, line 142 def absolute? options[:absolute] end
abspath(filename)
click to toggle source
# File lib/fake_ftp/server.rb, line 156 def abspath(filename) return filename if filename.start_with?('/') [@workdir.to_s, filename].join('/').gsub('//', '/') end
active?()
click to toggle source
# File lib/fake_ftp/server.rb, line 195 def active? options[:mode] == :active end
add_file(filename, data, last_modified_time = Time.now)
click to toggle source
# File lib/fake_ftp/server.rb, line 61 def add_file(filename, data, last_modified_time = Time.now) @store[abspath(filename)] = FakeFtp::File.new( filename.to_s, data, options[:mode], last_modified_time ) end
build_wildcards(args)
click to toggle source
# File lib/fake_ftp/server.rb, line 216 def build_wildcards(args) wildcards = [] args.each do |arg| next unless arg.include? '*' wildcards << arg.gsub('*', '.*') end wildcards end
debug(msg)
click to toggle source
# File lib/fake_ftp/server.rb, line 235 def debug(msg) return unless options[:debug] $stderr.puts("DEBUG:fake_ftp:#{msg}") end
file(name)
click to toggle source
# File lib/fake_ftp/server.rb, line 47 def file(name) @store.values.detect do |f| if absolute? abspath(f.name) == name else f.name == name end end end
files()
click to toggle source
# File lib/fake_ftp/server.rb, line 37 def files @store.values.map do |f| if absolute? abspath(f.name) else f.name end end end
matching_files(wildcards)
click to toggle source
# File lib/fake_ftp/server.rb, line 225 def matching_files(wildcards) if !wildcards.empty? @store.values.select do |f| wildcards.any? { |wildcard| f.name =~ /#{wildcard}/ } end else @store.values end end
mode()
click to toggle source
# File lib/fake_ftp/server.rb, line 138 def mode options[:mode] end
mode=(value)
click to toggle source
# File lib/fake_ftp/server.rb, line 131 def mode=(value) unless %i[active passive].include?(value) raise ArgumentError, "invalid mode #{value.inspect}" end options[:mode] = value end
reset()
click to toggle source
# File lib/fake_ftp/server.rb, line 57 def reset @store.clear end
respond_with(stuff)
click to toggle source
# File lib/fake_ftp/server.rb, line 161 def respond_with(stuff) return if stuff.nil? || @client.nil? || @client.closed? debug("server client raw: -> #{stuff.inspect}") @client.print(stuff + "\r\n") end
running?(tcp_port = nil)
click to toggle source
# File lib/fake_ftp/server.rb, line 124 def running?(tcp_port = nil) return port_is_open?(port) if tcp_port.nil? port_is_open?(tcp_port) end
Also aliased as: is_running?
start()
click to toggle source
# File lib/fake_ftp/server.rb, line 67 def start @started = true @server = ::TCPServer.new('127.0.0.1', port) @port = @server.addr[1] @thread = Thread.new do while @started debug('enter client loop') @client = begin @server.accept rescue => e debug("error on accept: #{e}") nil end next unless @client respond_with('220 Can has FTP?') @connection = Thread.new(@client) do |socket| debug('enter request thread') while @started && !socket.nil? && !socket.closed? input = begin socket.gets rescue debug("error on socket.gets: #{e}") nil end if input debug("server client raw: <- #{input.inspect}") respond_with(handle_request(input)) end end unless @client.nil? @client.close unless @client.closed? @client = nil end debug('leave request thread') end debug('leave client loop') end unless @server.nil? @server.close unless @server.closed? @server = nil end end return unless passive_port @data_server = ::TCPServer.new('127.0.0.1', passive_port) @passive_port = @data_server.addr[1] end
stop()
click to toggle source
# File lib/fake_ftp/server.rb, line 115 def stop @started = false @client&.close @server&.close @server = nil @data_server&.close @data_server = nil end
Private Instance Methods
handle_request(request)
click to toggle source
# File lib/fake_ftp/server.rb, line 167 def handle_request(request) return if request.nil? debug("raw request: #{request.inspect}") command = request[0, 4].downcase.strip contents = request.split message = contents[1..contents.length] inst = load_command_instance(command) return "500 Unknown command #{command.inspect}" if inst.nil? debug( "running command #{command.inspect} " \ "#{inst.class.name}#run(*#{message.inspect})" ) inst.run(*([self] + message)) end
load_command_instance(command)
click to toggle source
# File lib/fake_ftp/server.rb, line 183 def load_command_instance(command) require "fake_ftp/server_commands/#{command}" FakeFtp::ServerCommands.constants.each do |const_name| next unless const_name.to_s.downcase == command return FakeFtp::ServerCommands.const_get(const_name).new end nil rescue LoadError => e debug("failed to require #{command.inspect} class: #{e}") nil end
port_is_open?(port)
click to toggle source
# File lib/fake_ftp/server.rb, line 199 def port_is_open?(port) begin Timeout.timeout(1) do begin TCPSocket.new('127.0.0.1', port).close return true rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH return false end end rescue Timeout::Error => e debug("timeout while checking port #{port}: #{e}") end false end