ethon 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 +26 -1
- data/README.md +1 -1
- data/ethon.gemspec +6 -3
- data/lib/ethon/curls/codes.rb +3 -2
- data/lib/ethon/version.rb +1 -1
- metadata +6 -85
- data/.github/workflows/ruby.yml +0 -41
- data/.gitignore +0 -8
- data/.rspec +0 -3
- data/Gemfile +0 -43
- data/Guardfile +0 -10
- data/Rakefile +0 -40
- data/profile/benchmarks.rb +0 -104
- data/profile/memory_leaks.rb +0 -114
- data/profile/perf_spec_helper.rb +0 -37
- data/profile/support/memory_test_helpers.rb +0 -76
- data/profile/support/os_memory_leak_tracker.rb +0 -48
- data/profile/support/ruby_object_leak_tracker.rb +0 -49
- data/spec/ethon/curl_spec.rb +0 -38
- data/spec/ethon/easy/callbacks_spec.rb +0 -81
- data/spec/ethon/easy/debug_info_spec.rb +0 -54
- data/spec/ethon/easy/features_spec.rb +0 -24
- data/spec/ethon/easy/form_spec.rb +0 -104
- data/spec/ethon/easy/header_spec.rb +0 -79
- data/spec/ethon/easy/http/custom_spec.rb +0 -177
- data/spec/ethon/easy/http/delete_spec.rb +0 -21
- data/spec/ethon/easy/http/get_spec.rb +0 -126
- data/spec/ethon/easy/http/head_spec.rb +0 -80
- data/spec/ethon/easy/http/options_spec.rb +0 -51
- data/spec/ethon/easy/http/patch_spec.rb +0 -51
- data/spec/ethon/easy/http/post_spec.rb +0 -317
- data/spec/ethon/easy/http/put_spec.rb +0 -168
- data/spec/ethon/easy/http_spec.rb +0 -64
- data/spec/ethon/easy/informations_spec.rb +0 -126
- data/spec/ethon/easy/mirror_spec.rb +0 -47
- data/spec/ethon/easy/operations_spec.rb +0 -271
- data/spec/ethon/easy/options_spec.rb +0 -193
- data/spec/ethon/easy/queryable_spec.rb +0 -235
- data/spec/ethon/easy/response_callbacks_spec.rb +0 -152
- data/spec/ethon/easy/util_spec.rb +0 -28
- data/spec/ethon/easy_spec.rb +0 -203
- data/spec/ethon/libc_spec.rb +0 -14
- data/spec/ethon/loggable_spec.rb +0 -22
- data/spec/ethon/multi/operations_spec.rb +0 -298
- data/spec/ethon/multi/options_spec.rb +0 -182
- data/spec/ethon/multi/stack_spec.rb +0 -80
- data/spec/ethon/multi_spec.rb +0 -152
- data/spec/spec_helper.rb +0 -28
- data/spec/support/localhost_server.rb +0 -95
- data/spec/support/server.rb +0 -115
@@ -1,298 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi::Operations do
|
5
|
-
let(:multi) { Ethon::Multi.new }
|
6
|
-
let(:easy) { Ethon::Easy.new }
|
7
|
-
let(:pointer) { FFI::MemoryPointer.new(:int) }
|
8
|
-
|
9
|
-
describe "#handle" do
|
10
|
-
it "returns a pointer" do
|
11
|
-
expect(multi.handle).to be_a(FFI::Pointer)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "#running_count" do
|
16
|
-
context "when hydra has no easy" do
|
17
|
-
it "returns nil" do
|
18
|
-
expect(multi.send(:running_count)).to be_nil
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
context "when hydra has easy" do
|
23
|
-
before do
|
24
|
-
easy.url = "http://localhost:3001/"
|
25
|
-
multi.add(easy)
|
26
|
-
multi.send(:trigger, pointer)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "returns 1" do
|
30
|
-
expect(multi.send(:running_count)).to eq(1)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context "when hydra has more easys" do
|
35
|
-
let(:another_easy) { Ethon::Easy.new }
|
36
|
-
|
37
|
-
before do
|
38
|
-
easy.url = "http://localhost:3001/"
|
39
|
-
another_easy.url = "http://localhost:3001/"
|
40
|
-
multi.add(easy)
|
41
|
-
multi.add(another_easy)
|
42
|
-
multi.send(:trigger, pointer)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "returns 2" do
|
46
|
-
expect(multi.send(:running_count)).to eq(2)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe "#get_timeout" do
|
52
|
-
context "when code ok" do
|
53
|
-
let(:timeout) { 1 }
|
54
|
-
|
55
|
-
before do
|
56
|
-
expect(Ethon::Curl).to receive(:multi_timeout).and_return(:ok)
|
57
|
-
multi.instance_variable_set(:@timeout, double(:read_long => timeout))
|
58
|
-
end
|
59
|
-
|
60
|
-
it "doesn't raise" do
|
61
|
-
expect{ multi.send(:get_timeout) }.to_not raise_error
|
62
|
-
end
|
63
|
-
|
64
|
-
context "when timeout smaller zero" do
|
65
|
-
let(:timeout) { -1 }
|
66
|
-
|
67
|
-
it "returns 1" do
|
68
|
-
expect(multi.send(:get_timeout)).to eq(1)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context "when timeout bigger or equal zero" do
|
73
|
-
let(:timeout) { 2 }
|
74
|
-
|
75
|
-
it "returns timeout" do
|
76
|
-
expect(multi.send(:get_timeout)).to eq(timeout)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context "when code not ok" do
|
82
|
-
before { expect(Ethon::Curl).to receive(:multi_timeout).and_return(:not_ok) }
|
83
|
-
|
84
|
-
it "raises MultiTimeout error" do
|
85
|
-
expect{ multi.send(:get_timeout) }.to raise_error(Ethon::Errors::MultiTimeout)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe "#set_fds" do
|
91
|
-
let(:timeout) { 1 }
|
92
|
-
let(:max_fd) { 1 }
|
93
|
-
|
94
|
-
context "when code ok" do
|
95
|
-
before { expect(Ethon::Curl).to receive(:multi_fdset).and_return(:ok) }
|
96
|
-
|
97
|
-
it "doesn't raise" do
|
98
|
-
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error
|
99
|
-
end
|
100
|
-
|
101
|
-
context "when max_fd -1" do
|
102
|
-
let(:max_fd) { -1 }
|
103
|
-
|
104
|
-
before do
|
105
|
-
multi.instance_variable_set(:@max_fd, double(:read_int => max_fd))
|
106
|
-
expect(multi).to receive(:sleep).with(0.001)
|
107
|
-
end
|
108
|
-
|
109
|
-
it "waits 100ms" do
|
110
|
-
multi.method(:set_fds).call(timeout)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
context "when max_fd not -1" do
|
115
|
-
context "when code smaller zero" do
|
116
|
-
before { expect(Ethon::Curl).to receive(:select).and_return(-1) }
|
117
|
-
|
118
|
-
it "raises Select error" do
|
119
|
-
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::Select)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
context "when code bigger or equal zero" do
|
124
|
-
before { expect(Ethon::Curl).to receive(:select).and_return(0) }
|
125
|
-
|
126
|
-
it "doesn't raise" do
|
127
|
-
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
context "when code not ok" do
|
134
|
-
before { expect(Ethon::Curl).to receive(:multi_fdset).and_return(:not_ok) }
|
135
|
-
|
136
|
-
it "raises MultiFdset error" do
|
137
|
-
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::MultiFdset)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe "#perform" do
|
143
|
-
context "when no easy handles" do
|
144
|
-
it "returns nil" do
|
145
|
-
expect(multi.perform).to be_nil
|
146
|
-
end
|
147
|
-
|
148
|
-
it "logs" do
|
149
|
-
expect(Ethon.logger).to receive(:debug).twice
|
150
|
-
multi.perform
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
context "when easy handle" do
|
155
|
-
before do
|
156
|
-
easy.url = "http://localhost:3001/"
|
157
|
-
multi.add(easy)
|
158
|
-
end
|
159
|
-
|
160
|
-
it "requests" do
|
161
|
-
multi.perform
|
162
|
-
end
|
163
|
-
|
164
|
-
it "sets easy" do
|
165
|
-
multi.perform
|
166
|
-
expect(easy.response_code).to eq(200)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
context "when four easy handles" do
|
171
|
-
let(:easies) do
|
172
|
-
ary = []
|
173
|
-
4.times do
|
174
|
-
ary << another_easy = Ethon::Easy.new
|
175
|
-
another_easy.url = "http://localhost:3001/"
|
176
|
-
end
|
177
|
-
ary
|
178
|
-
end
|
179
|
-
|
180
|
-
before do
|
181
|
-
easies.each { |e| multi.add(e) }
|
182
|
-
multi.perform
|
183
|
-
end
|
184
|
-
|
185
|
-
it "sets response codes" do
|
186
|
-
expect(easies.all?{ |e| e.response_code == 200 }).to be_truthy
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
describe "#ongoing?" do
|
192
|
-
context "when easy_handles" do
|
193
|
-
before { multi.easy_handles << 1 }
|
194
|
-
|
195
|
-
context "when running_count not greater 0" do
|
196
|
-
before { multi.instance_variable_set(:@running_count, 0) }
|
197
|
-
|
198
|
-
it "returns true" do
|
199
|
-
expect(multi.method(:ongoing?).call).to be_truthy
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
context "when running_count greater 0" do
|
204
|
-
before { multi.instance_variable_set(:@running_count, 1) }
|
205
|
-
|
206
|
-
it "returns true" do
|
207
|
-
expect(multi.method(:ongoing?).call).to be_truthy
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
context "when no easy_handles" do
|
213
|
-
context "when running_count not greater 0" do
|
214
|
-
before { multi.instance_variable_set(:@running_count, 0) }
|
215
|
-
|
216
|
-
it "returns false" do
|
217
|
-
expect(multi.method(:ongoing?).call).to be_falsey
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
context "when running_count greater 0" do
|
222
|
-
before { multi.instance_variable_set(:@running_count, 1) }
|
223
|
-
|
224
|
-
it "returns true" do
|
225
|
-
expect(multi.method(:ongoing?).call).to be_truthy
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
describe "#init_vars" do
|
232
|
-
it "sets @timeout" do
|
233
|
-
expect(multi.instance_variable_get(:@timeout)).to be_a(FFI::MemoryPointer)
|
234
|
-
end
|
235
|
-
|
236
|
-
it "sets @timeval" do
|
237
|
-
expect(multi.instance_variable_get(:@timeval)).to be_a(Ethon::Curl::Timeval)
|
238
|
-
end
|
239
|
-
|
240
|
-
it "sets @fd_read" do
|
241
|
-
expect(multi.instance_variable_get(:@fd_read)).to be_a(Ethon::Curl::FDSet)
|
242
|
-
end
|
243
|
-
|
244
|
-
it "sets @fd_write" do
|
245
|
-
expect(multi.instance_variable_get(:@fd_write)).to be_a(Ethon::Curl::FDSet)
|
246
|
-
end
|
247
|
-
|
248
|
-
it "sets @fd_excep" do
|
249
|
-
expect(multi.instance_variable_get(:@fd_excep)).to be_a(Ethon::Curl::FDSet)
|
250
|
-
end
|
251
|
-
|
252
|
-
it "sets @max_fd" do
|
253
|
-
expect(multi.instance_variable_get(:@max_fd)).to be_a(FFI::MemoryPointer)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
describe "#reset_fds" do
|
258
|
-
after { multi.method(:reset_fds).call }
|
259
|
-
|
260
|
-
it "resets @fd_read" do
|
261
|
-
expect(multi.instance_variable_get(:@fd_read)).to receive(:clear)
|
262
|
-
end
|
263
|
-
|
264
|
-
it "resets @fd_write" do
|
265
|
-
expect(multi.instance_variable_get(:@fd_write)).to receive(:clear)
|
266
|
-
end
|
267
|
-
|
268
|
-
it "resets @fd_excep" do
|
269
|
-
expect(multi.instance_variable_get(:@fd_excep)).to receive(:clear)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
describe "#check" do
|
274
|
-
it { skip("untested") }
|
275
|
-
end
|
276
|
-
|
277
|
-
describe "#run" do
|
278
|
-
it { skip("untested") }
|
279
|
-
end
|
280
|
-
|
281
|
-
describe "#trigger" do
|
282
|
-
it "calls multi perform" do
|
283
|
-
expect(Ethon::Curl).to receive(:multi_perform)
|
284
|
-
multi.send(:trigger, pointer)
|
285
|
-
end
|
286
|
-
|
287
|
-
it "sets running count" do
|
288
|
-
multi.instance_variable_set(:@running_count, nil)
|
289
|
-
multi.send(:trigger, pointer)
|
290
|
-
expect(multi.instance_variable_get(:@running_count)).to_not be_nil
|
291
|
-
end
|
292
|
-
|
293
|
-
it "returns multi perform code" do
|
294
|
-
expect(Ethon::Curl).to receive(:multi_perform).and_return(:ok)
|
295
|
-
expect(multi.send(:trigger, pointer)).to eq(:ok)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
@@ -1,182 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi::Options do
|
5
|
-
let(:multi) { Ethon::Multi.new }
|
6
|
-
|
7
|
-
[
|
8
|
-
:maxconnects, :pipelining, :socketdata, :socketfunction,
|
9
|
-
:timerdata, :timerfunction, :max_total_connections
|
10
|
-
].each do |name|
|
11
|
-
describe "#{name}=" do
|
12
|
-
it "responds_to" do
|
13
|
-
expect(multi).to respond_to("#{name}=")
|
14
|
-
end
|
15
|
-
|
16
|
-
it "sets option" do
|
17
|
-
expect(Ethon::Curl).to receive(:set_option).with(name, anything, anything, anything)
|
18
|
-
multi.method("#{name}=").call(1)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "socket_action mode" do
|
24
|
-
let(:multi) { Ethon::Multi.new(execution_mode: :socket_action) }
|
25
|
-
|
26
|
-
describe "#socketfunction callbacks" do
|
27
|
-
it "allows multi_code return values" do
|
28
|
-
calls = []
|
29
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
30
|
-
calls << what
|
31
|
-
:ok
|
32
|
-
end
|
33
|
-
|
34
|
-
easy = Ethon::Easy.new
|
35
|
-
easy.url = "http://localhost:3001/?delay=1"
|
36
|
-
multi.add(easy)
|
37
|
-
expect(calls).to eq([])
|
38
|
-
5.times do
|
39
|
-
multi.socket_action
|
40
|
-
break unless calls.empty?
|
41
|
-
sleep 0.1
|
42
|
-
end
|
43
|
-
expect(calls.last).to eq(:in).or(eq(:out))
|
44
|
-
multi.delete(easy)
|
45
|
-
expect(calls.last).to eq(:remove)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "allows integer return values (compatibility)" do
|
49
|
-
called = false
|
50
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
51
|
-
called = true
|
52
|
-
0
|
53
|
-
end
|
54
|
-
|
55
|
-
easy = Ethon::Easy.new
|
56
|
-
easy.url = "http://localhost:3001/?delay=1"
|
57
|
-
multi.add(easy)
|
58
|
-
5.times do
|
59
|
-
multi.socket_action
|
60
|
-
break if called
|
61
|
-
sleep 0.1
|
62
|
-
end
|
63
|
-
multi.delete(easy)
|
64
|
-
|
65
|
-
expect(called).to be_truthy
|
66
|
-
end
|
67
|
-
|
68
|
-
it "errors on invalid return codes" do
|
69
|
-
called = false
|
70
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
71
|
-
called = true
|
72
|
-
"hi"
|
73
|
-
end
|
74
|
-
|
75
|
-
easy = Ethon::Easy.new
|
76
|
-
easy.url = "http://localhost:3001/?delay=1"
|
77
|
-
multi.add(easy)
|
78
|
-
expect {
|
79
|
-
5.times do
|
80
|
-
multi.socket_action
|
81
|
-
break if called
|
82
|
-
sleep 0.1
|
83
|
-
end
|
84
|
-
}.to raise_error(ArgumentError)
|
85
|
-
expect { multi.delete(easy) }.to raise_error(ArgumentError)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "#timerfunction callbacks" do
|
90
|
-
it "allows multi_code return values" do
|
91
|
-
calls = []
|
92
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
93
|
-
calls << timeout_ms
|
94
|
-
:ok
|
95
|
-
end
|
96
|
-
|
97
|
-
easy = Ethon::Easy.new
|
98
|
-
easy.url = "http://localhost:3001/?delay=1"
|
99
|
-
multi.add(easy)
|
100
|
-
expect(calls.last).to be >= 0 # adds an immediate timeout
|
101
|
-
|
102
|
-
multi.delete(easy)
|
103
|
-
expect(calls.last).to eq(-1) # cancels the timer
|
104
|
-
end
|
105
|
-
|
106
|
-
it "allows integer return values (compatibility)" do
|
107
|
-
called = false
|
108
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
109
|
-
called = true
|
110
|
-
0
|
111
|
-
end
|
112
|
-
|
113
|
-
easy = Ethon::Easy.new
|
114
|
-
easy.url = "http://localhost:3001/?delay=1"
|
115
|
-
multi.add(easy)
|
116
|
-
multi.socket_action
|
117
|
-
multi.delete(easy)
|
118
|
-
|
119
|
-
expect(called).to be_truthy
|
120
|
-
end
|
121
|
-
|
122
|
-
it "errors on invalid return codes" do
|
123
|
-
called = false
|
124
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
125
|
-
called = true
|
126
|
-
"hi"
|
127
|
-
end
|
128
|
-
|
129
|
-
easy = Ethon::Easy.new
|
130
|
-
easy.url = "http://localhost:3001/?delay=1"
|
131
|
-
expect { multi.add(easy) }.to raise_error(ArgumentError)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "#value_for" do
|
137
|
-
context "when option in bool" do
|
138
|
-
context "when value true" do
|
139
|
-
let(:value) { true }
|
140
|
-
|
141
|
-
it "returns 1" do
|
142
|
-
expect(multi.method(:value_for).call(value, :bool)).to eq(1)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
context "when value false" do
|
147
|
-
let(:value) { false }
|
148
|
-
|
149
|
-
it "returns 0" do
|
150
|
-
expect(multi.method(:value_for).call(value, :bool)).to eq(0)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
context "when value in int" do
|
157
|
-
let(:value) { "2" }
|
158
|
-
|
159
|
-
it "returns value casted to int" do
|
160
|
-
expect(multi.method(:value_for).call(value, :int)).to eq(2)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
context "when value in unspecific_options" do
|
165
|
-
context "when value a string" do
|
166
|
-
let(:value) { "www.example.\0com" }
|
167
|
-
|
168
|
-
it "returns zero byte escaped string" do
|
169
|
-
expect(multi.method(:value_for).call(value, nil)).to eq("www.example.\\0com")
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
context "when value not a string" do
|
174
|
-
let(:value) { 1 }
|
175
|
-
|
176
|
-
it "returns value" do
|
177
|
-
expect(multi.method(:value_for).call(value, nil)).to eq(1)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi::Stack do
|
5
|
-
let(:multi) { Ethon::Multi.new }
|
6
|
-
let(:easy) { Ethon::Easy.new }
|
7
|
-
|
8
|
-
describe "#add" do
|
9
|
-
context "when easy already added" do
|
10
|
-
before { multi.add(easy) }
|
11
|
-
|
12
|
-
it "returns nil" do
|
13
|
-
expect(multi.add(easy)).to be_nil
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "when easy new" do
|
18
|
-
it "adds easy to multi" do
|
19
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
20
|
-
multi.add(easy)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "adds easy to easy_handles" do
|
24
|
-
multi.add(easy)
|
25
|
-
expect(multi.easy_handles).to include(easy)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context "when multi_add_handle fails" do
|
30
|
-
it "raises multi add error" do
|
31
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:bad_easy_handle)
|
32
|
-
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "when multi cleaned up before" do
|
37
|
-
it "raises multi add error" do
|
38
|
-
Ethon::Curl.multi_cleanup(multi.handle)
|
39
|
-
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "#delete" do
|
45
|
-
context "when easy in easy_handles" do
|
46
|
-
before { multi.add(easy) }
|
47
|
-
|
48
|
-
it "deletes easy from multi" do
|
49
|
-
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:ok)
|
50
|
-
multi.delete(easy)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "deletes easy from easy_handles" do
|
54
|
-
multi.delete(easy)
|
55
|
-
expect(multi.easy_handles).to_not include(easy)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "when easy is not in easy_handles" do
|
60
|
-
it "does nothing" do
|
61
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
62
|
-
multi.add(easy)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "adds easy to easy_handles" do
|
66
|
-
multi.add(easy)
|
67
|
-
expect(multi.easy_handles).to include(easy)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
context "when multi_remove_handle fails" do
|
72
|
-
before { multi.add(easy) }
|
73
|
-
|
74
|
-
it "raises multi remove error" do
|
75
|
-
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:bad_easy_handle)
|
76
|
-
expect{ multi.delete(easy) }.to raise_error(Ethon::Errors::MultiRemove)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
data/spec/ethon/multi_spec.rb
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi do
|
5
|
-
describe ".new" do
|
6
|
-
it "inits curl" do
|
7
|
-
expect(Ethon::Curl).to receive(:init)
|
8
|
-
Ethon::Multi.new
|
9
|
-
end
|
10
|
-
|
11
|
-
context "with default options" do
|
12
|
-
it "allows running #perform with the default execution_mode" do
|
13
|
-
Ethon::Multi.new.perform
|
14
|
-
end
|
15
|
-
|
16
|
-
it "refuses to run #socket_action" do
|
17
|
-
expect { Ethon::Multi.new.socket_action }.to raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context "when options not empty" do
|
22
|
-
context "when pipelining is set" do
|
23
|
-
let(:options) { { :pipelining => true } }
|
24
|
-
|
25
|
-
it "sets pipelining" do
|
26
|
-
expect_any_instance_of(Ethon::Multi).to receive(:pipelining=).with(true)
|
27
|
-
Ethon::Multi.new(options)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context "when execution_mode option is :socket_action" do
|
32
|
-
let(:options) { { :execution_mode => :socket_action } }
|
33
|
-
let(:multi) { Ethon::Multi.new(options) }
|
34
|
-
|
35
|
-
it "refuses to run #perform" do
|
36
|
-
expect { multi.perform }.to raise_error(ArgumentError)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "allows running #socket_action" do
|
40
|
-
multi.socket_action
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe "#socket_action" do
|
47
|
-
let(:options) { { :execution_mode => :socket_action } }
|
48
|
-
let(:select_state) { { :readers => [], :writers => [], :timeout => 0 } }
|
49
|
-
let(:multi) {
|
50
|
-
multi = Ethon::Multi.new(options)
|
51
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
52
|
-
timeout_ms = nil if timeout_ms == -1
|
53
|
-
select_state[:timeout] = timeout_ms
|
54
|
-
:ok
|
55
|
-
end
|
56
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
57
|
-
case what
|
58
|
-
when :remove
|
59
|
-
select_state[:readers].delete(sock)
|
60
|
-
select_state[:writers].delete(sock)
|
61
|
-
when :in
|
62
|
-
select_state[:readers].push(sock) unless select_state[:readers].include? sock
|
63
|
-
select_state[:writers].delete(sock)
|
64
|
-
when :out
|
65
|
-
select_state[:readers].delete(sock)
|
66
|
-
select_state[:writers].push(sock) unless select_state[:writers].include? sock
|
67
|
-
when :inout
|
68
|
-
select_state[:readers].push(sock) unless select_state[:readers].include? sock
|
69
|
-
select_state[:writers].push(sock) unless select_state[:writers].include? sock
|
70
|
-
else
|
71
|
-
raise ArgumentError, "invalid value for 'what' in socketfunction callback"
|
72
|
-
end
|
73
|
-
:ok
|
74
|
-
end
|
75
|
-
multi
|
76
|
-
}
|
77
|
-
|
78
|
-
def fds_to_ios(fds)
|
79
|
-
fds.map do |fd|
|
80
|
-
IO.for_fd(fd).tap { |io| io.autoclose = false }
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def perform_socket_action_until_complete
|
85
|
-
multi.socket_action # start things off
|
86
|
-
|
87
|
-
while multi.ongoing?
|
88
|
-
readers, writers, _ = IO.select(
|
89
|
-
fds_to_ios(select_state[:readers]),
|
90
|
-
fds_to_ios(select_state[:writers]),
|
91
|
-
[],
|
92
|
-
select_state[:timeout]
|
93
|
-
)
|
94
|
-
|
95
|
-
to_notify = Hash.new { |hash, key| hash[key] = [] }
|
96
|
-
unless readers.nil?
|
97
|
-
readers.each do |reader|
|
98
|
-
to_notify[reader] << :in
|
99
|
-
end
|
100
|
-
end
|
101
|
-
unless writers.nil?
|
102
|
-
writers.each do |writer|
|
103
|
-
to_notify[writer] << :out
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
to_notify.each do |io, readiness|
|
108
|
-
multi.socket_action(io, readiness)
|
109
|
-
end
|
110
|
-
|
111
|
-
# if we didn't have anything to notify, then we timed out
|
112
|
-
multi.socket_action if to_notify.empty?
|
113
|
-
end
|
114
|
-
ensure
|
115
|
-
multi.easy_handles.dup.each do |h|
|
116
|
-
multi.delete(h)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
it "supports an end-to-end request" do
|
121
|
-
easy = Ethon::Easy.new
|
122
|
-
easy.url = "http://localhost:3001/"
|
123
|
-
multi.add(easy)
|
124
|
-
|
125
|
-
perform_socket_action_until_complete
|
126
|
-
|
127
|
-
expect(multi.ongoing?).to eq(false)
|
128
|
-
end
|
129
|
-
|
130
|
-
it "supports multiple concurrent requests" do
|
131
|
-
handles = []
|
132
|
-
10.times do
|
133
|
-
easy = Ethon::Easy.new
|
134
|
-
easy.url = "http://localhost:3001/?delay=1"
|
135
|
-
multi.add(easy)
|
136
|
-
handles << easy
|
137
|
-
end
|
138
|
-
|
139
|
-
start = Time.now
|
140
|
-
perform_socket_action_until_complete
|
141
|
-
duration = Time.now - start
|
142
|
-
|
143
|
-
# these should have happened concurrently
|
144
|
-
expect(duration).to be < 2
|
145
|
-
expect(multi.ongoing?).to eq(false)
|
146
|
-
|
147
|
-
handles.each do |handle|
|
148
|
-
expect(handle.response_code).to eq(200)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|