Class: FakeFtp::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/fake_ftp/server.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(control_port = 21, data_port = nil, options = {}) ⇒ Server

Returns a new instance of Server.

Raises:

  • (Errno::EADDRINUSE)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 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

Instance Attribute Details

#clientObject

Returns the value of attribute client.



9
10
11
# File 'lib/fake_ftp/server.rb', line 9

def client
  @client
end

#command_stateObject

Returns the value of attribute command_state.



9
10
11
# File 'lib/fake_ftp/server.rb', line 9

def command_state
  @command_state
end

#data_serverObject

Returns the value of attribute data_server.



9
10
11
# File 'lib/fake_ftp/server.rb', line 9

def data_server
  @data_server
end

#passive_portObject

Returns the value of attribute passive_port.



9
10
11
# File 'lib/fake_ftp/server.rb', line 9

def passive_port
  @passive_port
end

#portObject

Returns the value of attribute port.



10
11
12
# File 'lib/fake_ftp/server.rb', line 10

def port
  @port
end

#storeObject

Returns the value of attribute store.



10
11
12
# File 'lib/fake_ftp/server.rb', line 10

def store
  @store
end

#workdirObject Also known as: path

Returns the value of attribute workdir.



10
11
12
# File 'lib/fake_ftp/server.rb', line 10

def workdir
  @workdir
end

Instance Method Details

#absolute=(value) ⇒ Object



146
147
148
149
150
151
# 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?Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/fake_ftp/server.rb', line 142

def absolute?
  options[:absolute]
end

#abspath(filename) ⇒ Object



156
157
158
159
# 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?Boolean

Returns:

  • (Boolean)


195
196
197
# File 'lib/fake_ftp/server.rb', line 195

def active?
  options[:mode] == :active
end

#add_file(filename, data, last_modified_time = Time.now) ⇒ Object



61
62
63
64
65
# 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) ⇒ Object



216
217
218
219
220
221
222
223
# 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) ⇒ Object



235
236
237
238
# File 'lib/fake_ftp/server.rb', line 235

def debug(msg)
  return unless options[:debug]
  $stderr.puts("DEBUG:fake_ftp:#{msg}")
end

#file(name) ⇒ Object



47
48
49
50
51
52
53
54
55
# 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

#filesObject



37
38
39
40
41
42
43
44
45
# 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) ⇒ Object



225
226
227
228
229
230
231
232
233
# 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

#modeObject



138
139
140
# File 'lib/fake_ftp/server.rb', line 138

def mode
  options[:mode]
end

#mode=(value) ⇒ Object



131
132
133
134
135
136
# 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

#resetObject



57
58
59
# File 'lib/fake_ftp/server.rb', line 57

def reset
  @store.clear
end

#respond_with(stuff) ⇒ Object



161
162
163
164
165
# 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) ⇒ Boolean Also known as: is_running?

Returns:

  • (Boolean)


124
125
126
127
# 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

#startObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# 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

#stopObject



115
116
117
118
119
120
121
122
# 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