ftpd 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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