ethon 0.5.12 → 0.6.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 +15 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/Guardfile +9 -0
- data/ethon.gemspec +26 -0
- data/lib/ethon/curl.rb +0 -12
- data/lib/ethon/curls/constants.rb +6 -22
- data/lib/ethon/curls/functions.rb +38 -41
- data/lib/ethon/curls/infos.rb +19 -0
- data/lib/ethon/curls/options.rb +416 -219
- data/lib/ethon/curls/settings.rb +1 -0
- data/lib/ethon/easy.rb +12 -18
- data/lib/ethon/easy/callbacks.rb +40 -6
- data/lib/ethon/easy/debug_info.rb +46 -0
- data/lib/ethon/easy/mirror.rb +39 -0
- data/lib/ethon/easy/options.rb +17 -1235
- data/lib/ethon/easy/queryable.rb +6 -8
- data/lib/ethon/easy/response_callbacks.rb +1 -1
- data/lib/ethon/version.rb +1 -1
- data/profile/benchmarks.rb +137 -0
- data/profile/memory_leaks.rb +113 -0
- data/profile/perf_spec_helper.rb +36 -0
- data/profile/support/memory_test_helpers.rb +75 -0
- data/profile/support/os_memory_leak_tracker.rb +47 -0
- data/profile/support/ruby_object_leak_tracker.rb +48 -0
- data/spec/ethon/curl_spec.rb +27 -0
- data/spec/ethon/easy/callbacks_spec.rb +31 -0
- data/spec/ethon/easy/debug_info_spec.rb +52 -0
- data/spec/ethon/easy/form_spec.rb +76 -0
- data/spec/ethon/easy/header_spec.rb +78 -0
- data/spec/ethon/easy/http/custom_spec.rb +176 -0
- data/spec/ethon/easy/http/delete_spec.rb +20 -0
- data/spec/ethon/easy/http/get_spec.rb +89 -0
- data/spec/ethon/easy/http/head_spec.rb +79 -0
- data/spec/ethon/easy/http/options_spec.rb +50 -0
- data/spec/ethon/easy/http/patch_spec.rb +50 -0
- data/spec/ethon/easy/http/post_spec.rb +220 -0
- data/spec/ethon/easy/http/put_spec.rb +124 -0
- data/spec/ethon/easy/http_spec.rb +44 -0
- data/spec/ethon/easy/informations_spec.rb +82 -0
- data/spec/ethon/easy/mirror_spec.rb +39 -0
- data/spec/ethon/easy/operations_spec.rb +251 -0
- data/spec/ethon/easy/options_spec.rb +135 -0
- data/spec/ethon/easy/queryable_spec.rb +188 -0
- data/spec/ethon/easy/response_callbacks_spec.rb +50 -0
- data/spec/ethon/easy/util_spec.rb +27 -0
- data/spec/ethon/easy_spec.rb +105 -0
- data/spec/ethon/libc_spec.rb +13 -0
- data/spec/ethon/loggable_spec.rb +21 -0
- data/spec/ethon/multi/operations_spec.rb +297 -0
- data/spec/ethon/multi/options_spec.rb +70 -0
- data/spec/ethon/multi/stack_spec.rb +79 -0
- data/spec/ethon/multi_spec.rb +21 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/localhost_server.rb +94 -0
- data/spec/support/server.rb +114 -0
- metadata +91 -31
- data/lib/ethon/curls/auth_types.rb +0 -25
- data/lib/ethon/curls/http_versions.rb +0 -22
- data/lib/ethon/curls/postredir.rb +0 -15
- data/lib/ethon/curls/protocols.rb +0 -36
- data/lib/ethon/curls/proxy_types.rb +0 -25
- data/lib/ethon/curls/ssl_versions.rb +0 -23
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ethon::Libc do
|
4
|
+
describe "#getdtablesize" do
|
5
|
+
it "returns an integer" do
|
6
|
+
expect(Ethon::Libc.getdtablesize).to be_a(Integer)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns bigger zero" do
|
10
|
+
expect(Ethon::Libc.getdtablesize).to_not be_zero
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ethon::Loggable do
|
4
|
+
|
5
|
+
describe "#logger=" do
|
6
|
+
|
7
|
+
let(:logger) do
|
8
|
+
Logger.new($stdout).tap do |log|
|
9
|
+
log.level = Logger::INFO
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Ethon.logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sets the logger" do
|
18
|
+
expect(Ethon.logger).to eq(logger)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ethon::Multi::Operations do
|
4
|
+
let(:multi) { Ethon::Multi.new }
|
5
|
+
let(:easy) { Ethon::Easy.new }
|
6
|
+
let(:pointer) { FFI::MemoryPointer.new(:int) }
|
7
|
+
|
8
|
+
describe "#handle" do
|
9
|
+
it "returns a pointer" do
|
10
|
+
expect(multi.handle).to be_a(FFI::Pointer)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#running_count" do
|
15
|
+
context "when hydra has no easy" do
|
16
|
+
it "returns nil" do
|
17
|
+
expect(multi.send(:running_count)).to be_nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when hydra has easy" do
|
22
|
+
before do
|
23
|
+
easy.url = "http://localhost:3001/"
|
24
|
+
multi.add(easy)
|
25
|
+
multi.send(:trigger, pointer)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns 1" do
|
29
|
+
expect(multi.send(:running_count)).to eq(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when hydra has more easys" do
|
34
|
+
let(:another_easy) { Ethon::Easy.new }
|
35
|
+
|
36
|
+
before do
|
37
|
+
easy.url = "http://localhost:3001/"
|
38
|
+
another_easy.url = "http://localhost:3001/"
|
39
|
+
multi.add(easy)
|
40
|
+
multi.add(another_easy)
|
41
|
+
multi.send(:trigger, pointer)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns 2" do
|
45
|
+
expect(multi.send(:running_count)).to eq(2)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#get_timeout" do
|
51
|
+
context "when code ok" do
|
52
|
+
let(:timeout) { 1 }
|
53
|
+
|
54
|
+
before do
|
55
|
+
Ethon::Curl.should_receive(:multi_timeout).and_return(:ok)
|
56
|
+
multi.instance_variable_set(:@timeout, double(:read_long => timeout))
|
57
|
+
end
|
58
|
+
|
59
|
+
it "doesn't raise" do
|
60
|
+
expect{ multi.send(:get_timeout) }.to_not raise_error
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when timeout smaller zero" do
|
64
|
+
let(:timeout) { -1 }
|
65
|
+
|
66
|
+
it "returns 1" do
|
67
|
+
expect(multi.send(:get_timeout)).to eq(1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when timeout bigger or equal zero" do
|
72
|
+
let(:timeout) { 2 }
|
73
|
+
|
74
|
+
it "returns timeout" do
|
75
|
+
expect(multi.send(:get_timeout)).to eq(timeout)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when code not ok" do
|
81
|
+
before { Ethon::Curl.should_receive(:multi_timeout).and_return(:not_ok) }
|
82
|
+
|
83
|
+
it "raises MultiTimeout error" do
|
84
|
+
expect{ multi.send(:get_timeout) }.to raise_error(Ethon::Errors::MultiTimeout)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#set_fds" do
|
90
|
+
let(:timeout) { 1 }
|
91
|
+
let(:max_fd) { 1 }
|
92
|
+
|
93
|
+
context "when code ok" do
|
94
|
+
before { Ethon::Curl.should_receive(:multi_fdset).and_return(:ok) }
|
95
|
+
|
96
|
+
it "doesn't raise" do
|
97
|
+
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error(Ethon::Errors::MultiFdset)
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when max_fd -1" do
|
101
|
+
let(:max_fd) { -1 }
|
102
|
+
|
103
|
+
before do
|
104
|
+
multi.instance_variable_set(:@max_fd, double(:read_int => max_fd))
|
105
|
+
multi.should_receive(:sleep).with(0.001)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "waits 100ms" do
|
109
|
+
multi.method(:set_fds).call(timeout)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when max_fd not -1" do
|
114
|
+
context "when code smaller zero" do
|
115
|
+
before { Ethon::Curl.should_receive(:select).and_return(-1) }
|
116
|
+
|
117
|
+
it "raises Select error" do
|
118
|
+
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::Select)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "when code bigger or equal zero" do
|
123
|
+
before { Ethon::Curl.should_receive(:select).and_return(0) }
|
124
|
+
|
125
|
+
it "doesn't raise" do
|
126
|
+
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error(Ethon::Errors::Select)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when code not ok" do
|
133
|
+
before { Ethon::Curl.should_receive(:multi_fdset).and_return(:not_ok) }
|
134
|
+
|
135
|
+
it "raises MultiFdset error" do
|
136
|
+
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::MultiFdset)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#perform" do
|
142
|
+
context "when no easy handles" do
|
143
|
+
it "returns nil" do
|
144
|
+
expect(multi.perform).to be_nil
|
145
|
+
end
|
146
|
+
|
147
|
+
it "logs" do
|
148
|
+
Ethon.logger.should_receive(:debug).twice
|
149
|
+
multi.perform
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when easy handle" do
|
154
|
+
before do
|
155
|
+
easy.url = "http://localhost:3001/"
|
156
|
+
multi.add(easy)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "requests" do
|
160
|
+
multi.perform
|
161
|
+
end
|
162
|
+
|
163
|
+
it "sets easy" do
|
164
|
+
multi.perform
|
165
|
+
expect(easy.response_code).to eq(200)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when four easy handles" do
|
170
|
+
let(:easies) do
|
171
|
+
ary = []
|
172
|
+
4.times do
|
173
|
+
ary << another_easy = Ethon::Easy.new
|
174
|
+
another_easy.url = "http://localhost:3001/"
|
175
|
+
end
|
176
|
+
ary
|
177
|
+
end
|
178
|
+
|
179
|
+
before do
|
180
|
+
easies.each { |e| multi.add(e) }
|
181
|
+
multi.perform
|
182
|
+
end
|
183
|
+
|
184
|
+
it "sets response codes" do
|
185
|
+
expect(easies.all?{ |e| e.response_code == 200 }).to be_true
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "#ongoing?" do
|
191
|
+
context "when easy_handles" do
|
192
|
+
before { multi.easy_handles << 1 }
|
193
|
+
|
194
|
+
context "when running_count not greater 0" do
|
195
|
+
before { multi.instance_variable_set(:@running_count, 0) }
|
196
|
+
|
197
|
+
it "returns true" do
|
198
|
+
expect(multi.method(:ongoing?).call).to be_true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "when running_count greater 0" do
|
203
|
+
before { multi.instance_variable_set(:@running_count, 1) }
|
204
|
+
|
205
|
+
it "returns true" do
|
206
|
+
expect(multi.method(:ongoing?).call).to be_true
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "when no easy_handles" do
|
212
|
+
context "when running_count not greater 0" do
|
213
|
+
before { multi.instance_variable_set(:@running_count, 0) }
|
214
|
+
|
215
|
+
it "returns false" do
|
216
|
+
expect(multi.method(:ongoing?).call).to be_false
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context "when running_count greater 0" do
|
221
|
+
before { multi.instance_variable_set(:@running_count, 1) }
|
222
|
+
|
223
|
+
it "returns true" do
|
224
|
+
expect(multi.method(:ongoing?).call).to be_true
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#init_vars" do
|
231
|
+
it "sets @timeout" do
|
232
|
+
expect(multi.instance_variable_get(:@timeout)).to be_a(FFI::MemoryPointer)
|
233
|
+
end
|
234
|
+
|
235
|
+
it "sets @timeval" do
|
236
|
+
expect(multi.instance_variable_get(:@timeval)).to be_a(Ethon::Curl::Timeval)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "sets @fd_read" do
|
240
|
+
expect(multi.instance_variable_get(:@fd_read)).to be_a(Ethon::Curl::FDSet)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "sets @fd_write" do
|
244
|
+
expect(multi.instance_variable_get(:@fd_write)).to be_a(Ethon::Curl::FDSet)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "sets @fd_excep" do
|
248
|
+
expect(multi.instance_variable_get(:@fd_excep)).to be_a(Ethon::Curl::FDSet)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "sets @max_fd" do
|
252
|
+
expect(multi.instance_variable_get(:@max_fd)).to be_a(FFI::MemoryPointer)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "#reset_fds" do
|
257
|
+
after { multi.method(:reset_fds).call }
|
258
|
+
|
259
|
+
it "resets @fd_read" do
|
260
|
+
multi.instance_variable_get(:@fd_read).should_receive(:clear)
|
261
|
+
end
|
262
|
+
|
263
|
+
it "resets @fd_write" do
|
264
|
+
multi.instance_variable_get(:@fd_write).should_receive(:clear)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "resets @fd_excep" do
|
268
|
+
multi.instance_variable_get(:@fd_excep).should_receive(:clear)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "#check" do
|
273
|
+
it { pending("untested") }
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "#run" do
|
277
|
+
it { pending("untested") }
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "#trigger" do
|
281
|
+
it "calls multi perform" do
|
282
|
+
Ethon::Curl.should_receive(:multi_perform)
|
283
|
+
multi.send(:trigger, pointer)
|
284
|
+
end
|
285
|
+
|
286
|
+
it "sets running count" do
|
287
|
+
multi.instance_variable_set(:@running_count, nil)
|
288
|
+
multi.send(:trigger, pointer)
|
289
|
+
expect(multi.instance_variable_get(:@running_count)).to_not be_nil
|
290
|
+
end
|
291
|
+
|
292
|
+
it "returns multi perform code" do
|
293
|
+
Ethon::Curl.should_receive(:multi_perform).and_return(:ok)
|
294
|
+
expect(multi.send(:trigger, pointer)).to eq(:ok)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ethon::Multi::Options do
|
4
|
+
let(:multi) { Ethon::Multi.new }
|
5
|
+
|
6
|
+
[
|
7
|
+
:maxconnects, :pipelining, :socketdata, :socketfunction,
|
8
|
+
:timerdata, :timerfunction
|
9
|
+
].each do |name|
|
10
|
+
describe "#{name}=" do
|
11
|
+
it "responds_to" do
|
12
|
+
expect(multi).to respond_to("#{name}=")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "sets option" do
|
16
|
+
Ethon::Curl.should_receive(:set_option).with do |option, _, _|
|
17
|
+
expect(option).to be(name)
|
18
|
+
end
|
19
|
+
multi.method("#{name}=").call(1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#value_for" do
|
25
|
+
context "when option in bool" do
|
26
|
+
context "when value true" do
|
27
|
+
let(:value) { true }
|
28
|
+
|
29
|
+
it "returns 1" do
|
30
|
+
expect(multi.method(:value_for).call(value, :bool)).to eq(1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when value false" do
|
35
|
+
let(:value) { false }
|
36
|
+
|
37
|
+
it "returns 0" do
|
38
|
+
expect(multi.method(:value_for).call(value, :bool)).to eq(0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
context "when value in int" do
|
45
|
+
let(:value) { "2" }
|
46
|
+
|
47
|
+
it "returns value casted to int" do
|
48
|
+
expect(multi.method(:value_for).call(value, :int)).to eq(2)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when value in unspecific_options" do
|
53
|
+
context "when value a string" do
|
54
|
+
let(:value) { "www.example.\0com" }
|
55
|
+
|
56
|
+
it "returns zero byte escaped string" do
|
57
|
+
expect(multi.method(:value_for).call(value, nil)).to eq("www.example.\\0com")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when value not a string" do
|
62
|
+
let(:value) { 1 }
|
63
|
+
|
64
|
+
it "returns value" do
|
65
|
+
expect(multi.method(:value_for).call(value, nil)).to eq(1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ethon::Multi::Stack do
|
4
|
+
let(:multi) { Ethon::Multi.new }
|
5
|
+
let(:easy) { Ethon::Easy.new }
|
6
|
+
|
7
|
+
describe "#add" do
|
8
|
+
context "when easy already added" do
|
9
|
+
before { multi.add(easy) }
|
10
|
+
|
11
|
+
it "returns nil" do
|
12
|
+
expect(multi.add(easy)).to be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when easy new" do
|
17
|
+
it "adds easy to multi" do
|
18
|
+
Ethon::Curl.should_receive(:multi_add_handle).and_return(:ok)
|
19
|
+
multi.add(easy)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "adds easy to easy_handles" do
|
23
|
+
multi.add(easy)
|
24
|
+
expect(multi.easy_handles).to include(easy)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when multi_add_handle fails" do
|
29
|
+
it "raises multi add error" do
|
30
|
+
Ethon::Curl.should_receive(:multi_add_handle).and_return(:bad_easy_handle)
|
31
|
+
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when multi cleaned up before" do
|
36
|
+
it "raises multi add error" do
|
37
|
+
Ethon::Curl.multi_cleanup(multi.handle)
|
38
|
+
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#delete" do
|
44
|
+
context "when easy in easy_handles" do
|
45
|
+
before { multi.add(easy) }
|
46
|
+
|
47
|
+
it "deletes easy from multi" do
|
48
|
+
Ethon::Curl.should_receive(:multi_remove_handle).and_return(:ok)
|
49
|
+
multi.delete(easy)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "deletes easy from easy_handles" do
|
53
|
+
multi.delete(easy)
|
54
|
+
expect(multi.easy_handles).to_not include(easy)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when easy is not in easy_handles" do
|
59
|
+
it "does nothing" do
|
60
|
+
Ethon::Curl.should_receive(:multi_add_handle).and_return(:ok)
|
61
|
+
multi.add(easy)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "adds easy to easy_handles" do
|
65
|
+
multi.add(easy)
|
66
|
+
expect(multi.easy_handles).to include(easy)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when multi_remove_handle fails" do
|
71
|
+
before { multi.add(easy) }
|
72
|
+
|
73
|
+
it "raises multi remove error" do
|
74
|
+
Ethon::Curl.should_receive(:multi_remove_handle).and_return(:bad_easy_handle)
|
75
|
+
expect{ multi.delete(easy) }.to raise_error(Ethon::Errors::MultiRemove)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|