curb 0.8.5 → 1.3.6
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 +7 -0
- data/README.md +579 -0
- data/Rakefile +85 -27
- data/doc.rb +48 -8
- data/ext/banned.h +32 -0
- data/ext/curb.c +563 -233
- data/ext/curb.h +19 -10
- data/ext/curb_easy.c +3229 -368
- data/ext/curb_easy.h +42 -0
- data/ext/curb_errors.c +117 -18
- data/ext/curb_errors.h +9 -5
- data/ext/curb_macros.h +33 -21
- data/ext/curb_multi.c +1904 -272
- data/ext/curb_multi.h +11 -3
- data/ext/curb_postfield.c +149 -77
- data/ext/curb_postfield.h +1 -0
- data/ext/curb_upload.c +38 -11
- data/ext/curb_upload.h +2 -0
- data/ext/extconf.rb +355 -35
- data/lib/curb.rb +1 -0
- data/lib/curl/download.rb +160 -0
- data/lib/curl/easy.rb +422 -88
- data/lib/curl/multi.rb +295 -56
- data/lib/curl.rb +676 -11
- data/tests/bug_crash_on_debug.rb +14 -28
- data/tests/bug_crash_on_progress.rb +32 -16
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +10 -15
- data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +6 -30
- data/tests/bug_follow_redirect_288.rb +83 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +3 -5
- data/tests/bug_issue102.rb +17 -0
- data/tests/bug_issue_noproxy.rb +56 -0
- data/tests/bug_issue_post_redirect.rb +93 -0
- data/tests/bug_issue_spnego.rb +41 -0
- data/tests/bug_multi_segfault.rb +1 -0
- data/tests/bug_poison.rb +29 -0
- data/tests/bug_raise_on_callback.rb +30 -0
- data/tests/helper.rb +400 -44
- data/tests/leak_trace.rb +237 -0
- data/tests/mem_check.rb +3 -0
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +98 -7
- data/tests/tc_curl_easy.rb +985 -66
- data/tests/tc_curl_easy_cookielist.rb +277 -0
- data/tests/tc_curl_easy_request_target.rb +41 -0
- data/tests/tc_curl_easy_resolve.rb +48 -0
- data/tests/tc_curl_maxfilesize.rb +212 -0
- data/tests/tc_curl_multi.rb +1111 -51
- data/tests/tc_curl_native_coverage.rb +145 -0
- data/tests/tc_curl_network_policy.rb +1475 -0
- data/tests/tc_curl_postfield.rb +207 -30
- data/tests/tc_curl_protocols.rb +388 -0
- data/tests/tc_fiber_scheduler.rb +584 -0
- data/tests/tc_ftp_options.rb +39 -0
- data/tests/tc_gc_compact.rb +223 -0
- data/tests/tc_test_server_methods.rb +110 -0
- data/tests/timeout.rb +30 -6
- metadata +66 -31
- data/README +0 -194
data/tests/tc_curl_multi.rb
CHANGED
|
@@ -1,19 +1,233 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'tmpdir'
|
|
2
4
|
|
|
3
5
|
class TestCurbCurlMulti < Test::Unit::TestCase
|
|
4
6
|
def teardown
|
|
5
7
|
# get a better read on memory loss when running in valgrind
|
|
6
8
|
ObjectSpace.garbage_collect
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# for https://github.com/taf2/curb/issues/277
|
|
13
|
+
# must connect to an external
|
|
14
|
+
def test_connection_keepalive
|
|
15
|
+
omit('Not supported on Windows runners') if WINDOWS
|
|
16
|
+
# this test fails with libcurl 7.22.0. I didn't investigate, but it may be related
|
|
17
|
+
# to CURLOPT_MAXCONNECTS bug fixed in 7.30.0:
|
|
18
|
+
# https://github.com/curl/curl/commit/e87e76e2dc108efb1cae87df496416f49c55fca0
|
|
19
|
+
omit("Skip, libcurl too old (< 7.22.0)") if Curl::CURL_VERSION.to_f < 8 && Curl::CURL_VERSION.split('.')[1].to_i <= 22
|
|
20
|
+
|
|
21
|
+
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
22
|
+
port = port_socket.addr[1]
|
|
23
|
+
port_socket.close
|
|
24
|
+
|
|
25
|
+
server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
26
|
+
server.mount_proc(TestServlet.path) do |req, res|
|
|
27
|
+
res.body = "GET#{req.query_string}"
|
|
28
|
+
res['Content-Type'] = 'text/plain'
|
|
29
|
+
end
|
|
30
|
+
server_thread = Thread.new(server) { |srv| srv.start }
|
|
31
|
+
wait_for_server_ready(port, thread: server_thread)
|
|
32
|
+
|
|
33
|
+
test_url = "http://127.0.0.1:#{port}#{TestServlet.path}"
|
|
34
|
+
Curl::Multi.autoclose = true
|
|
35
|
+
assert Curl::Multi.autoclose
|
|
36
|
+
|
|
37
|
+
if `which ss`.strip.size == 0
|
|
38
|
+
# osx need lsof still :(
|
|
39
|
+
open_fds = lambda do
|
|
40
|
+
out = `/usr/sbin/lsof -nP -a -p #{Process.pid} -iTCP:#{port} -sTCP:ESTABLISHED`
|
|
41
|
+
[out.lines.drop(1).size, 0].max
|
|
42
|
+
end
|
|
43
|
+
else
|
|
44
|
+
ss = `which ss`.strip
|
|
45
|
+
open_fds = lambda do
|
|
46
|
+
`#{ss} -tn4 state established dport = :#{port} | wc -l`.strip.to_i
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
Curl::Multi.autoclose = false
|
|
50
|
+
before_open = open_fds.call
|
|
51
|
+
#puts "before_open: #{before_open.inspect}"
|
|
52
|
+
assert !Curl::Multi.autoclose
|
|
53
|
+
multi = Curl::Multi.new
|
|
54
|
+
multi.max_connects = 1 # limit to 1 connection within the multi handle
|
|
55
|
+
|
|
56
|
+
did_complete = false
|
|
57
|
+
5.times do |n|
|
|
58
|
+
easy = Curl::Easy.new(test_url) do |curl|
|
|
59
|
+
curl.timeout = 5 # ensure we don't hang for ever connecting to an external host
|
|
60
|
+
curl.on_complete {
|
|
61
|
+
did_complete = true
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
multi.add(easy)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
multi.perform
|
|
68
|
+
assert did_complete
|
|
69
|
+
after_open = open_fds.call
|
|
70
|
+
#puts "after_open: #{after_open} before_open: #{before_open.inspect}"
|
|
71
|
+
# Some CI/libcurl combinations tear down the loopback connection before
|
|
72
|
+
# ss/lsof observes it, so only assert that the multi cache does not retain
|
|
73
|
+
# more than one extra connection here.
|
|
74
|
+
assert (after_open - before_open) < 3, "with max connections set to 1 the multi handle should not retain more than one extra connection"
|
|
75
|
+
multi.close
|
|
76
|
+
|
|
77
|
+
after_open = open_fds.call
|
|
78
|
+
#puts "after_open: #{after_open} before_open: #{before_open.inspect}"
|
|
79
|
+
assert_equal 0, (after_open - before_open), "after closing the multi handle all connections should be closed"
|
|
80
|
+
|
|
81
|
+
Curl::Multi.autoclose = true
|
|
82
|
+
multi = Curl::Multi.new
|
|
83
|
+
did_complete = false
|
|
84
|
+
5.times do |n|
|
|
85
|
+
easy = Curl::Easy.new(test_url) do |curl|
|
|
86
|
+
curl.timeout = 5 # ensure we don't hang for ever connecting to an external host
|
|
87
|
+
curl.on_complete {
|
|
88
|
+
did_complete = true
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
multi.add(easy)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
multi.perform
|
|
95
|
+
assert did_complete
|
|
96
|
+
after_open = open_fds.call
|
|
97
|
+
#puts "after_open: #{after_open} before_open: #{before_open.inspect}"
|
|
98
|
+
assert_equal 0, (after_open - before_open), "auto close the connections"
|
|
99
|
+
ensure
|
|
100
|
+
server.shutdown if defined?(server) && server
|
|
101
|
+
server_thread.join(server_startup_timeout) if defined?(server_thread) && server_thread
|
|
102
|
+
server_thread.kill if defined?(server_thread) && server_thread&.alive?
|
|
103
|
+
Curl::Multi.autoclose = false # restore default
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_connection_autoclose
|
|
107
|
+
assert !Curl::Multi.autoclose
|
|
108
|
+
Curl::Multi.autoclose = true
|
|
109
|
+
assert Curl::Multi.autoclose
|
|
110
|
+
ensure
|
|
111
|
+
Curl::Multi.autoclose = false # restore default
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_perform_can_reuse_multi_when_autoclose_is_enabled
|
|
115
|
+
Curl::Multi.autoclose = true
|
|
116
|
+
|
|
117
|
+
multi = Curl::Multi.new
|
|
118
|
+
results = []
|
|
119
|
+
|
|
120
|
+
first = Curl::Easy.new(TestServlet.url)
|
|
121
|
+
first.on_complete { results << first.code }
|
|
122
|
+
multi.add(first)
|
|
123
|
+
multi.perform
|
|
124
|
+
|
|
125
|
+
second = Curl::Easy.new(TestServlet.url)
|
|
126
|
+
second.on_complete { results << second.code }
|
|
127
|
+
multi.add(second)
|
|
128
|
+
multi.perform
|
|
129
|
+
|
|
130
|
+
assert_equal [200, 200], results
|
|
131
|
+
ensure
|
|
132
|
+
multi.close if defined?(multi) && multi
|
|
133
|
+
Curl::Multi.autoclose = false
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def test_close_inside_completion_callback_is_rejected_without_invalidating_multi
|
|
137
|
+
multi = Curl::Multi.new
|
|
138
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
139
|
+
|
|
140
|
+
easy.on_complete { multi.close }
|
|
141
|
+
multi.add(easy)
|
|
142
|
+
|
|
143
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { multi.perform }
|
|
144
|
+
assert_match(/Cannot close an active Curl::Multi handle/, error.message)
|
|
145
|
+
assert multi.idle?
|
|
146
|
+
assert_equal({}, multi.requests)
|
|
147
|
+
|
|
148
|
+
easy.on_complete { |_curl| }
|
|
149
|
+
multi.add(easy)
|
|
150
|
+
multi.perform
|
|
151
|
+
|
|
152
|
+
assert_equal "GET", easy.body_str
|
|
153
|
+
ensure
|
|
154
|
+
multi.close if defined?(multi) && multi
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_close_inside_perform_block_is_rejected_without_invalidating_multi
|
|
158
|
+
multi = Curl::Multi.new
|
|
159
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
160
|
+
multi.add(easy)
|
|
161
|
+
|
|
162
|
+
error = assert_raise(RuntimeError) do
|
|
163
|
+
multi.perform { multi.close }
|
|
164
|
+
end
|
|
165
|
+
assert_match(/Cannot close an active Curl::Multi handle/, error.message)
|
|
166
|
+
|
|
167
|
+
assert_nothing_raised { multi.close }
|
|
168
|
+
multi = nil
|
|
169
|
+
ensure
|
|
170
|
+
multi.close if defined?(multi) && multi
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_active_easy_cannot_be_added_to_another_multi
|
|
174
|
+
first_multi = Curl::Multi.new
|
|
175
|
+
second_multi = Curl::Multi.new
|
|
176
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
177
|
+
|
|
178
|
+
first_multi.add(easy)
|
|
179
|
+
|
|
180
|
+
error = assert_raise(RuntimeError) { second_multi.add(easy) }
|
|
181
|
+
assert_match(/active Curl::Easy/, error.message)
|
|
182
|
+
assert_same first_multi, easy.multi
|
|
183
|
+
assert_equal easy, first_multi.requests[easy.object_id]
|
|
184
|
+
|
|
185
|
+
assert_nothing_raised { first_multi.close }
|
|
186
|
+
first_multi = nil
|
|
187
|
+
ensure
|
|
188
|
+
first_multi.close if defined?(first_multi) && first_multi
|
|
189
|
+
second_multi.close if defined?(second_multi) && second_multi
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_easy_multi_assignment_detaches_from_old_multi
|
|
193
|
+
multi = Curl::Multi.new
|
|
194
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
195
|
+
|
|
196
|
+
multi.add(easy)
|
|
197
|
+
easy.multi = nil
|
|
198
|
+
|
|
199
|
+
assert_nil easy.multi
|
|
200
|
+
assert_equal({}, multi.requests)
|
|
201
|
+
assert_nothing_raised { easy.close }
|
|
202
|
+
assert_nothing_raised { multi.close }
|
|
203
|
+
|
|
204
|
+
multi = nil
|
|
205
|
+
ensure
|
|
206
|
+
multi.close if defined?(multi) && multi
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_close_makes_multi_unusable
|
|
210
|
+
multi = Curl::Multi.new
|
|
211
|
+
multi.close
|
|
212
|
+
|
|
213
|
+
error = assert_raise(Curl::Err::MultiBadHandle) do
|
|
214
|
+
multi.add(Curl::Easy.new($TEST_URL))
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
assert_match(/Invalid multi handle/i, error.message)
|
|
218
|
+
assert_equal 0, multi.requests.length
|
|
219
|
+
ensure
|
|
220
|
+
multi.close if multi
|
|
7
221
|
end
|
|
8
222
|
|
|
9
223
|
def test_new_multi_01
|
|
10
|
-
d1 =
|
|
224
|
+
d1 = String.new
|
|
11
225
|
c1 = Curl::Easy.new($TEST_URL) do |curl|
|
|
12
226
|
curl.headers["User-Agent"] = "myapp-0.0"
|
|
13
227
|
curl.on_body {|d| d1 << d; d.length }
|
|
14
228
|
end
|
|
15
229
|
|
|
16
|
-
d2 =
|
|
230
|
+
d2 = String.new
|
|
17
231
|
c2 = Curl::Easy.new($TEST_URL) do |curl|
|
|
18
232
|
curl.headers["User-Agent"] = "myapp-0.0"
|
|
19
233
|
curl.on_body {|d| d2 << d; d.length }
|
|
@@ -28,9 +242,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
28
242
|
|
|
29
243
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, d1)
|
|
30
244
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, d2)
|
|
31
|
-
|
|
32
|
-
m
|
|
33
|
-
|
|
245
|
+
ensure
|
|
246
|
+
m.close if m
|
|
34
247
|
end
|
|
35
248
|
|
|
36
249
|
def test_perform_block
|
|
@@ -49,11 +262,426 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
49
262
|
|
|
50
263
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c1.body_str)
|
|
51
264
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c2.body_str)
|
|
265
|
+
ensure
|
|
266
|
+
m.close if m
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def test_multi_perform_runs_work_added_from_final_idle_yield
|
|
270
|
+
with_queue_refill_test_server do |port, hits|
|
|
271
|
+
previous_autoclose = Curl::Multi.autoclose
|
|
272
|
+
Curl::Multi.autoclose = true
|
|
273
|
+
|
|
274
|
+
multi = Curl::Multi.new
|
|
275
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
276
|
+
queued = Curl::Easy.new("http://127.0.0.1:#{port}/queued")
|
|
277
|
+
completions = []
|
|
278
|
+
empty_yields = 0
|
|
279
|
+
queued_added = false
|
|
280
|
+
|
|
281
|
+
slow.on_complete { completions << :slow }
|
|
282
|
+
queued.on_complete { completions << :queued }
|
|
283
|
+
|
|
284
|
+
multi.add(slow)
|
|
285
|
+
multi.perform do |performing_multi|
|
|
286
|
+
empty_yields += 1 if performing_multi.requests.empty?
|
|
287
|
+
if !queued_added && empty_yields >= 2
|
|
288
|
+
queued_added = true
|
|
289
|
+
performing_multi.add(queued)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
assert queued_added, "test should add queued work from the final idle yield"
|
|
294
|
+
assert_equal [:slow, :queued], completions
|
|
295
|
+
assert_equal 1, hits[:slow]
|
|
296
|
+
assert_equal 1, hits[:queued]
|
|
297
|
+
assert_equal 0, multi.requests.length
|
|
298
|
+
ensure
|
|
299
|
+
Curl::Multi.autoclose = previous_autoclose if defined?(previous_autoclose)
|
|
300
|
+
multi.close if defined?(multi) && multi
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def test_multi_easy_get
|
|
305
|
+
n = 1
|
|
306
|
+
urls = []
|
|
307
|
+
n.times { urls << $TEST_URL }
|
|
308
|
+
Curl::Multi.get(urls, {timeout: 5}) {|easy|
|
|
309
|
+
assert_match(/file:/, easy.last_effective_url)
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def test_multi_easy_get_with_error
|
|
314
|
+
omit('Path/line parsing differs on Windows') if WINDOWS
|
|
315
|
+
begin
|
|
316
|
+
did_raise = false
|
|
317
|
+
n = 3
|
|
318
|
+
urls = []
|
|
319
|
+
n.times { urls << $TEST_URL }
|
|
320
|
+
error_line_number_should_be = nil
|
|
321
|
+
Curl::Multi.get(urls, {timeout: 5}) {|easy|
|
|
322
|
+
# if we got this right the error will be reported to be on the line below our error_line_number_should_be
|
|
323
|
+
error_line_number_should_be = __LINE__
|
|
324
|
+
raise
|
|
325
|
+
}
|
|
52
326
|
|
|
53
|
-
|
|
327
|
+
rescue Curl::Err::AbortedByCallbackError => e
|
|
328
|
+
did_raise = true
|
|
329
|
+
in_file = e.backtrace.detect {|err| err.match?(File.basename(__FILE__)) }
|
|
330
|
+
#in_file_stack = e.backtrace.select {|err| err.match?(File.basename(__FILE__)) }
|
|
331
|
+
assert_match(__FILE__, in_file)
|
|
332
|
+
in_file.gsub!(__FILE__)
|
|
333
|
+
parts = in_file.split(':')
|
|
334
|
+
parts.shift
|
|
335
|
+
line_no = parts.shift.to_i
|
|
336
|
+
assert_equal error_line_number_should_be+1, line_no.to_i
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
assert did_raise, "we should have raised an exception"
|
|
54
340
|
|
|
55
341
|
end
|
|
56
342
|
|
|
343
|
+
def test_multi_perform_reraises_on_body_exception
|
|
344
|
+
multi = Curl::Multi.new
|
|
345
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
346
|
+
easy.on_body { raise "body blew up" }
|
|
347
|
+
|
|
348
|
+
error = assert_raise(RuntimeError) do
|
|
349
|
+
multi.add(easy)
|
|
350
|
+
multi.perform
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
assert_equal "body blew up", error.message
|
|
354
|
+
assert !easy.instance_variable_defined?(:@__curb_callback_error)
|
|
355
|
+
ensure
|
|
356
|
+
multi.close if multi
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def test_multi_perform_reraises_on_body_exception_for_frozen_easy
|
|
360
|
+
multi = Curl::Multi.new
|
|
361
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
362
|
+
callback_error = Class.new(RuntimeError)
|
|
363
|
+
easy.on_body { raise callback_error, "body blew up" }
|
|
364
|
+
easy.freeze
|
|
365
|
+
|
|
366
|
+
error = assert_raise(callback_error) do
|
|
367
|
+
multi.add(easy)
|
|
368
|
+
multi.perform
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
assert_equal "body blew up", error.message
|
|
372
|
+
ensure
|
|
373
|
+
begin
|
|
374
|
+
multi.close if multi
|
|
375
|
+
rescue StandardError
|
|
376
|
+
multi.instance_variable_set(:@requests, {})
|
|
377
|
+
multi._close
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def test_multi_perform_reraises_on_header_exception
|
|
382
|
+
multi = Curl::Multi.new
|
|
383
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
384
|
+
easy.on_header { raise "header blew up" }
|
|
385
|
+
|
|
386
|
+
error = assert_raise(RuntimeError) do
|
|
387
|
+
multi.add(easy)
|
|
388
|
+
multi.perform
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
assert_equal "header blew up", error.message
|
|
392
|
+
assert !easy.instance_variable_defined?(:@__curb_callback_error)
|
|
393
|
+
ensure
|
|
394
|
+
multi.close if multi
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def test_multi_perform_drains_completed_siblings_before_reraising_on_body_exception
|
|
398
|
+
multi = Curl::Multi.new
|
|
399
|
+
failed = Curl::Easy.new($TEST_URL)
|
|
400
|
+
completed = Curl::Easy.new($TEST_URL)
|
|
401
|
+
completions = []
|
|
402
|
+
|
|
403
|
+
failed.on_body { raise "body blew up" }
|
|
404
|
+
completed.on_complete { completions << :completed }
|
|
405
|
+
|
|
406
|
+
error = assert_raise(RuntimeError) do
|
|
407
|
+
multi.add(failed)
|
|
408
|
+
multi.add(completed)
|
|
409
|
+
multi.perform
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
assert_equal "body blew up", error.message
|
|
413
|
+
assert_equal [:completed], completions
|
|
414
|
+
assert multi.idle?, 'The multi handle should be idle after draining the completed batch'
|
|
415
|
+
assert_equal 0, multi.requests.length
|
|
416
|
+
assert_nil failed.multi
|
|
417
|
+
assert_nil completed.multi
|
|
418
|
+
assert !failed.instance_variable_defined?(:@__curb_callback_error)
|
|
419
|
+
ensure
|
|
420
|
+
multi.close if multi
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def test_multi_perform_waits_until_idle_before_reraising_on_complete_exception
|
|
424
|
+
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
425
|
+
port = port_socket.addr[1]
|
|
426
|
+
port_socket.close
|
|
427
|
+
|
|
428
|
+
server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
429
|
+
server.mount_proc('/fast') do |_req, res|
|
|
430
|
+
res['Content-Type'] = 'text/plain'
|
|
431
|
+
res.body = 'fast'
|
|
432
|
+
end
|
|
433
|
+
server.mount_proc('/slow') do |_req, res|
|
|
434
|
+
res['Content-Type'] = 'text/plain'
|
|
435
|
+
sleep 0.2
|
|
436
|
+
res.body = 'slow'
|
|
437
|
+
end
|
|
438
|
+
server_thread = Thread.new(server) { |srv| srv.start }
|
|
439
|
+
wait_for_server_ready(port, thread: server_thread)
|
|
440
|
+
|
|
441
|
+
multi = Curl::Multi.new
|
|
442
|
+
fast = Curl::Easy.new("http://127.0.0.1:#{port}/fast")
|
|
443
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
444
|
+
completions = []
|
|
445
|
+
|
|
446
|
+
fast.on_complete do
|
|
447
|
+
completions << :fast
|
|
448
|
+
raise "complete blew up"
|
|
449
|
+
end
|
|
450
|
+
slow.on_complete { completions << :slow }
|
|
451
|
+
|
|
452
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) do
|
|
453
|
+
multi.add(fast)
|
|
454
|
+
multi.add(slow)
|
|
455
|
+
multi.perform
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
assert_equal "complete blew up", error.message
|
|
459
|
+
assert_equal [:fast, :slow], completions
|
|
460
|
+
assert multi.idle?, 'The multi handle should be idle before the deferred exception is raised'
|
|
461
|
+
assert_equal 0, multi.requests.length
|
|
462
|
+
assert_nil fast.multi
|
|
463
|
+
assert_nil slow.multi
|
|
464
|
+
ensure
|
|
465
|
+
multi.close if multi
|
|
466
|
+
server.shutdown if defined?(server) && server
|
|
467
|
+
server_thread.join(server_startup_timeout) if defined?(server_thread) && server_thread
|
|
468
|
+
server_thread.kill if defined?(server_thread) && server_thread&.alive?
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def test_multi_perform_keeps_yielding_block_while_draining_deferred_on_complete_exception
|
|
472
|
+
with_queue_refill_test_server do |port, hits|
|
|
473
|
+
multi = Curl::Multi.new
|
|
474
|
+
failed = Curl::Easy.new("http://127.0.0.1:#{port}/fail")
|
|
475
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
476
|
+
failure_seen = false
|
|
477
|
+
slow_completed = false
|
|
478
|
+
yielded_during_drain = false
|
|
479
|
+
|
|
480
|
+
failed.on_complete do
|
|
481
|
+
failure_seen = true
|
|
482
|
+
raise "complete blew up"
|
|
483
|
+
end
|
|
484
|
+
slow.on_complete { slow_completed = true }
|
|
485
|
+
|
|
486
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) do
|
|
487
|
+
multi.add(failed)
|
|
488
|
+
multi.add(slow)
|
|
489
|
+
multi.perform do
|
|
490
|
+
yielded_during_drain ||= failure_seen && !slow_completed
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
assert_equal "complete blew up", error.message
|
|
495
|
+
assert yielded_during_drain,
|
|
496
|
+
"perform block should continue yielding while draining a slow sibling after a deferred callback exception"
|
|
497
|
+
assert_equal 1, hits[:fail]
|
|
498
|
+
assert_equal 1, hits[:slow]
|
|
499
|
+
ensure
|
|
500
|
+
multi.close if multi
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def test_multi_perform_does_not_start_queued_work_after_on_complete_exception
|
|
505
|
+
with_queue_refill_test_server do |port, hits|
|
|
506
|
+
multi = Curl::Multi.new
|
|
507
|
+
failed = Curl::Easy.new("http://127.0.0.1:#{port}/fail")
|
|
508
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
509
|
+
queued = Curl::Easy.new("http://127.0.0.1:#{port}/queued")
|
|
510
|
+
failure_seen = false
|
|
511
|
+
|
|
512
|
+
failed.on_complete do
|
|
513
|
+
failure_seen = true
|
|
514
|
+
raise "complete blew up"
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) do
|
|
518
|
+
multi.add(failed)
|
|
519
|
+
multi.add(slow)
|
|
520
|
+
multi.perform do
|
|
521
|
+
next unless failure_seen
|
|
522
|
+
|
|
523
|
+
multi.add(queued)
|
|
524
|
+
failure_seen = false
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
assert_equal "complete blew up", error.message
|
|
529
|
+
assert_equal 1, hits[:fail]
|
|
530
|
+
assert_equal 1, hits[:slow]
|
|
531
|
+
assert_equal 0, hits[:queued], "queued request should not start once a deferred callback exception is pending"
|
|
532
|
+
ensure
|
|
533
|
+
multi.close if multi
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def test_multi_perform_does_not_start_work_added_within_failing_on_complete_callback
|
|
538
|
+
with_queue_refill_test_server do |port, hits|
|
|
539
|
+
multi = Curl::Multi.new
|
|
540
|
+
failed = Curl::Easy.new("http://127.0.0.1:#{port}/fail")
|
|
541
|
+
queued = Curl::Easy.new("http://127.0.0.1:#{port}/queued")
|
|
542
|
+
|
|
543
|
+
failed.on_complete do
|
|
544
|
+
multi.add(queued)
|
|
545
|
+
raise "complete blew up"
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) do
|
|
549
|
+
multi.add(failed)
|
|
550
|
+
multi.perform
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
assert_equal "complete blew up", error.message
|
|
554
|
+
assert_equal 1, hits[:fail]
|
|
555
|
+
assert_equal 0, hits[:queued],
|
|
556
|
+
"request added from a failing on_complete callback should not start once the multi begins aborting"
|
|
557
|
+
ensure
|
|
558
|
+
multi.close if multi
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def test_multi_perform_does_not_restart_sibling_removed_and_readded_from_failing_on_complete_callback
|
|
563
|
+
with_queue_refill_test_server(wait_fail_until_slow: true) do |port, hits|
|
|
564
|
+
multi = Curl::Multi.new
|
|
565
|
+
failed = Curl::Easy.new("http://127.0.0.1:#{port}/fail")
|
|
566
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
567
|
+
|
|
568
|
+
failed.on_complete do
|
|
569
|
+
multi.remove(slow)
|
|
570
|
+
multi.add(slow)
|
|
571
|
+
raise "complete blew up"
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) do
|
|
575
|
+
multi.add(failed)
|
|
576
|
+
multi.add(slow)
|
|
577
|
+
multi.perform
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
assert_equal "complete blew up", error.message
|
|
581
|
+
assert_equal 1, hits[:fail]
|
|
582
|
+
assert_equal 1, hits[:slow],
|
|
583
|
+
"sibling removed and re-added from a failing on_complete callback should be treated as replacement work and not restarted"
|
|
584
|
+
ensure
|
|
585
|
+
multi.close if multi
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def test_multi_perform_does_not_start_work_added_within_on_complete_after_on_body_exception
|
|
590
|
+
with_queue_refill_test_server do |port, hits|
|
|
591
|
+
multi = Curl::Multi.new
|
|
592
|
+
failed = Curl::Easy.new("http://127.0.0.1:#{port}/fail")
|
|
593
|
+
queued = Curl::Easy.new("http://127.0.0.1:#{port}/queued")
|
|
594
|
+
|
|
595
|
+
failed.on_body { raise "body blew up" }
|
|
596
|
+
failed.on_complete { multi.add(queued) }
|
|
597
|
+
|
|
598
|
+
error = assert_raise(RuntimeError) do
|
|
599
|
+
multi.add(failed)
|
|
600
|
+
multi.perform
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
assert_equal "body blew up", error.message
|
|
604
|
+
assert_equal 1, hits[:fail]
|
|
605
|
+
assert_equal 0, hits[:queued],
|
|
606
|
+
"request added from on_complete after a body callback exception should not start once the multi begins aborting"
|
|
607
|
+
ensure
|
|
608
|
+
multi.close if multi
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
def test_multi_http_does_not_fetch_queued_url_after_on_body_exception
|
|
613
|
+
with_queue_refill_test_server do |port, hits|
|
|
614
|
+
urls = [
|
|
615
|
+
{ :url => "http://127.0.0.1:#{port}/queued", :method => :get },
|
|
616
|
+
{ :url => "http://127.0.0.1:#{port}/slow", :method => :get },
|
|
617
|
+
{ :url => "http://127.0.0.1:#{port}/fail", :method => :get, :on_body => proc { raise "body blew up" } }
|
|
618
|
+
]
|
|
619
|
+
|
|
620
|
+
error = assert_raise(RuntimeError) do
|
|
621
|
+
Curl::Multi.http(urls, {:max_connects => 2}) { |_easy, _code, _method| }
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
assert_equal "body blew up", error.message
|
|
625
|
+
assert_equal 1, hits[:fail]
|
|
626
|
+
assert_equal 1, hits[:slow]
|
|
627
|
+
assert_equal 0, hits[:queued], "Curl::Multi.http should not refill queued urls after a callback abort"
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def test_multi_perform_runs_status_callbacks_after_on_body_exception
|
|
632
|
+
multi = Curl::Multi.new
|
|
633
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
634
|
+
callbacks = []
|
|
635
|
+
|
|
636
|
+
easy.on_body { raise "body blew up" }
|
|
637
|
+
easy.on_complete { callbacks << :complete }
|
|
638
|
+
easy.on_failure do |_completed_easy, error_info|
|
|
639
|
+
callbacks << :failure
|
|
640
|
+
assert_equal Curl::Err::WriteError, error_info.first
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
error = assert_raise(RuntimeError) do
|
|
644
|
+
multi.add(easy)
|
|
645
|
+
multi.perform
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
assert_equal "body blew up", error.message
|
|
649
|
+
assert_equal [:complete, :failure], callbacks
|
|
650
|
+
assert multi.idle?
|
|
651
|
+
assert_equal 0, multi.requests.length
|
|
652
|
+
ensure
|
|
653
|
+
multi.close if multi
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def test_multi_perform_preserves_on_body_exception_when_status_callbacks_raise
|
|
657
|
+
multi = Curl::Multi.new
|
|
658
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
659
|
+
callbacks = []
|
|
660
|
+
|
|
661
|
+
easy.on_body { raise "body blew up" }
|
|
662
|
+
easy.on_complete do
|
|
663
|
+
callbacks << :complete
|
|
664
|
+
raise "complete blew up"
|
|
665
|
+
end
|
|
666
|
+
easy.on_failure do |_completed_easy, error_info|
|
|
667
|
+
callbacks << :failure
|
|
668
|
+
assert_equal Curl::Err::WriteError, error_info.first
|
|
669
|
+
raise "failure blew up"
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
error = assert_raise(RuntimeError) do
|
|
673
|
+
multi.add(easy)
|
|
674
|
+
multi.perform
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
assert_equal "body blew up", error.message
|
|
678
|
+
assert_equal [:complete, :failure], callbacks
|
|
679
|
+
assert multi.idle?
|
|
680
|
+
assert_equal 0, multi.requests.length
|
|
681
|
+
ensure
|
|
682
|
+
multi.close if multi
|
|
683
|
+
end
|
|
684
|
+
|
|
57
685
|
# NOTE: if this test runs slowly on Mac OSX, it is probably due to the use of a port install curl+ssl+ares install
|
|
58
686
|
# on my MacBook, this causes curl_easy_init to take nearly 0.01 seconds / * 100 below is 1 second too many!
|
|
59
687
|
def test_n_requests
|
|
@@ -61,7 +689,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
61
689
|
m = Curl::Multi.new
|
|
62
690
|
responses = []
|
|
63
691
|
n.times do|i|
|
|
64
|
-
responses[i] =
|
|
692
|
+
responses[i] = String.new
|
|
65
693
|
c = Curl::Easy.new($TEST_URL) do|curl|
|
|
66
694
|
curl.on_body{|data| responses[i] << data; data.size }
|
|
67
695
|
end
|
|
@@ -74,8 +702,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
74
702
|
n.times do|i|
|
|
75
703
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, responses[i], "response #{i}")
|
|
76
704
|
end
|
|
77
|
-
|
|
78
|
-
m
|
|
705
|
+
ensure
|
|
706
|
+
m.close if m
|
|
79
707
|
end
|
|
80
708
|
|
|
81
709
|
def test_n_requests_with_break
|
|
@@ -85,7 +713,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
85
713
|
5.times do|it|
|
|
86
714
|
responses = []
|
|
87
715
|
n.times do|i|
|
|
88
|
-
responses[i] =
|
|
716
|
+
responses[i] = String.new
|
|
89
717
|
c = Curl::Easy.new($TEST_URL) do|curl|
|
|
90
718
|
curl.on_body{|data| responses[i] << data; data.size }
|
|
91
719
|
end
|
|
@@ -98,9 +726,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
98
726
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, responses[i], "response #{i}")
|
|
99
727
|
end
|
|
100
728
|
end
|
|
101
|
-
|
|
102
|
-
m
|
|
103
|
-
|
|
729
|
+
ensure
|
|
730
|
+
m.close if m
|
|
104
731
|
end
|
|
105
732
|
|
|
106
733
|
def test_idle_check
|
|
@@ -109,19 +736,25 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
109
736
|
|
|
110
737
|
assert(m.idle?, 'A new Curl::Multi handle should be idle')
|
|
111
738
|
|
|
739
|
+
assert_nil e.multi
|
|
740
|
+
|
|
112
741
|
m.add(e)
|
|
113
742
|
|
|
743
|
+
assert_not_nil e.multi
|
|
744
|
+
|
|
114
745
|
assert((not m.idle?), 'A Curl::Multi handle with a request should not be idle')
|
|
115
746
|
|
|
116
747
|
m.perform
|
|
117
748
|
|
|
118
749
|
assert(m.idle?, 'A Curl::Multi handle should be idle after performing its requests')
|
|
750
|
+
ensure
|
|
751
|
+
m.close if m
|
|
119
752
|
end
|
|
120
753
|
|
|
121
754
|
def test_requests
|
|
122
755
|
m = Curl::Multi.new
|
|
123
756
|
|
|
124
|
-
assert_equal(
|
|
757
|
+
assert_equal(0, m.requests.length, 'A new Curl::Multi handle should have no requests')
|
|
125
758
|
|
|
126
759
|
10.times do
|
|
127
760
|
m.add(Curl::Easy.new($TEST_URL))
|
|
@@ -131,7 +764,78 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
131
764
|
|
|
132
765
|
m.perform
|
|
133
766
|
|
|
134
|
-
assert_equal(
|
|
767
|
+
assert_equal(0, m.requests.length, 'A new Curl::Multi handle should have no requests after a perform')
|
|
768
|
+
ensure
|
|
769
|
+
m.close if m
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def test_native_safety_signatures_are_pruned_after_perform
|
|
773
|
+
m = Curl::Multi.new
|
|
774
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
775
|
+
|
|
776
|
+
m.add(easy)
|
|
777
|
+
signatures = m.__send__(:__curb_native_safety_signatures)
|
|
778
|
+
assert_equal easy, m.requests[easy.object_id]
|
|
779
|
+
assert signatures.key?(easy.object_id)
|
|
780
|
+
|
|
781
|
+
m.perform
|
|
782
|
+
|
|
783
|
+
assert_equal 0, m.requests.length
|
|
784
|
+
assert_empty signatures
|
|
785
|
+
ensure
|
|
786
|
+
m.close if m
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
def test_easy_multi_is_cleared_after_perform
|
|
790
|
+
m = Curl::Multi.new
|
|
791
|
+
c = Curl::Easy.new($TEST_URL)
|
|
792
|
+
|
|
793
|
+
m.add(c)
|
|
794
|
+
assert_equal m, c.multi
|
|
795
|
+
|
|
796
|
+
m.perform
|
|
797
|
+
|
|
798
|
+
assert_nil c.multi
|
|
799
|
+
ensure
|
|
800
|
+
m.close if m
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def test_frozen_idle_easy_multi_reference_is_cleared_on_close
|
|
804
|
+
multi = Curl::Multi.new
|
|
805
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
806
|
+
easy.freeze
|
|
807
|
+
|
|
808
|
+
easy.multi = multi
|
|
809
|
+
|
|
810
|
+
assert_same multi, easy.multi
|
|
811
|
+
assert_nothing_raised { multi.close }
|
|
812
|
+
assert_nil easy.multi
|
|
813
|
+
multi = nil
|
|
814
|
+
ensure
|
|
815
|
+
multi.close if multi
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def test_easy_multi_is_available_during_completion_callbacks
|
|
819
|
+
multi = Curl::Multi.new
|
|
820
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
821
|
+
seen_multi = {}
|
|
822
|
+
|
|
823
|
+
easy.on_complete do |completed_easy|
|
|
824
|
+
seen_multi[:complete] = completed_easy.multi
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
easy.on_success do |completed_easy|
|
|
828
|
+
seen_multi[:success] = completed_easy.multi
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
multi.add(easy)
|
|
832
|
+
multi.perform
|
|
833
|
+
|
|
834
|
+
assert_same multi, seen_multi[:complete]
|
|
835
|
+
assert_same multi, seen_multi[:success]
|
|
836
|
+
assert_nil easy.multi
|
|
837
|
+
ensure
|
|
838
|
+
multi.close if multi
|
|
135
839
|
end
|
|
136
840
|
|
|
137
841
|
def test_cancel
|
|
@@ -144,7 +848,9 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
144
848
|
|
|
145
849
|
m.cancel!
|
|
146
850
|
|
|
147
|
-
assert_equal(
|
|
851
|
+
assert_equal(0, m.requests.size, 'A new Curl::Multi handle should have no requests after being canceled')
|
|
852
|
+
ensure
|
|
853
|
+
m.close if m
|
|
148
854
|
end
|
|
149
855
|
|
|
150
856
|
def test_with_success
|
|
@@ -175,8 +881,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
175
881
|
|
|
176
882
|
assert success_called2
|
|
177
883
|
assert success_called1
|
|
178
|
-
|
|
179
|
-
m
|
|
884
|
+
ensure
|
|
885
|
+
m.close if m
|
|
180
886
|
end
|
|
181
887
|
|
|
182
888
|
def test_with_success_cb_with_404
|
|
@@ -188,7 +894,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
188
894
|
c1.on_success do|c|
|
|
189
895
|
success_called1 = true
|
|
190
896
|
#puts "success 1 called: #{c.body_str.inspect}"
|
|
191
|
-
|
|
897
|
+
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
|
|
192
898
|
end
|
|
193
899
|
|
|
194
900
|
c1.on_failure do|c,rc|
|
|
@@ -218,8 +924,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
218
924
|
|
|
219
925
|
assert success_called2
|
|
220
926
|
assert !success_called1
|
|
221
|
-
|
|
222
|
-
m
|
|
927
|
+
ensure
|
|
928
|
+
m.close if m
|
|
223
929
|
end
|
|
224
930
|
|
|
225
931
|
# This tests whether, ruby's GC will trash an out of scope easy handle
|
|
@@ -227,7 +933,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
227
933
|
attr_reader :buf
|
|
228
934
|
|
|
229
935
|
def t_method
|
|
230
|
-
@buf =
|
|
936
|
+
@buf = String.new
|
|
231
937
|
@m = Curl::Multi.new
|
|
232
938
|
10.times do|i|
|
|
233
939
|
c = Curl::Easy.new($TEST_URL)
|
|
@@ -243,6 +949,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
243
949
|
@m.perform do
|
|
244
950
|
ObjectSpace.garbage_collect
|
|
245
951
|
end
|
|
952
|
+
ensure
|
|
953
|
+
@m.close if @m
|
|
246
954
|
end
|
|
247
955
|
|
|
248
956
|
def self.test
|
|
@@ -273,8 +981,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
273
981
|
m = Curl::Multi.new
|
|
274
982
|
# add a few easy handles
|
|
275
983
|
requests.each do |url|
|
|
276
|
-
responses[url] =
|
|
277
|
-
responses["#{url}-header"] =
|
|
984
|
+
responses[url] = String.new
|
|
985
|
+
responses["#{url}-header"] = String.new
|
|
278
986
|
c = Curl::Easy.new(url) do|curl|
|
|
279
987
|
curl.follow_location = true
|
|
280
988
|
curl.on_header{|data| responses["#{url}-header"] << data; data.size }
|
|
@@ -314,24 +1022,209 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
314
1022
|
urls = []
|
|
315
1023
|
downloads = []
|
|
316
1024
|
file_info = {}
|
|
317
|
-
FileUtils.mkdir("tmp/")
|
|
318
1025
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
1026
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1027
|
+
# for each file store the size by file name
|
|
1028
|
+
Dir[File.dirname(__FILE__) + "/../ext/*.c"].each do|path|
|
|
1029
|
+
urls << (root_uri + File.basename(path))
|
|
1030
|
+
downloads << File.join(download_dir, File.basename(path))
|
|
1031
|
+
file_info[File.basename(path)] = {:size => File.size(path), :path => path}
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
# start downloads
|
|
1035
|
+
Curl::Multi.download(urls,{},{},downloads) do|curl,download_path|
|
|
1036
|
+
assert_equal 200, curl.response_code
|
|
1037
|
+
assert File.exist?(download_path)
|
|
1038
|
+
assert_equal file_info[File.basename(download_path)][:size], File.size(download_path), "incomplete download: #{download_path}"
|
|
1039
|
+
end
|
|
324
1040
|
end
|
|
1041
|
+
end
|
|
325
1042
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
1043
|
+
def test_multi_download_refuses_existing_explicit_download_path
|
|
1044
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1045
|
+
|
|
1046
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1047
|
+
download_path = File.join(download_dir, 'curb_easy.c')
|
|
1048
|
+
File.write(download_path, 'sentinel')
|
|
1049
|
+
|
|
1050
|
+
assert_raise(Curl::DownloadTargetExistsError) do
|
|
1051
|
+
Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, [download_path])
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
assert_equal 'sentinel', File.read(download_path)
|
|
1055
|
+
end
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
def test_multi_download_failed_transfer_does_not_publish_file_without_block
|
|
1059
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1060
|
+
download_path = File.join(download_dir, 'missing')
|
|
1061
|
+
|
|
1062
|
+
error = assert_raise(Curl::Multi::DownloadError) do
|
|
1063
|
+
Curl::Multi.download(['file:///curb/missing'], {}, {}, [download_path])
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
assert !error.errors.empty?
|
|
1067
|
+
assert !File.exist?(download_path)
|
|
1068
|
+
end
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
def test_multi_download_failed_transfer_does_not_publish_file_or_call_block
|
|
1072
|
+
called = false
|
|
1073
|
+
|
|
1074
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1075
|
+
download_path = File.join(download_dir, 'missing')
|
|
1076
|
+
|
|
1077
|
+
error = assert_raise(Curl::Multi::DownloadError) do
|
|
1078
|
+
Curl::Multi.download(['file:///curb/missing'], {}, {}, [download_path]) do |_curl, _path|
|
|
1079
|
+
called = true
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
assert !error.errors.empty?
|
|
1084
|
+
assert_equal false, called
|
|
1085
|
+
assert !File.exist?(download_path)
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
def test_multi_download_failed_transfer_closes_io_destination
|
|
1090
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1091
|
+
download_path = File.join(download_dir, 'missing')
|
|
1092
|
+
io = File.open(download_path, 'wb')
|
|
1093
|
+
|
|
1094
|
+
error = assert_raise(Curl::Multi::DownloadError) do
|
|
1095
|
+
Curl::Multi.download(['file:///curb/missing'], {}, {}, [io])
|
|
1096
|
+
end
|
|
1097
|
+
|
|
1098
|
+
assert !error.errors.any? { |e| e.is_a?(ArgumentError) }
|
|
1099
|
+
assert io.closed?
|
|
1100
|
+
assert_equal '', File.read(download_path)
|
|
1101
|
+
end
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
def test_multi_download_preserves_body_limit_callback_error
|
|
1105
|
+
with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
|
|
1106
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1107
|
+
download_path = File.join(download_dir, 'too-large')
|
|
1108
|
+
|
|
1109
|
+
error = assert_raise(Curl::Err::FileSizeExceededError) do
|
|
1110
|
+
Curl::Multi.download([url], { :max_body_bytes => 10 }, {}, [download_path])
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
assert_match(/Maximum body size exceeded/, error.message)
|
|
1114
|
+
assert !File.exist?(download_path)
|
|
1115
|
+
end
|
|
1116
|
+
end
|
|
1117
|
+
end
|
|
1118
|
+
|
|
1119
|
+
def test_multi_download_preserves_output_write_callback_error
|
|
1120
|
+
writer_error = Class.new(StandardError)
|
|
1121
|
+
|
|
1122
|
+
with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata") do |url|
|
|
1123
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1124
|
+
writer = File.open(File.join(download_dir, 'writer'), 'wb')
|
|
1125
|
+
writer.define_singleton_method(:write) do |_data|
|
|
1126
|
+
raise writer_error, 'writer failed'
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
error = assert_raise(writer_error) do
|
|
1130
|
+
Curl::Multi.download([url], {}, {}, [writer])
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
assert_equal 'writer failed', error.message
|
|
1134
|
+
assert writer.closed?
|
|
1135
|
+
end
|
|
1136
|
+
end
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
def test_multi_download_safe_directory_rejects_unsafe_names
|
|
1140
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1141
|
+
|
|
1142
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1143
|
+
['../escape.c', '.env', 'nested/file', '/tmp/escape.c', "bad\0name"].each do |name|
|
|
1144
|
+
assert_raise(ArgumentError, "expected #{name.inspect} to be rejected") do
|
|
1145
|
+
Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, [name], :download_dir => download_dir)
|
|
1146
|
+
end
|
|
1147
|
+
end
|
|
1148
|
+
end
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def test_multi_download_rejects_duplicate_destinations
|
|
1152
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1153
|
+
|
|
1154
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1155
|
+
assert_raise(ArgumentError) do
|
|
1156
|
+
Curl::Multi.download(
|
|
1157
|
+
[root_uri + 'curb_easy.c?one=1', root_uri + 'curb_easy.c?two=2'],
|
|
1158
|
+
{},
|
|
1159
|
+
{},
|
|
1160
|
+
nil,
|
|
1161
|
+
:download_dir => download_dir
|
|
1162
|
+
)
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
assert !File.exist?(File.join(download_dir, 'curb_easy.c'))
|
|
1166
|
+
assert_equal [], Dir[File.join(download_dir, '.curb-download-*')]
|
|
1167
|
+
end
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
def test_multi_download_safe_directory_strips_query_from_url_basename
|
|
1171
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1172
|
+
|
|
1173
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1174
|
+
Curl::Multi.download([root_uri + 'curb_easy.c?cache=1'], {}, {}, nil, :download_dir => download_dir)
|
|
1175
|
+
|
|
1176
|
+
download_path = File.join(download_dir, 'curb_easy.c')
|
|
329
1177
|
assert File.exist?(download_path)
|
|
330
|
-
|
|
331
|
-
assert_equal
|
|
1178
|
+
assert !File.exist?(File.join(download_dir, 'curb_easy.c?cache=1'))
|
|
1179
|
+
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
|
|
1180
|
+
end
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
def test_multi_download_hash_url_config_uses_hash_url
|
|
1184
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1185
|
+
|
|
1186
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1187
|
+
download_path = File.join(download_dir, 'curb_easy.c')
|
|
1188
|
+
|
|
1189
|
+
Curl::Multi.download([{ :url => root_uri + 'curb_easy.c', :method => :get }], {}, {}, [download_path]) do |curl, path|
|
|
1190
|
+
assert_equal 200, curl.response_code
|
|
1191
|
+
assert_equal download_path, path
|
|
1192
|
+
end
|
|
1193
|
+
|
|
1194
|
+
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
def test_multi_download_duplicate_urls_use_distinct_explicit_paths
|
|
1199
|
+
url = "file://#{File.expand_path(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c'))}"
|
|
1200
|
+
|
|
1201
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1202
|
+
paths = [
|
|
1203
|
+
File.join(download_dir, 'a.c'),
|
|
1204
|
+
File.join(download_dir, 'b.c')
|
|
1205
|
+
]
|
|
1206
|
+
completed_paths = []
|
|
1207
|
+
|
|
1208
|
+
Curl::Multi.download([url, url], {}, {}, paths) do |_curl, path|
|
|
1209
|
+
completed_paths << path
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
assert_equal paths.sort, completed_paths.sort
|
|
1213
|
+
assert_equal File.read(paths[0]), File.read(paths[1])
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
def test_multi_download_accepts_indexed_hash_download_paths
|
|
1218
|
+
root_uri = 'http://127.0.0.1:9129/ext/'
|
|
1219
|
+
|
|
1220
|
+
Dir.mktmpdir('curb-download-') do |download_dir|
|
|
1221
|
+
download_path = File.join(download_dir, 'curb_easy.c')
|
|
1222
|
+
|
|
1223
|
+
Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, {0 => download_path})
|
|
1224
|
+
|
|
1225
|
+
assert File.exist?(download_path)
|
|
1226
|
+
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
|
|
332
1227
|
end
|
|
333
|
-
ensure
|
|
334
|
-
FileUtils.rm_rf("tmp/")
|
|
335
1228
|
end
|
|
336
1229
|
|
|
337
1230
|
def test_multi_easy_post_01
|
|
@@ -342,7 +1235,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
342
1235
|
]
|
|
343
1236
|
Curl::Multi.post(urls, {:follow_location => true, :multipart_form_post => true}, {:pipeline => true}) do|easy|
|
|
344
1237
|
str = easy.body_str
|
|
345
|
-
assert_match
|
|
1238
|
+
assert_match(/POST/, str)
|
|
346
1239
|
fields = {}
|
|
347
1240
|
str.gsub(/POST\n/,'').split('&').map{|sv| k, v = sv.split('='); fields[k] = v }
|
|
348
1241
|
expected = urls.find{|s| s[:url] == easy.last_effective_url }
|
|
@@ -357,8 +1250,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
357
1250
|
{ :url => TestServlet.url, :method => :put, :put_data => "message",
|
|
358
1251
|
:headers => {'Content-Type' => 'application/json' } }]
|
|
359
1252
|
Curl::Multi.put(urls, {}, {:pipeline => true}) do|easy|
|
|
360
|
-
assert_match
|
|
361
|
-
assert_match
|
|
1253
|
+
assert_match(/PUT/, easy.body_str)
|
|
1254
|
+
assert_match(/message/, easy.body_str)
|
|
362
1255
|
end
|
|
363
1256
|
end
|
|
364
1257
|
|
|
@@ -372,35 +1265,83 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
372
1265
|
{ :url => TestServlet.url, :method => :get }
|
|
373
1266
|
]
|
|
374
1267
|
Curl::Multi.http(urls, {:pipeline => true}) do|easy, code, method|
|
|
375
|
-
assert_equal
|
|
1268
|
+
assert_equal 200, code
|
|
376
1269
|
case method
|
|
377
1270
|
when :post
|
|
378
|
-
assert_match
|
|
1271
|
+
assert_match(/POST/, easy.body_str)
|
|
379
1272
|
when :get
|
|
380
|
-
assert_match
|
|
1273
|
+
assert_match(/GET/, easy.body_str)
|
|
381
1274
|
when :put
|
|
382
|
-
assert_match
|
|
1275
|
+
assert_match(/PUT/, easy.body_str)
|
|
383
1276
|
end
|
|
384
1277
|
#puts "#{easy.body_str.inspect}, #{method.inspect}, #{code.inspect}"
|
|
385
1278
|
end
|
|
386
1279
|
end
|
|
387
1280
|
|
|
388
1281
|
def test_multi_easy_http_with_max_connects
|
|
389
|
-
|
|
1282
|
+
urls = [
|
|
390
1283
|
{ :url => TestServlet.url + '?q=1', :method => :get },
|
|
391
1284
|
{ :url => TestServlet.url + '?q=2', :method => :get },
|
|
392
1285
|
{ :url => TestServlet.url + '?q=3', :method => :get }
|
|
393
1286
|
]
|
|
394
1287
|
Curl::Multi.http(urls, {:pipeline => true, :max_connects => 1}) do|easy, code, method|
|
|
395
|
-
assert_equal
|
|
1288
|
+
assert_equal 200, code
|
|
396
1289
|
case method
|
|
397
1290
|
when :post
|
|
398
|
-
assert_match
|
|
1291
|
+
assert_match(/POST/, easy.body)
|
|
399
1292
|
when :get
|
|
400
|
-
assert_match
|
|
1293
|
+
assert_match(/GET/, easy.body)
|
|
401
1294
|
when :put
|
|
402
|
-
assert_match
|
|
1295
|
+
assert_match(/PUT/, easy.body)
|
|
1296
|
+
end
|
|
1297
|
+
end
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
def test_multi_easy_http_with_max_host_connections
|
|
1301
|
+
urls = [
|
|
1302
|
+
{ :url => TestServlet.url + '?q=1', :method => :get },
|
|
1303
|
+
{ :url => TestServlet.url + '?q=2', :method => :get },
|
|
1304
|
+
{ :url => TestServlet.url + '?q=3', :method => :get }
|
|
1305
|
+
]
|
|
1306
|
+
Curl::Multi.http(urls, {:pipeline => true, :max_host_connections => 1}) do|easy, code, method|
|
|
1307
|
+
assert_equal 200, code
|
|
1308
|
+
case method
|
|
1309
|
+
when :post
|
|
1310
|
+
assert_match(/POST/, easy.body)
|
|
1311
|
+
when :get
|
|
1312
|
+
assert_match(/GET/, easy.body)
|
|
1313
|
+
when :put
|
|
1314
|
+
assert_match(/PUT/, easy.body)
|
|
1315
|
+
end
|
|
1316
|
+
end
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
# Regression test for issue #267 (2015): ensure that when reusing
|
|
1320
|
+
# easy handles with constrained concurrency, the callback receives
|
|
1321
|
+
# the correct URL for each completed request rather than repeating
|
|
1322
|
+
# the first URL.
|
|
1323
|
+
def test_multi_easy_http_urls_unique_across_max_connects
|
|
1324
|
+
urls = [
|
|
1325
|
+
{ :url => TestServlet.url + '?q=1', :method => :get },
|
|
1326
|
+
{ :url => TestServlet.url + '?q=2', :method => :get },
|
|
1327
|
+
{ :url => TestServlet.url + '?q=3', :method => :get }
|
|
1328
|
+
]
|
|
1329
|
+
|
|
1330
|
+
[1, 2, 3].each do |max|
|
|
1331
|
+
results = []
|
|
1332
|
+
Curl::Multi.http(urls.dup, {:pipeline => true, :max_connects => max}) do |easy, code, method|
|
|
1333
|
+
assert_equal 200, code
|
|
1334
|
+
assert_equal :get, method
|
|
1335
|
+
results << easy.last_effective_url
|
|
403
1336
|
end
|
|
1337
|
+
|
|
1338
|
+
# Ensure we saw one completion per input URL
|
|
1339
|
+
assert_equal urls.size, results.size, "expected #{urls.size} results with max_connects=#{max}"
|
|
1340
|
+
|
|
1341
|
+
# And that each URL completed exactly once (no accidental reuse/mis-reporting)
|
|
1342
|
+
expected_urls = urls.map { |u| u[:url] }
|
|
1343
|
+
assert_equal expected_urls.to_set, results.to_set, "unexpected URLs for max_connects=#{max}: #{results.inspect}"
|
|
1344
|
+
assert_equal expected_urls.size, results.uniq.size, "duplicate URLs seen for max_connects=#{max}: #{results.inspect}"
|
|
404
1345
|
end
|
|
405
1346
|
end
|
|
406
1347
|
|
|
@@ -422,6 +1363,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
422
1363
|
failure = false
|
|
423
1364
|
assert !failure
|
|
424
1365
|
assert_equal "POST\nhello=world", e2.body_str
|
|
1366
|
+
ensure
|
|
1367
|
+
m.close if m
|
|
425
1368
|
end
|
|
426
1369
|
|
|
427
1370
|
def test_remove_exception_is_descriptive
|
|
@@ -431,6 +1374,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
431
1374
|
rescue => e
|
|
432
1375
|
assert_equal 'CURLError: Invalid easy handle', e.message
|
|
433
1376
|
assert_equal 0, m.requests.size
|
|
1377
|
+
ensure
|
|
1378
|
+
m.close if m
|
|
434
1379
|
end
|
|
435
1380
|
|
|
436
1381
|
def test_retry_easy_handle
|
|
@@ -454,6 +1399,50 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
454
1399
|
m.perform
|
|
455
1400
|
assert_equal 0, tries
|
|
456
1401
|
assert_equal 0, m.requests.size
|
|
1402
|
+
ensure
|
|
1403
|
+
m.close if m
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1406
|
+
def test_close_in_on_missing_callback_is_blocked
|
|
1407
|
+
m = Curl::Multi.new
|
|
1408
|
+
c = Curl::Easy.new(TestServlet.url + '/not_here')
|
|
1409
|
+
did_raise = false
|
|
1410
|
+
|
|
1411
|
+
c.on_missing do |easy, _error|
|
|
1412
|
+
begin
|
|
1413
|
+
easy.close
|
|
1414
|
+
rescue RuntimeError
|
|
1415
|
+
did_raise = true
|
|
1416
|
+
end
|
|
1417
|
+
end
|
|
1418
|
+
|
|
1419
|
+
m.add(c)
|
|
1420
|
+
m.perform
|
|
1421
|
+
|
|
1422
|
+
assert did_raise
|
|
1423
|
+
ensure
|
|
1424
|
+
m.close if m
|
|
1425
|
+
end
|
|
1426
|
+
|
|
1427
|
+
def test_close_in_on_redirect_callback_is_blocked
|
|
1428
|
+
m = Curl::Multi.new
|
|
1429
|
+
c = Curl::Easy.new(TestServlet.url + '/redirect')
|
|
1430
|
+
did_raise = false
|
|
1431
|
+
|
|
1432
|
+
c.on_redirect do |easy, _error|
|
|
1433
|
+
begin
|
|
1434
|
+
easy.close
|
|
1435
|
+
rescue RuntimeError
|
|
1436
|
+
did_raise = true
|
|
1437
|
+
end
|
|
1438
|
+
end
|
|
1439
|
+
|
|
1440
|
+
m.add(c)
|
|
1441
|
+
m.perform
|
|
1442
|
+
|
|
1443
|
+
assert did_raise
|
|
1444
|
+
ensure
|
|
1445
|
+
m.close if m
|
|
457
1446
|
end
|
|
458
1447
|
|
|
459
1448
|
def test_reusing_handle
|
|
@@ -466,7 +1455,9 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
466
1455
|
m.add(c)
|
|
467
1456
|
m.add(c)
|
|
468
1457
|
rescue => e
|
|
469
|
-
|
|
1458
|
+
assert Curl::Err::MultiBadEasyHandle == e.class || Curl::Err::MultiAddedAlready == e.class
|
|
1459
|
+
ensure
|
|
1460
|
+
m.close if m
|
|
470
1461
|
end
|
|
471
1462
|
|
|
472
1463
|
def test_multi_default_timeout
|
|
@@ -476,10 +1467,79 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
476
1467
|
assert_equal 100, (Curl::Multi.default_timeout = 100)
|
|
477
1468
|
end
|
|
478
1469
|
|
|
1470
|
+
def with_queue_refill_test_server(wait_fail_until_slow: false)
|
|
1471
|
+
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
1472
|
+
port = port_socket.addr[1]
|
|
1473
|
+
port_socket.close
|
|
1474
|
+
|
|
1475
|
+
hits = Hash.new(0)
|
|
1476
|
+
slow_started = false
|
|
1477
|
+
slow_started_lock = Mutex.new
|
|
1478
|
+
slow_started_condition = ConditionVariable.new
|
|
1479
|
+
server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
1480
|
+
server.mount_proc('/fail') do |_req, res|
|
|
1481
|
+
if wait_fail_until_slow
|
|
1482
|
+
slow_started_lock.synchronize do
|
|
1483
|
+
slow_started_condition.wait(slow_started_lock, 1) unless slow_started
|
|
1484
|
+
end
|
|
1485
|
+
end
|
|
1486
|
+
|
|
1487
|
+
hits[:fail] += 1
|
|
1488
|
+
res['Content-Type'] = 'text/plain'
|
|
1489
|
+
res.body = 'fail'
|
|
1490
|
+
end
|
|
1491
|
+
server.mount_proc('/slow') do |_req, res|
|
|
1492
|
+
slow_started_lock.synchronize do
|
|
1493
|
+
hits[:slow] += 1
|
|
1494
|
+
slow_started = true
|
|
1495
|
+
slow_started_condition.broadcast
|
|
1496
|
+
end
|
|
1497
|
+
|
|
1498
|
+
res['Content-Type'] = 'text/plain'
|
|
1499
|
+
sleep 0.3
|
|
1500
|
+
res.body = 'slow'
|
|
1501
|
+
end
|
|
1502
|
+
server.mount_proc('/queued') do |_req, res|
|
|
1503
|
+
hits[:queued] += 1
|
|
1504
|
+
res['Content-Type'] = 'text/plain'
|
|
1505
|
+
res.body = 'queued'
|
|
1506
|
+
end
|
|
1507
|
+
|
|
1508
|
+
server_thread = Thread.new(server) { |srv| srv.start }
|
|
1509
|
+
wait_for_server_ready(port, thread: server_thread)
|
|
1510
|
+
|
|
1511
|
+
yield port, hits
|
|
1512
|
+
ensure
|
|
1513
|
+
server.shutdown if defined?(server) && server
|
|
1514
|
+
server_thread.join(server_startup_timeout) if defined?(server_thread) && server_thread
|
|
1515
|
+
server_thread.kill if defined?(server_thread) && server_thread&.alive?
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1518
|
+
def with_raw_http_response(response)
|
|
1519
|
+
server = TCPServer.new('127.0.0.1', 0)
|
|
1520
|
+
port = server.addr[1]
|
|
1521
|
+
thread = Thread.new do
|
|
1522
|
+
socket = server.accept
|
|
1523
|
+
begin
|
|
1524
|
+
socket.readpartial(4096)
|
|
1525
|
+
rescue EOFError
|
|
1526
|
+
end
|
|
1527
|
+
socket.write(response)
|
|
1528
|
+
socket.close
|
|
1529
|
+
end
|
|
1530
|
+
|
|
1531
|
+
yield "http://127.0.0.1:#{port}/"
|
|
1532
|
+
ensure
|
|
1533
|
+
server.close if defined?(server) && server && !server.closed?
|
|
1534
|
+
if defined?(thread) && thread
|
|
1535
|
+
thread.join(5)
|
|
1536
|
+
thread.kill if thread.alive?
|
|
1537
|
+
end
|
|
1538
|
+
end
|
|
1539
|
+
|
|
479
1540
|
include TestServerMethods
|
|
480
1541
|
|
|
481
1542
|
def setup
|
|
482
1543
|
server_setup
|
|
483
1544
|
end
|
|
484
|
-
|
|
485
1545
|
end
|