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 +4 -4
- data/Changelog.md +24 -1
- data/README.md +1 -0
- data/VERSION +1 -1
- data/doc/benchmarks.md +43 -54
- data/features/support/test_server.rb +4 -4
- data/ftpd.gemspec +7 -8
- data/lib/ftpd.rb +1 -4
- data/lib/ftpd/cmd_appe.rb +3 -2
- data/lib/ftpd/cmd_auth.rb +2 -2
- data/lib/ftpd/cmd_dele.rb +1 -1
- data/lib/ftpd/cmd_list.rb +1 -1
- data/lib/ftpd/cmd_mode.rb +2 -2
- data/lib/ftpd/cmd_nlst.rb +1 -1
- data/lib/ftpd/cmd_opts.rb +1 -1
- data/lib/ftpd/cmd_pbsz.rb +2 -2
- data/lib/ftpd/cmd_prot.rb +3 -3
- data/lib/ftpd/cmd_retr.rb +3 -2
- data/lib/ftpd/cmd_size.rb +11 -3
- data/lib/ftpd/cmd_stor.rb +3 -2
- data/lib/ftpd/cmd_stou.rb +3 -2
- data/lib/ftpd/cmd_stru.rb +2 -2
- data/lib/ftpd/cmd_type.rb +2 -2
- data/lib/ftpd/command_handler.rb +0 -1
- data/lib/ftpd/command_loop.rb +3 -3
- data/lib/ftpd/command_sequence_checker.rb +1 -1
- data/lib/ftpd/data_connection_helper.rb +9 -10
- data/lib/ftpd/disk_file_system.rb +21 -12
- data/lib/ftpd/error.rb +5 -17
- data/lib/ftpd/exceptions.rb +45 -10
- data/lib/ftpd/file_system_helper.rb +4 -4
- data/lib/ftpd/session.rb +8 -8
- data/lib/ftpd/stream.rb +80 -0
- data/lib/ftpd/translate_exceptions.rb +3 -3
- data/spec/command_sequence_checker_spec.rb +6 -4
- data/spec/disk_file_system_spec.rb +12 -7
- data/spec/ftp_server_error_spec.rb +13 -0
- metadata +27 -28
- data/lib/ftpd/ascii_helper.rb +0 -16
- data/lib/ftpd/file_system_error_translator.rb +0 -29
- data/spec/file_system_error_translator_spec.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15d6cd56c8148e7961d07ebb86246fb3c1fe7a25
|
4
|
+
data.tar.gz: 54b088a8bee0cfe945377d9dd97b5ef1bcf97650
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
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.
|
9
|
-
|
10
|
-
(starting with
|
11
|
-
STOR (client -> server)
|
12
|
-
RETR (server -> client)
|
13
|
-
200 concurrent clients (connect, login) 0.
|
14
|
-
STOR (1 file with 200 idle clients)
|
15
|
-
RETR (1 file with 200 idle clients)
|
16
|
-
200 concurrent clients (RETR 10.0M file)
|
17
|
-
200 concurrent clients (STOR 10.0M file)
|
18
|
-
200 concurrent clients (QUIT) 0.
|
19
|
-
|
20
|
-
###
|
21
|
-
|
22
|
-
(starting with
|
23
|
-
STOR (client -> server)
|
24
|
-
RETR (server -> client)
|
25
|
-
200 concurrent clients (connect, login) 2.
|
26
|
-
STOR (1 file with 200 idle clients)
|
27
|
-
RETR (1 file with 200 idle clients)
|
28
|
-
200 concurrent clients (RETR 10.0M file) 2.
|
29
|
-
200 concurrent clients (STOR 10.0M file)
|
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.
|
32
|
+
### proftpd 1.3.5rc4
|
33
33
|
|
34
|
-
(starting with
|
35
|
-
STOR (client -> server)
|
36
|
-
RETR (server -> client)
|
37
|
-
200 concurrent clients (connect, login)
|
38
|
-
STOR (1 file with 200 idle clients)
|
39
|
-
RETR (1 file with 200 idle clients)
|
40
|
-
200 concurrent clients (RETR 10.0M file) 2.
|
41
|
-
200 concurrent clients (STOR 10.0M file)
|
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
|
-
###
|
44
|
+
### Notes
|
45
45
|
|
46
|
-
|
47
|
-
pyftpdlib
|
48
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
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
|
72
|
-
*
|
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$
|
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.
|
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.
|
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.
|
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-
|
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/
|
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.
|
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
|
-
|
16
|
-
|
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 "
|
10
|
+
error "AUTH already done", 503
|
11
11
|
end
|
12
12
|
unless security_scheme =~ /^TLS(-C)?$/i
|
13
|
-
error "
|
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 "
|
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
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 "
|
12
|
-
error "
|
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
data/lib/ftpd/cmd_opts.rb
CHANGED
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 "
|
12
|
+
error "PBSZ must be preceded by AUTH", 503
|
13
13
|
end
|
14
14
|
unless buffer_size == 0
|
15
|
-
error "
|
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 "
|
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 "
|
14
|
+
error "Unknown protection level", 504
|
15
15
|
end
|
16
16
|
unless level == :private
|
17
|
-
error "
|
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
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
|
-
|
15
|
-
|
16
|
-
|
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
|