ftpd 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d52205c73f9747677423670210f0b7eff28659e7
4
- data.tar.gz: 352b8761bf5518f2a41b549c64b57f21d989d93e
3
+ metadata.gz: 15d6cd56c8148e7961d07ebb86246fb3c1fe7a25
4
+ data.tar.gz: 54b088a8bee0cfe945377d9dd97b5ef1bcf97650
5
5
  SHA512:
6
- metadata.gz: 190fe51abf6cdc5b7a8122cb32bd31d476d43a55d40034f5eaea3b0e5d4ceb640ff0be3472b6791efac335b601cec7adb4a51beb23a07ad3022befe58cc0b2da
7
- data.tar.gz: 639d4e7024ec277a57bdc77c985dfa3068df523660038412e4e341b228f7354f373049250700461f57dbeaa7e291c43528d08ff0fcb340e4da70dadebeb7a31f
6
+ metadata.gz: 4978b27e90b70d04d670f2e60db440dc7e2b5686eda9674bf539db822f873e7210d446a640b95600f88f7dba89b2dbf41b01b3594ec1f28ee81ae670abdaa8d8
7
+ data.tar.gz: dfcbb64e17744d9dd2016e2d7572da2194e4b5e24cd3efe986a7f1ca71f2ae76876039ffcb8001cc49ba1ab235348cff7838c48238c2afca941dab17c0026ac6
data/Changelog.md CHANGED
@@ -2,6 +2,29 @@ This is the change log for the main branch of ftpd, which supports
2
2
  Ruby 1.9 and greater. For ruby 1.8.7, please use the latest version
3
3
  before 0.8.0.
4
4
 
