ftpd 0.17.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/Changelog.md +7 -0
- data/Gemfile +9 -8
- data/Gemfile.lock +66 -55
- data/README.md +4 -2
- data/VERSION +1 -1
- data/features/ftp_server/step_definitions/logging.rb +6 -6
- data/features/step_definitions/client_and_server_files.rb +2 -2
- data/features/step_definitions/client_files.rb +4 -4
- data/features/step_definitions/connect.rb +2 -2
- data/features/step_definitions/directory_navigation.rb +2 -2
- data/features/step_definitions/error_replies.rb +2 -2
- data/features/step_definitions/features.rb +2 -2
- data/features/step_definitions/help.rb +4 -4
- data/features/step_definitions/list.rb +9 -9
- data/features/step_definitions/login.rb +3 -3
- data/features/step_definitions/mtime.rb +1 -1
- data/features/step_definitions/server_files.rb +11 -11
- data/features/step_definitions/server_title.rb +2 -2
- data/features/step_definitions/size.rb +1 -1
- data/features/step_definitions/success_replies.rb +1 -1
- data/features/step_definitions/timing.rb +2 -2
- data/ftpd.gemspec +30 -27
- data/spec/connection_throttle_spec.rb +14 -11
- data/spec/connection_tracker_spec.rb +23 -17
- data/spec/disk_file_system_spec.rb +15 -15
- data/spec/exception_translator_spec.rb +1 -0
- data/spec/file_info_spec.rb +4 -4
- data/spec/list_format/eplf_spec.rb +3 -4
- data/spec/list_format/ls_spec.rb +3 -3
- data/spec/null_logger_spec.rb +1 -1
- data/spec/protocols_spec.rb +8 -8
- data/spec/spec_helper.rb +1 -0
- metadata +54 -40
@@ -4,9 +4,9 @@ Then /^the server returns its title$/ do
|
|
4
4
|
end
|
5
5
|
|
6
6
|
Then /^the server returns its name$/ do
|
7
|
-
@response.
|
7
|
+
expect(@response).to include @server.server_name
|
8
8
|
end
|
9
9
|
|
10
10
|
Then /^the server returns its version$/ do
|
11
|
-
@response.
|
11
|
+
expect(@response).to match /\b\d+\.\d+\.\d+\b/
|
12
12
|
end
|
@@ -9,11 +9,11 @@ end
|
|
9
9
|
Then /^it should take at least (\S+) seconds$/ do |s|
|
10
10
|
min_elapsed_time = s.to_f
|
11
11
|
elapsed_time = Time.now - @start_time
|
12
|
-
elapsed_time.
|
12
|
+
expect(elapsed_time).to be >= min_elapsed_time
|
13
13
|
end
|
14
14
|
|
15
15
|
Then /^it should take less than (\S+) seconds$/ do |s|
|
16
16
|
max_elapsed_time = s.to_f
|
17
17
|
elapsed_time = Time.now - @start_time
|
18
|
-
elapsed_time.
|
18
|
+
expect(elapsed_time).to be < max_elapsed_time
|
19
19
|
end
|
data/ftpd.gemspec
CHANGED
@@ -2,16 +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 1.0.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "ftpd"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "1.0.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Wayne Conrad"]
|
14
|
-
s.date = "2014-
|
14
|
+
s.date = "2014-09-05"
|
15
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."
|
16
16
|
s.email = "wconrad@yagni.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -245,35 +245,38 @@ Gem::Specification.new do |s|
|
|
245
245
|
|
246
246
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
247
247
|
s.add_runtime_dependency(%q<memoizer>, ["~> 1.0"])
|
248
|
-
s.add_development_dependency(%q<cucumber>, ["
|
249
|
-
s.add_development_dependency(%q<double-bag-ftps>, ["
|
250
|
-
s.add_development_dependency(%q<jeweler>, ["
|
251
|
-
s.add_development_dependency(%q<rake>, ["
|
252
|
-
s.add_development_dependency(%q<redcarpet>, ["
|
253
|
-
s.add_development_dependency(%q<rspec>, ["
|
254
|
-
s.add_development_dependency(%q<
|
255
|
-
s.add_development_dependency(%q<
|
248
|
+
s.add_development_dependency(%q<cucumber>, ["~> 1.3.16"])
|
249
|
+
s.add_development_dependency(%q<double-bag-ftps>, ["~> 0.1.2"])
|
250
|
+
s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
|
251
|
+
s.add_development_dependency(%q<rake>, ["~> 10.3.2"])
|
252
|
+
s.add_development_dependency(%q<redcarpet>, ["~> 3.1.2"])
|
253
|
+
s.add_development_dependency(%q<rspec>, ["~> 3.1.0"])
|
254
|
+
s.add_development_dependency(%q<rspec-its>, ["~> 1.0.1"])
|
255
|
+
s.add_development_dependency(%q<timecop>, ["~> 0.7.1"])
|
256
|
+
s.add_development_dependency(%q<yard>, ["~> 0.8.7.4"])
|
256
257
|
else
|
257
258
|
s.add_dependency(%q<memoizer>, ["~> 1.0"])
|
258
|
-
s.add_dependency(%q<cucumber>, ["
|
259
|
-
s.add_dependency(%q<double-bag-ftps>, ["
|
260
|
-
s.add_dependency(%q<jeweler>, ["
|
261
|
-
s.add_dependency(%q<rake>, ["
|
262
|
-
s.add_dependency(%q<redcarpet>, ["
|
263
|
-
s.add_dependency(%q<rspec>, ["
|
264
|
-
s.add_dependency(%q<
|
265
|
-
s.add_dependency(%q<
|
259
|
+
s.add_dependency(%q<cucumber>, ["~> 1.3.16"])
|
260
|
+
s.add_dependency(%q<double-bag-ftps>, ["~> 0.1.2"])
|
261
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
262
|
+
s.add_dependency(%q<rake>, ["~> 10.3.2"])
|
263
|
+
s.add_dependency(%q<redcarpet>, ["~> 3.1.2"])
|
264
|
+
s.add_dependency(%q<rspec>, ["~> 3.1.0"])
|
265
|
+
s.add_dependency(%q<rspec-its>, ["~> 1.0.1"])
|
266
|
+
s.add_dependency(%q<timecop>, ["~> 0.7.1"])
|
267
|
+
s.add_dependency(%q<yard>, ["~> 0.8.7.4"])
|
266
268
|
end
|
267
269
|
else
|
268
270
|
s.add_dependency(%q<memoizer>, ["~> 1.0"])
|
269
|
-
s.add_dependency(%q<cucumber>, ["
|
270
|
-
s.add_dependency(%q<double-bag-ftps>, ["
|
271
|
-
s.add_dependency(%q<jeweler>, ["
|
272
|
-
s.add_dependency(%q<rake>, ["
|
273
|
-
s.add_dependency(%q<redcarpet>, ["
|
274
|
-
s.add_dependency(%q<rspec>, ["
|
275
|
-
s.add_dependency(%q<
|
276
|
-
s.add_dependency(%q<
|
271
|
+
s.add_dependency(%q<cucumber>, ["~> 1.3.16"])
|
272
|
+
s.add_dependency(%q<double-bag-ftps>, ["~> 0.1.2"])
|
273
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
274
|
+
s.add_dependency(%q<rake>, ["~> 10.3.2"])
|
275
|
+
s.add_dependency(%q<redcarpet>, ["~> 3.1.2"])
|
276
|
+
s.add_dependency(%q<rspec>, ["~> 3.1.0"])
|
277
|
+
s.add_dependency(%q<rspec-its>, ["~> 1.0.1"])
|
278
|
+
s.add_dependency(%q<timecop>, ["~> 0.7.1"])
|
279
|
+
s.add_dependency(%q<yard>, ["~> 0.8.7.4"])
|
277
280
|
end
|
278
281
|
end
|
279
282
|
|
@@ -15,13 +15,16 @@ module Ftpd
|
|
15
15
|
end
|
16
16
|
|
17
17
|
before(:each) do
|
18
|
-
connection_tracker.
|
19
|
-
|
18
|
+
allow(connection_tracker).to receive(:connections)
|
19
|
+
.and_return(connections)
|
20
|
+
allow(connection_tracker).to receive(:connections_for)
|
21
|
+
.with(socket)
|
22
|
+
.and_return(connections_for_socket)
|
20
23
|
end
|
21
24
|
|
22
25
|
it 'should have defaults' do
|
23
|
-
connection_throttle.max_connections.
|
24
|
-
connection_throttle.max_connections_per_ip.
|
26
|
+
expect(connection_throttle.max_connections).to be_nil
|
27
|
+
expect(connection_throttle.max_connections_per_ip).to be_nil
|
25
28
|
end
|
26
29
|
|
27
30
|
describe '#allow?' do
|
@@ -37,17 +40,17 @@ module Ftpd
|
|
37
40
|
|
38
41
|
context 'almost at maximum connections' do
|
39
42
|
let(:connections) {max_connections - 1}
|
40
|
-
specify {connection_throttle.allow?(socket).
|
43
|
+
specify {expect(connection_throttle.allow?(socket)).to be_truthy}
|
41
44
|
end
|
42
45
|
|
43
46
|
context 'at maximum connections' do
|
44
47
|
let(:connections) {max_connections}
|
45
|
-
specify {connection_throttle.allow?(socket).
|
48
|
+
specify {expect(connection_throttle.allow?(socket)).to be_falsey}
|
46
49
|
end
|
47
50
|
|
48
51
|
context 'above maximum connections' do
|
49
52
|
let(:connections) {max_connections + 1}
|
50
|
-
specify {connection_throttle.allow?(socket).
|
53
|
+
specify {expect(connection_throttle.allow?(socket)).to be_falsey}
|
51
54
|
end
|
52
55
|
|
53
56
|
end
|
@@ -63,17 +66,17 @@ module Ftpd
|
|
63
66
|
|
64
67
|
context 'almost at maximum connections for ip' do
|
65
68
|
let(:connections_for_socket) {max_connections_per_ip - 1}
|
66
|
-
specify {connection_throttle.allow?(socket).
|
69
|
+
specify {expect(connection_throttle.allow?(socket)).to be_truthy}
|
67
70
|
end
|
68
71
|
|
69
72
|
context 'at maximum connections for ip' do
|
70
73
|
let(:connections_for_socket) {max_connections_per_ip}
|
71
|
-
specify {connection_throttle.allow?(socket).
|
74
|
+
specify {expect(connection_throttle.allow?(socket)).to be_falsey}
|
72
75
|
end
|
73
76
|
|
74
77
|
context 'above maximum connections for ip' do
|
75
78
|
let(:connections_for_socket) {max_connections_per_ip + 1}
|
76
|
-
specify {connection_throttle.allow?(socket).
|
79
|
+
specify {expect(connection_throttle.allow?(socket)).to be_falsey}
|
77
80
|
end
|
78
81
|
|
79
82
|
end
|
@@ -86,7 +89,7 @@ module Ftpd
|
|
86
89
|
|
87
90
|
it 'should send a "too many connections" message' do
|
88
91
|
connection_throttle.deny socket
|
89
|
-
socket.string.
|
92
|
+
expect(socket.string).to eq "421 Too many connections\r\n"
|
90
93
|
end
|
91
94
|
|
92
95
|
end
|
@@ -13,7 +13,7 @@ module Ftpd
|
|
13
13
|
def socket_bound_to(source_ip)
|
14
14
|
socket = double TCPSocket
|
15
15
|
peeraddr = Socket.pack_sockaddr_in(0, source_ip)
|
16
|
-
socket.
|
16
|
+
allow(socket).to receive(:getpeername) {peeraddr}
|
17
17
|
socket
|
18
18
|
end
|
19
19
|
|
@@ -23,6 +23,11 @@ module Ftpd
|
|
23
23
|
|
24
24
|
class Connector
|
25
25
|
|
26
|
+
# Enable the rspec expect syntax in this class.
|
27
|
+
# This uses an internal API of rspec-mock.
|
28
|
+
# See: http://stackoverflow.com/q/25692786/238886
|
29
|
+
RSpec::Mocks::Syntax.enable_expect self
|
30
|
+
|
26
31
|
def initialize(connection_tracker)
|
27
32
|
@connection_tracker = connection_tracker
|
28
33
|
@tracked = Queue.new
|
@@ -39,7 +44,8 @@ module Ftpd
|
|
39
44
|
@tracked.enq :go
|
40
45
|
command = @end_session.deq
|
41
46
|
if command == :close
|
42
|
-
socket.
|
47
|
+
allow(socket).to receive(:getpeername)
|
48
|
+
.and_raise(RuntimeError, "Socket closed")
|
43
49
|
end
|
44
50
|
end
|
45
51
|
@session_ended.enq :go
|
@@ -67,11 +73,11 @@ module Ftpd
|
|
67
73
|
context '(session ends normally)' do
|
68
74
|
|
69
75
|
it 'should track the total number of connection' do
|
70
|
-
connection_tracker.connections.
|
76
|
+
expect(connection_tracker.connections).to eq 0
|
71
77
|
connector.start_session socket
|
72
|
-
connection_tracker.connections.
|
78
|
+
expect(connection_tracker.connections).to eq 1
|
73
79
|
connector.end_session
|
74
|
-
connection_tracker.connections.
|
80
|
+
expect(connection_tracker.connections).to eq 0
|
75
81
|
end
|
76
82
|
|
77
83
|
end
|
@@ -79,11 +85,11 @@ module Ftpd
|
|
79
85
|
context '(socket disconnected during session)' do
|
80
86
|
|
81
87
|
it 'should track the total number of connection' do
|
82
|
-
connection_tracker.connections.
|
88
|
+
expect(connection_tracker.connections).to eq 0
|
83
89
|
connector.start_session socket
|
84
|
-
connection_tracker.connections.
|
90
|
+
expect(connection_tracker.connections).to eq 1
|
85
91
|
connector.end_session :close
|
86
|
-
connection_tracker.connections.
|
92
|
+
expect(connection_tracker.connections).to eq 0
|
87
93
|
end
|
88
94
|
|
89
95
|
end
|
@@ -95,14 +101,14 @@ module Ftpd
|
|
95
101
|
it 'should track the number of connections for an ip' do
|
96
102
|
socket1 = socket_bound_to('127.0.0.1')
|
97
103
|
socket2 = socket_bound_to('127.0.0.2')
|
98
|
-
connection_tracker.connections_for(socket1).
|
99
|
-
connection_tracker.connections_for(socket2).
|
104
|
+
expect(connection_tracker.connections_for(socket1)).to eq 0
|
105
|
+
expect(connection_tracker.connections_for(socket2)).to eq 0
|
100
106
|
connector.start_session socket1
|
101
|
-
connection_tracker.connections_for(socket1).
|
102
|
-
connection_tracker.connections_for(socket2).
|
107
|
+
expect(connection_tracker.connections_for(socket1)).to eq 1
|
108
|
+
expect(connection_tracker.connections_for(socket2)).to eq 0
|
103
109
|
connector.end_session
|
104
|
-
connection_tracker.connections_for(socket1).
|
105
|
-
connection_tracker.connections_for(socket2).
|
110
|
+
expect(connection_tracker.connections_for(socket1)).to eq 0
|
111
|
+
expect(connection_tracker.connections_for(socket2)).to eq 0
|
106
112
|
end
|
107
113
|
|
108
114
|
end
|
@@ -112,11 +118,11 @@ module Ftpd
|
|
112
118
|
let(:socket) {socket_bound_to('127.0.0.1')}
|
113
119
|
|
114
120
|
it 'should forget about an IP that has no connection' do
|
115
|
-
connection_tracker.known_ip_count.
|
121
|
+
expect(connection_tracker.known_ip_count).to eq 0
|
116
122
|
connector.start_session socket
|
117
|
-
connection_tracker.known_ip_count.
|
123
|
+
expect(connection_tracker.known_ip_count).to eq 1
|
118
124
|
connector.end_session
|
119
|
-
connection_tracker.known_ip_count.
|
125
|
+
expect(connection_tracker.known_ip_count).to eq 0
|
120
126
|
end
|
121
127
|
|
122
128
|
end
|
@@ -60,13 +60,13 @@ module Ftpd
|
|
60
60
|
|
61
61
|
context '(within tree)' do
|
62
62
|
specify do
|
63
|
-
disk_file_system.accessible?('file').
|
63
|
+
expect(disk_file_system.accessible?('file')).to be_truthy
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
67
|
context '(outside tree)' do
|
68
68
|
specify do
|
69
|
-
disk_file_system.accessible?('../outside').
|
69
|
+
expect(disk_file_system.accessible?('../outside')).to be_falsey
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -76,13 +76,13 @@ module Ftpd
|
|
76
76
|
|
77
77
|
context '(exists)' do
|
78
78
|
specify do
|
79
|
-
disk_file_system.exists?('file').
|
79
|
+
expect(disk_file_system.exists?('file')).to be_truthy
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
83
|
context '(does not exist)' do
|
84
84
|
specify do
|
85
|
-
disk_file_system.exists?('missing').
|
85
|
+
expect(disk_file_system.exists?('missing')).to be_falsey
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -92,13 +92,13 @@ module Ftpd
|
|
92
92
|
|
93
93
|
context '(directory)' do
|
94
94
|
specify do
|
95
|
-
disk_file_system.directory?('file').
|
95
|
+
expect(disk_file_system.directory?('file')).to be_falsey
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
99
|
context '(file)' do
|
100
100
|
specify do
|
101
|
-
disk_file_system.directory?('dir').
|
101
|
+
expect(disk_file_system.directory?('dir')).to be_truthy
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
@@ -109,7 +109,7 @@ module Ftpd
|
|
109
109
|
context '(success)' do
|
110
110
|
specify do
|
111
111
|
disk_file_system.delete('file')
|
112
|
-
exists?('file').
|
112
|
+
expect(exists?('file')).to be_falsey
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -129,8 +129,8 @@ module Ftpd
|
|
129
129
|
let(:path) {'file'}
|
130
130
|
specify do
|
131
131
|
disk_file_system.read(path) do |file|
|
132
|
-
file.
|
133
|
-
file.read.
|
132
|
+
expect(file).to be_a(IO)
|
133
|
+
expect(file.read).to eq canned_contents(path)
|
134
134
|
end
|
135
135
|
end
|
136
136
|
end
|
@@ -154,7 +154,7 @@ module Ftpd
|
|
154
154
|
let(:path) {'file_path'}
|
155
155
|
specify do
|
156
156
|
disk_file_system.write(path, stream)
|
157
|
-
read_file(path).
|
157
|
+
expect(read_file(path)).to eq contents
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
@@ -177,7 +177,7 @@ module Ftpd
|
|
177
177
|
let(:path) {'file_path'}
|
178
178
|
specify do
|
179
179
|
disk_file_system.append(path, stream)
|
180
|
-
read_file(path).
|
180
|
+
expect(read_file(path)).to eq contents
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
@@ -185,7 +185,7 @@ module Ftpd
|
|
185
185
|
let(:path) {'file'}
|
186
186
|
specify do
|
187
187
|
disk_file_system.append(path, stream)
|
188
|
-
read_file(path).
|
188
|
+
expect(read_file(path)).to eq canned_contents(path) + contents
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
@@ -205,7 +205,7 @@ module Ftpd
|
|
205
205
|
let(:path) {'another_subdir'}
|
206
206
|
specify do
|
207
207
|
disk_file_system.mkdir(path)
|
208
|
-
directory?(path).
|
208
|
+
expect(directory?(path)).to be_truthy
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
@@ -227,8 +227,8 @@ module Ftpd
|
|
227
227
|
context '(success)' do
|
228
228
|
specify do
|
229
229
|
disk_file_system.rename(from_path, to_path)
|
230
|
-
exists?(from_path).
|
231
|
-
exists?(to_path).
|
230
|
+
expect(exists?(from_path)).to be_falsey
|
231
|
+
expect(exists?(to_path)).to be_truthy
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
data/spec/file_info_spec.rb
CHANGED
@@ -29,12 +29,12 @@ module Ftpd
|
|
29
29
|
|
30
30
|
context '(file)' do
|
31
31
|
let(:ftype) {'file'}
|
32
|
-
its(:file?) {should
|
32
|
+
its(:file?) {should be_truthy}
|
33
33
|
end
|
34
34
|
|
35
35
|
context '(directory)' do
|
36
36
|
let(:ftype) {'directory'}
|
37
|
-
its(:file?) {should
|
37
|
+
its(:file?) {should be_falsey}
|
38
38
|
end
|
39
39
|
|
40
40
|
end
|
@@ -45,12 +45,12 @@ module Ftpd
|
|
45
45
|
|
46
46
|
context '(file)' do
|
47
47
|
let(:ftype) {'file'}
|
48
|
-
its(:directory?) {should
|
48
|
+
its(:directory?) {should be_falsey}
|
49
49
|
end
|
50
50
|
|
51
51
|
context '(directory)' do
|
52
52
|
let(:ftype) {'directory'}
|
53
|
-
its(:directory?) {should
|
53
|
+
its(:directory?) {should be_truthy}
|
54
54
|
end
|
55
55
|
|
56
56
|
end
|
@@ -16,7 +16,7 @@ module Ftpd
|
|
16
16
|
subject(:formatter) {Eplf.new(file_info)}
|
17
17
|
|
18
18
|
it 'should produce EPLF format' do
|
19
|
-
formatter.to_s.
|
19
|
+
expect(formatter.to_s).to eq "+r,s1234,m1362299880\tfoo"
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -33,7 +33,7 @@ module Ftpd
|
|
33
33
|
subject(:formatter) {Eplf.new(file_info)}
|
34
34
|
|
35
35
|
it 'should produce EPLF format' do
|
36
|
-
formatter.to_s.
|
36
|
+
expect(formatter.to_s).to eq "+/,m1362299880\tfoo"
|
37
37
|
end
|
38
38
|
|
39
39
|
end
|
@@ -51,8 +51,7 @@ module Ftpd
|
|
51
51
|
subject(:formatter) {Eplf.new(file_info)}
|
52
52
|
|
53
53
|
it 'should produce EPLF format' do
|
54
|
-
formatter.to_s.
|
55
|
-
"+r,s1234,m1362299880,i1234.5678\tfoo"
|
54
|
+
expect(formatter.to_s).to eq "+r,s1234,m1362299880,i1234.5678\tfoo"
|
56
55
|
end
|
57
56
|
|
58
57
|
end
|