5
+ ### 0.17.0
6
+
7
+ This release is brought to you by Mike Ragalie. Thanks, Mike!
8
+
9
+ Features
10
+
11
+ * Streaming file transfers (issues #12 and #26)
12
+
13
+ API Changes
14
+
15
+ These breaking API changes are for streaming file transfers. Custom
16
+ file systems will notice these changes. Nobody else should.
17
+
18
+ * The FileSystemErrorTranslator has been removed. Instead, the file
19
+ system may raise FtpServerError with a message and an optional error
20
+ code.
21
+
22
+ * DiskFileSystem#write now takes an `Ftpd::Stream` object instead of a
23
+ `String`.
24
+
25
+ * DiskFileSystem#read expects to receive a block and yields an `IO`
26
+ object to the block.
27
+
5
28
  ### 0.16.0
6
29
 
7
30
  Bug fixes
@@ -12,7 +35,7 @@ Bug fixes
12
35
 
13
36
  Bug fixes
14
37
 
15
- * Ignore Errno::ENOTCONN on socket shutdown (issue #24)
38
+ * Ignore Errno::ENOTCONN on socket shutdown (issue #24)
16
39
 
17
40
  Administration
18
41
 
data/README.md CHANGED
@@ -357,6 +357,7 @@ Among those who have improved ftpd are:
357
357
  * Joshua Rutherford
358
358
  * Larry. W. Cashdollar
359
359
  * Michael de Silva
360
+ * Mike Ragalie
360
361
  * cransom
361
362
 
362
363
  If I've forgotten to add you, please remind me, or submit a merge
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.16.0
1
+ 0.17.0
data/doc/benchmarks.md CHANGED
@@ -5,78 +5,67 @@ Benchmarks using pyftpd's benchmark script and
5
5
 
6
6
  ## Results
7
7
 
8
- ### ftpd 0.4.0
9
-
10
- (starting with 13.6M of RSS memory being used)
11
- STOR (client -> server) 845.55 MB/sec 13.6M RSS
12
- RETR (server -> client) 1234.80 MB/sec 859.5M RSS
13
- 200 concurrent clients (connect, login) 0.14 secs 863.0M RSS
14
- STOR (1 file with 200 idle clients) 848.56 MB/sec 863.0M RSS
15
- RETR (1 file with 200 idle clients) 1227.46 MB/sec 866.4M RSS
16
- 200 concurrent clients (RETR 10.0M file) 5.36 secs 2.0G RSS
17
- 200 concurrent clients (STOR 10.0M file) 4.65 secs 2.0G RSS
18
- 200 concurrent clients (QUIT) 0.01 secs
19
-
20
- ### pyftpdlib 1.0.1
21
-
22
- (starting with 8.9M of RSS memory being used)
23
- STOR (client -> server) 81.73 MB/sec 8.9M RSS
24
- RETR (server -> client) 1001.05 MB/sec 8.9M RSS
25
- 200 concurrent clients (connect, login) 2.58 secs 9.6M RSS
26
- STOR (1 file with 200 idle clients) 108.46 MB/sec 9.6M RSS
27
- RETR (1 file with 200 idle clients) 1134.18 MB/sec 9.6M RSS
28
- 200 concurrent clients (RETR 10.0M file) 2.32 secs 10.3M RSS
29
- 200 concurrent clients (STOR 10.0M file) 4.12 secs 10.4M RSS
8
+ ### ftpd 0.17.0
9
+
10
+ (starting with 14.9M of memory being used)
11
+ STOR (client -> server) 120.75 MB/sec 14.9M
12
+ RETR (server -> client) 741.77 MB/sec 22.7M
13
+ 200 concurrent clients (connect, login) 0.20 secs 19.2M
14
+ STOR (1 file with 200 idle clients) 144.77 MB/sec 19.2M
15
+ RETR (1 file with 200 idle clients) 542.86 MB/sec 27.1M
16
+ 200 concurrent clients (RETR 10.0M file) 6.38 secs 32.1M
17
+ 200 concurrent clients (STOR 10.0M file) 2.76 secs 21.7M
18
+ 200 concurrent clients (QUIT) 0.03 secs
19
+
20
+ ### pyftpd 1.4.0
21
+
22
+ (starting with 4.2M of memory being used)
23
+ STOR (client -> server) 127.62 MB/sec 4.2M
24
+ RETR (server -> client) 1170.82 MB/sec 4.3M
25
+ 200 concurrent clients (connect, login) 2.53 secs 4.8M
26
+ STOR (1 file with 200 idle clients) 113.38 MB/sec 4.9M
27
+ RETR (1 file with 200 idle clients) 1139.89 MB/sec 4.9M
28
+ 200 concurrent clients (RETR 10.0M file) 2.55 secs 5.6M
29
+ 200 concurrent clients (STOR 10.0M file) 2.03 secs 5.7M
30
30
  200 concurrent clients (QUIT) 0.02 secs
31
31
 
32
- ### proftpd 1.3.4a-3
32
+ ### proftpd 1.3.5rc4
33
33
 
34
- (starting with 2.3M of RSS memory being used)
35
- STOR (client -> server) 93.06 MB/sec 6.6M RSS
36
- RETR (server -> client) 1267.63 MB/sec 6.6M RSS
37
- 200 concurrent clients (connect, login) 14.21 secs 868.0M RSS
38
- STOR (1 file with 200 idle clients) 76.04 MB/sec 872.3M RSS
39
- RETR (1 file with 200 idle clients) 1289.75 MB/sec 872.3M RSS
40
- 200 concurrent clients (RETR 10.0M file) 2.04 secs 868.0M RSS
41
- 200 concurrent clients (STOR 10.0M file) 4.51 secs 868.2M RSS
34
+ (starting with 1.4M of memory being used)
35
+ STOR (client -> server) 117.59 MB/sec 3.2M
36
+ RETR (server -> client) 1318.32 MB/sec 3.2M
37
+ 200 concurrent clients (connect, login) 12.20 secs 366.4M
38
+ STOR (1 file with 200 idle clients) 123.82 MB/sec 368.3M
39
+ RETR (1 file with 200 idle clients) 1302.86 MB/sec 368.3M
40
+ 200 concurrent clients (RETR 10.0M file) 2.33 secs 366.4M
41
+ 200 concurrent clients (STOR 10.0M file) 2.76 secs 366.4M
42
42
  200 concurrent clients (QUIT) 0.00 secs
43
43
 
44
- ### Discussion
44
+ ### Notes
45
45
 
46
- ftpd's STOR results seen anomalous. I suspect that proftpd and
47
- pyftpdlib aren't getting a fair shake here. proftpd and pyftpdlib are
48
- serving my home directory, whereas ftpd is serving a temporary
49
- directory, but I don't know what difference that could make.
46
+ * Ftpd's fast time on the login test, compared to proftpd and
47
+ pyftpdlib, is probably a result of it not doing PAM authentication
48
+ whereas the other ftpd servers are. It is not an apples-to-apples
49
+ comparison.
50
50
 
51
- Ftpd is a memory hog. During a STOR or RETR, it loads the entire
52
- contents of a file into memory. This limits the number of concurrent
53
- file transfers it can handle. The pyftpd team uses -n 300 (300
54
- concurrent connections) when benchmarking, but Ftpd can't handle that
55
- many at the moment.
56
-
57
- Ftpd's fast time on the login test, compared to proftpd and pyftpdlib,
58
- is a result of it not doing PAM authentication. It is an unfair
59
- comparison and should be disregarded.
60
-
61
- pyftpd's memory footprint is impressive.
62
-
63
- ftpd is less performant with many concurrent connections than either
64
- proftpd or pyftpdlib.
51
+ * The only benchmark for which ftpd beats the competition is the
52
+ single-client STOR.
65
53
 
66
54
  ## Setup
67
55
 
68
56
  ### Machine
69
57
 
70
58
  * Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz (4 cores)
71
- * Python 2.7.3rc2 (default, Apr 22 2012, 22:35:38)
72
- * ruby 2.0.0p0 (2013-02-24 revision 39474) [i686-linux]
59
+ * Python 3.4.1rc1
60
+ * pyftpd 1.4.0
61
+ * ruby 2.1.1p76 (2014-02-24 revision 45161) [i686-linux]
73
62
  * Linux 3.0.0-1-686-pae #1 SMP Sat Aug 27 16:41:03 UTC 2011 i686 GNU/Linux
74
63
 
75
64
  ### Benchmark command
76
65
 
77
66
  bench.py needs psutil. Under Debian, install _python-psutil_.
78
67
 
79
- pyftpdlib-1.0.1/test$ python bench.py -u <USER> -p <PASS> -H localhost -P <PORT> -b all -n 200 -k <PID>
68
+ pyftpdlib-1.0.1/test$ python3 bench.py -u <USER> -p <PASS> -H localhost -P <PORT> -b all -n 200 -k <PID>
80
69
 
81
70
  ### Proftpd
82
71
 
@@ -90,4 +79,4 @@ bench.py needs psutil. Under Debian, install _python-psutil_.
90
79
 
91
80
  ### Pyftpd
92
81
 
93
- pyftpdlib-1.0.0/demo$ sudo python unix_daemon.py 2>/dev/null
82
+ pyftpdlib-1.4.0/demo$ sudo python ./unix_daemon.py 2>/dev/null
@@ -98,21 +98,21 @@ class TestServer
98
98
 
99
99
  def raise_on_file_system_error(method_name)
100
100
  original_method = instance_method(method_name)
101
- define_method method_name do |*args|
101
+ define_method method_name do |*args, &block|
102
102
  ftp_path = args.first
103
103
  if force_file_system_error?(ftp_path)
104
104
  raise Ftpd::PermanentFileSystemError, 'Unable to do it'
105
105
  end
106
- original_method.bind(self).call *args
106
+ original_method.bind(self).call *args, &block
107
107
  end
108
108
  end
109
109
 
110
110
  def return_true_on_file_system_error(method_name)
111
111
  original_method = instance_method(method_name)
112
- define_method method_name do |*args|
112
+ define_method method_name do |*args, &block|
113
113
  ftp_path = args.first
114
114
  return true if force_file_system_error?(ftp_path)
115
- original_method.bind(self).call *args
115
+ original_method.bind(self).call *args, &block
116
116
  end
117
117
  end
118
118
 
data/ftpd.gemspec CHANGED
@@ -2,15 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: ftpd 0.16.0 ruby lib
5
+ # stub: ftpd 0.17.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "ftpd"
9
- s.version = "0.16.0"
9
+ s.version = "0.17.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
12
13
  s.authors = ["Wayne Conrad"]
13
- s.date = "2014-05-19"
14
+ s.date = "2014-06-29"
14
15
  s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, IPV6, passive and active mode, and is unconditionally compliant per RFC-1123. It can be used as part of a test fixture or embedded in a program."
15
16
  s.email = "wconrad@yagni.com"
16
17
  s.extra_rdoc_files = [
@@ -140,7 +141,6 @@ Gem::Specification.new do |s|
140
141
  "ftpd.gemspec",
141
142
  "insecure-test-cert.pem",
142
143
  "lib/ftpd.rb",
143
- "lib/ftpd/ascii_helper.rb",
144
144
  "lib/ftpd/auth_levels.rb",
145
145
  "lib/ftpd/cmd_abor.rb",
146
146
  "lib/ftpd/cmd_allo.rb",
@@ -195,7 +195,6 @@ Gem::Specification.new do |s|
195
195
  "lib/ftpd/exception_translator.rb",
196
196
  "lib/ftpd/exceptions.rb",
197
197
  "lib/ftpd/file_info.rb",
198
- "lib/ftpd/file_system_error_translator.rb",
199
198
  "lib/ftpd/file_system_helper.rb",
200
199
  "lib/ftpd/ftp_server.rb",
201
200
  "lib/ftpd/insecure_certificate.rb",
@@ -208,6 +207,7 @@ Gem::Specification.new do |s|
208
207
  "lib/ftpd/server.rb",
209
208
  "lib/ftpd/session.rb",
210
209
  "lib/ftpd/session_config.rb",
210
+ "lib/ftpd/stream.rb",
211
211
  "lib/ftpd/telnet.rb",
212
212
  "lib/ftpd/temp_dir.rb",
213
213
  "lib/ftpd/tls_server.rb",
@@ -224,7 +224,7 @@ Gem::Specification.new do |s|
224
224
  "spec/disk_file_system_spec.rb",
225
225
  "spec/exception_translator_spec.rb",
226
226
  "spec/file_info_spec.rb",
227
- "spec/file_system_error_translator_spec.rb",
227
+ "spec/ftp_server_error_spec.rb",
228
228
  "spec/list_format/eplf_spec.rb",
229
229
  "spec/list_format/ls_spec.rb",
230
230
  "spec/list_path_spec.rb",
@@ -237,8 +237,7 @@ Gem::Specification.new do |s|
237
237
  ]
238
238
  s.homepage = "http://github.com/wconrad/ftpd"
239
239
  s.licenses = ["MIT"]
240
- s.require_paths = ["lib"]
241
- s.rubygems_version = "2.1.11"
240
+ s.rubygems_version = "2.2.2"
242
241
  s.summary = "Pure Ruby FTP server library"
243
242
 
244
243
  if s.respond_to? :specification_version then
data/lib/ftpd.rb CHANGED
@@ -13,10 +13,7 @@ require 'tmpdir'
13
13
  # Gems
14
14
  require 'memoizer'
15
15
 
16
-
17
16
  # Ftpd
18
-
19
- require_relative 'ftpd/ascii_helper'
20
17
  require_relative 'ftpd/auth_levels'
21
18
  require_relative 'ftpd/cmd_abor'
22
19
  require_relative 'ftpd/cmd_allo'
@@ -70,7 +67,6 @@ require_relative 'ftpd/error'
70
67
  require_relative 'ftpd/exception_translator'
71
68
  require_relative 'ftpd/exceptions'
72
69
  require_relative 'ftpd/file_info'
73
- require_relative 'ftpd/file_system_error_translator'
74
70
  require_relative 'ftpd/file_system_helper'
75
71
  require_relative 'ftpd/ftp_server'
76
72
  require_relative 'ftpd/insecure_certificate'
@@ -83,6 +79,7 @@ require_relative 'ftpd/read_only_disk_file_system'
83
79
  require_relative 'ftpd/server'
84
80
  require_relative 'ftpd/session'
85
81
  require_relative 'ftpd/session_config'
82
+ require_relative 'ftpd/stream'
86
83
  require_relative 'ftpd/telnet'
87
84
  require_relative 'ftpd/temp_dir'
88
85
  require_relative 'ftpd/tls_server'
data/lib/ftpd/cmd_appe.rb CHANGED
@@ -12,8 +12,9 @@ module Ftpd
12
12
  syntax_error unless path
13
13
  path = File.expand_path(path, name_prefix)
14
14
  ensure_accessible path
15
- contents = receive_file
16
- file_system.append path, contents
15
+ receive_file do |data_socket|
16
+ file_system.append path, data_socket
17
+ end
17
18
  reply "226 Transfer complete"
18
19
  end
19
20
  end
data/lib/ftpd/cmd_auth.rb CHANGED
@@ -7,10 +7,10 @@ module Ftpd
7
7
  def cmd_auth(security_scheme)
8
8
  ensure_tls_supported
9
9
  if socket.encrypted?
10
- error "503 AUTH already done"
10
+ error "AUTH already done", 503
11
11
  end
12
12
  unless security_scheme =~ /^TLS(-C)?$/i
13
- error "504 Security scheme not implemented: #{security_scheme}"
13
+ error "Security scheme not implemented: #{security_scheme}", 504
14
14
  end
15
15
  reply "234 AUTH #{security_scheme} OK."
16
16
  socket.encrypt
data/lib/ftpd/cmd_dele.rb CHANGED
@@ -8,7 +8,7 @@ module Ftpd
8
8
  ensure_logged_in
9
9
  ensure_file_system_supports :delete
10
10
  path = argument
11
- error "501 Path required" unless path
11
+ error "Path required", 501 unless path
12
12
  path = File.expand_path(path, name_prefix)
13
13
  ensure_accessible path
14
14
  ensure_exists path
data/lib/ftpd/cmd_list.rb CHANGED
@@ -11,7 +11,7 @@ module Ftpd
11
11
  ensure_file_system_supports :file_info
12
12
  path = list_path(argument)
13
13
  path = File.expand_path(path, name_prefix)
14
- transmit_file(list(path), 'A')
14
+ transmit_file(StringIO.new(list(path)), 'A')
15
15
  end
16
16
  end
17
17
 
data/lib/ftpd/cmd_mode.rb CHANGED
@@ -8,8 +8,8 @@ module Ftpd
8
8
  syntax_error unless argument
9
9
  ensure_logged_in
10
10
  name, implemented = TRANSMISSION_MODES[argument]
11
- error "504 Invalid mode code" unless name
12
- error "504 Mode not implemented" unless implemented
11
+ error "Invalid mode code", 504 unless name
12
+ error "Mode not implemented", 504 unless implemented
13
13
  self.mode = argument
14
14
  reply "200 Mode set to #{name}"
15
15
  end
data/lib/ftpd/cmd_nlst.rb CHANGED
@@ -10,7 +10,7 @@ module Ftpd
10
10
  ensure_file_system_supports :dir
11
11
  path = list_path(argument)
12
12
  path = File.expand_path(path, name_prefix)
13
- transmit_file(name_list(path), 'A')
13
+ transmit_file(StringIO.new(name_list(path)), 'A')
14
14
  end
15
15
  end
16
16
 
data/lib/ftpd/cmd_opts.rb CHANGED
@@ -6,7 +6,7 @@ module Ftpd
6
6
 
7
7
  def cmd_opts(argument)
8
8
  syntax_error unless argument
9
- error '501 Unsupported option'
9
+ error 'Unsupported option', 501
10
10
  end
11
11
 
12
12
  end
data/lib/ftpd/cmd_pbsz.rb CHANGED
@@ -9,10 +9,10 @@ module Ftpd
9
9
  syntax_error unless buffer_size =~ /^\d+$/
10
10
  buffer_size = buffer_size.to_i
11
11
  unless socket.encrypted?
12
- error "503 PBSZ must be preceded by AUTH"
12
+ error "PBSZ must be preceded by AUTH", 503
13
13
  end
14
14
  unless buffer_size == 0
15
- error "501 PBSZ=0"
15
+ error "PBSZ=0", 501
16
16
  end
17
17
  reply "200 PBSZ=0"
18
18
  self.protection_buffer_size_set = true
data/lib/ftpd/cmd_prot.rb CHANGED
@@ -7,14 +7,14 @@ module Ftpd
7
7
  def cmd_prot(level_arg)
8
8
  level_code = level_arg.upcase
9
9
  unless protection_buffer_size_set
10
- error "503 PROT must be preceded by PBSZ"
10
+ error "PROT must be preceded by PBSZ", 503
11
11
  end
12
12
  level = DATA_CHANNEL_PROTECTION_LEVELS[level_code]
13
13
  unless level
14
- error "504 Unknown protection level"
14
+ error "Unknown protection level", 504
15
15
  end
16
16
  unless level == :private
17
- error "536 Unsupported protection level #{level}"
17
+ error "Unsupported protection level #{level}", 536
18
18
  end
19
19
  self.data_channel_protection_level = level
20
20
  reply "200 Data protection level #{level_code}"
data/lib/ftpd/cmd_retr.rb CHANGED
@@ -13,8 +13,9 @@ module Ftpd
13
13
  path = File.expand_path(path, name_prefix)
14
14
  ensure_accessible path
15
15
  ensure_exists path
16
- contents = file_system.read(path)
17
- transmit_file contents
16
+ file_system.read(path) do |file|
17
+ transmit_file file
18
+ end
18
19
  end
19
20
  end
20
21
 
data/lib/ftpd/cmd_size.rb CHANGED
@@ -11,9 +11,17 @@ module Ftpd
11
11
  path = File.expand_path(path, name_prefix)
12
12
  ensure_accessible(path)
13
13
  ensure_exists(path)
14
- contents = file_system.read(path)
15
- contents = (data_type == 'A') ? unix_to_nvt_ascii(contents) : contents
16
- reply "213 #{contents.bytesize}"
14
+ file_system.read(path) do |file|
15
+ if data_type == 'A'
16
+ output = StringIO.new
17
+ io = Ftpd::Stream.new(output, data_type)
18
+ io.write(file)
19
+ size = output.size
20
+ else
21
+ size = file.size
22
+ end
23
+ reply "213 #{size}"
24
+ end
17
25
  end
18
26
 
19
27
  end