curb 1.0.9 → 1.2.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/{README.markdown → README.md} +7 -1
- data/ext/curb.c +6 -4
- data/ext/curb.h +4 -4
- data/ext/curb_easy.c +117 -7
- data/ext/curb_multi.c +497 -15
- data/ext/curb_postfield.c +6 -0
- data/ext/curb_upload.c +3 -0
- data/ext/extconf.rb +217 -17
- data/lib/curl.rb +0 -1
- 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/helper.rb +34 -0
- data/tests/mem_check.rb +3 -0
- data/tests/tc_curl_download.rb +2 -1
- data/tests/tc_curl_easy.rb +86 -14
- data/tests/tc_curl_multi.rb +21 -0
- data/tests/tc_fiber_scheduler.rb +190 -0
- data/tests/test_basic.rb +29 -0
- data/tests/test_fiber_debug.rb +69 -0
- data/tests/test_fiber_simple.rb +65 -0
- data/tests/test_real_url.rb +65 -0
- data/tests/test_simple_fiber.rb +34 -0
- metadata +24 -8
- data/tests/bug_issue277.rb +0 -32
data/tests/tc_curl_easy.rb
CHANGED
@@ -18,6 +18,20 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
18
18
|
assert_equal 200, easy.code
|
19
19
|
end
|
20
20
|
|
21
|
+
def test_curlopt_resolve
|
22
|
+
require 'resolv'
|
23
|
+
uri = URI.parse(TestServlet.url)
|
24
|
+
resolved_ip = Resolv.getaddress(uri.host) # perform DNS lookup once
|
25
|
+
mapping = "#{uri.host}:#{uri.port}:#{resolved_ip}"
|
26
|
+
|
27
|
+
http = Curl::Easy.new(TestServlet.url)
|
28
|
+
http.setopt(Curl::CURLOPT_RESOLVE, [mapping])
|
29
|
+
|
30
|
+
http.get
|
31
|
+
|
32
|
+
assert_match(/GET/, http.body)
|
33
|
+
end
|
34
|
+
|
21
35
|
def test_curlopt_stderr_with_file
|
22
36
|
# does not work with Tempfile directly
|
23
37
|
path = Tempfile.new('curb_test_curlopt_stderr').path
|
@@ -34,20 +48,62 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
34
48
|
end
|
35
49
|
|
36
50
|
def test_curlopt_stderr_with_io
|
37
|
-
|
51
|
+
tmp = Tempfile.new('curb_test_curlopt_stderr')
|
52
|
+
path = tmp.path
|
38
53
|
fd = IO.sysopen(path, 'w')
|
39
|
-
io = IO.
|
54
|
+
io = IO.new(fd, 'w')
|
55
|
+
io.sync = true
|
40
56
|
|
41
57
|
easy = Curl::Easy.new(TestServlet.url)
|
42
58
|
easy.verbose = true
|
43
59
|
easy.setopt(Curl::CURLOPT_STDERR, io)
|
44
60
|
easy.perform
|
45
61
|
|
62
|
+
io.flush
|
63
|
+
io.close
|
64
|
+
output = File.read(path)
|
65
|
+
|
66
|
+
assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
|
67
|
+
assert_match('Host: 127.0.0.1:9129', output)
|
68
|
+
ensure
|
69
|
+
tmp.close! if defined?(tmp) && tmp
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_curlopt_stderr_gc_safe
|
73
|
+
tmp = Tempfile.new('curb_test_curlopt_stderr_gc')
|
74
|
+
path = tmp.path
|
75
|
+
fd = IO.sysopen(path, 'w')
|
76
|
+
io = IO.new(fd, 'w')
|
77
|
+
|
78
|
+
easy = Curl::Easy.new(TestServlet.url)
|
79
|
+
easy.verbose = true
|
80
|
+
easy.setopt(Curl::CURLOPT_STDERR, io)
|
81
|
+
|
82
|
+
# Drop our Ruby reference and force GC; curb should retain it internally
|
83
|
+
io = nil
|
84
|
+
GC.start
|
85
|
+
|
86
|
+
easy.perform
|
46
87
|
|
47
88
|
output = File.read(path)
|
89
|
+
assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
|
90
|
+
assert_match('Host: 127.0.0.1:9129', output)
|
91
|
+
ensure
|
92
|
+
tmp.close! if defined?(tmp) && tmp
|
93
|
+
end
|
48
94
|
|
49
|
-
|
50
|
-
|
95
|
+
def test_head_request_no_body_and_no_timeout
|
96
|
+
# Ensure a HEAD request completes and does not attempt to read a body
|
97
|
+
# even when the server advertises a Content-Length.
|
98
|
+
easy = nil
|
99
|
+
assert_nothing_raised do
|
100
|
+
easy = Curl.http(:HEAD, TestServlet.url)
|
101
|
+
end
|
102
|
+
assert_not_nil easy
|
103
|
+
# Header string should contain the HTTP status line.
|
104
|
+
assert_match(/HTTP\/1\.1\s\d+\s/, easy.header_str.to_s)
|
105
|
+
# Body should be empty for HEAD requests (libcurl won't call the write callback).
|
106
|
+
assert_equal "", easy.body_str.to_s
|
51
107
|
end
|
52
108
|
|
53
109
|
def test_curlopt_stderr_fails_with_tempdir
|
@@ -231,6 +287,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
231
287
|
|
232
288
|
|
233
289
|
def test_last_effective_url_01
|
290
|
+
omit('Windows file URL semantics differ') if WINDOWS
|
234
291
|
c = Curl::Easy.new($TEST_URL)
|
235
292
|
|
236
293
|
assert_equal $TEST_URL, c.url
|
@@ -805,8 +862,8 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
805
862
|
curl = Curl::Easy.new(TestServlet.url)
|
806
863
|
curl.multipart_form_post = true
|
807
864
|
fields = [
|
808
|
-
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
809
|
-
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
865
|
+
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))),
|
866
|
+
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
810
867
|
]
|
811
868
|
curl.http_post(fields)
|
812
869
|
assert_match(/HTTP POST file upload/, curl.body_str)
|
@@ -815,8 +872,8 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
815
872
|
curl = Curl::Easy.new(TestServlet.url)
|
816
873
|
curl.multipart_form_post = true
|
817
874
|
fields = [
|
818
|
-
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
819
|
-
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
875
|
+
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))),
|
876
|
+
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
820
877
|
]
|
821
878
|
curl.http_put(fields)
|
822
879
|
assert_match(/HTTP POST file upload/, curl.body_str)
|
@@ -866,7 +923,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
866
923
|
[:put, :post, :patch].each {|method|
|
867
924
|
curl = Curl::Easy.new(TestServlet.url)
|
868
925
|
curl.multipart_form_post = true
|
869
|
-
pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
926
|
+
pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
870
927
|
curl.send("http_#{method}", pf)
|
871
928
|
assert_match(/HTTP POST file upload/, curl.body_str)
|
872
929
|
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
@@ -1027,7 +1084,11 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
1027
1084
|
$auth_header = nil
|
1028
1085
|
# curl checks the auth type supported by the server, so we have to create a
|
1029
1086
|
# new easy handle if we're going to change the auth type...
|
1030
|
-
|
1087
|
+
if WINDOWS
|
1088
|
+
# On Windows, libcurl often uses SSPI for NTLM which yields a different
|
1089
|
+
# header value and encoding; skip the NTLM-specific assertion.
|
1090
|
+
return
|
1091
|
+
end
|
1031
1092
|
curl = Curl::Easy.new(TestServlet.url)
|
1032
1093
|
curl.username = "foo"
|
1033
1094
|
curl.password = "bar"
|
@@ -1048,7 +1109,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
1048
1109
|
end
|
1049
1110
|
|
1050
1111
|
def test_post_streaming
|
1051
|
-
readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README.
|
1112
|
+
readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))
|
1052
1113
|
|
1053
1114
|
pf = Curl::PostField.file("filename", readme)
|
1054
1115
|
|
@@ -1058,11 +1119,10 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
1058
1119
|
easy.multipart_form_post = true
|
1059
1120
|
easy.http_post(pf)
|
1060
1121
|
|
1061
|
-
assert_not_equal(0,easy.
|
1062
|
-
assert_equal(easy.
|
1122
|
+
assert_not_equal(0,easy.body.size)
|
1123
|
+
assert_equal(Digest::MD5.hexdigest(easy.body), Digest::MD5.hexdigest(File.binread(readme)))
|
1063
1124
|
end
|
1064
1125
|
|
1065
|
-
|
1066
1126
|
def test_easy_close
|
1067
1127
|
easy = Curl::Easy.new
|
1068
1128
|
easy.close
|
@@ -1082,6 +1142,18 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
1082
1142
|
easy.http_get
|
1083
1143
|
end
|
1084
1144
|
|
1145
|
+
def test_last_result_initialization
|
1146
|
+
# Test for issue #463 - ensure last_result is properly initialized to 0
|
1147
|
+
easy = Curl::Easy.new
|
1148
|
+
assert_equal 0, easy.last_result, "last_result should be initialized to 0 for new instance"
|
1149
|
+
|
1150
|
+
# Test that reset also sets last_result to 0
|
1151
|
+
easy.url = TestServlet.url
|
1152
|
+
easy.http_get
|
1153
|
+
easy.reset
|
1154
|
+
assert_equal 0, easy.last_result, "last_result should be reset to 0 after calling reset"
|
1155
|
+
end
|
1156
|
+
|
1085
1157
|
def test_easy_use_http_versions
|
1086
1158
|
easy = Curl::Easy.new
|
1087
1159
|
easy.url = TestServlet.url + "?query=foo"
|
data/tests/tc_curl_multi.rb
CHANGED
@@ -10,6 +10,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
10
10
|
# for https://github.com/taf2/curb/issues/277
|
11
11
|
# must connect to an external
|
12
12
|
def test_connection_keepalive
|
13
|
+
omit('Not supported on Windows runners') if WINDOWS
|
13
14
|
# this test fails with libcurl 7.22.0. I didn't investigate, but it may be related
|
14
15
|
# to CURLOPT_MAXCONNECTS bug fixed in 7.30.0:
|
15
16
|
# https://github.com/curl/curl/commit/e87e76e2dc108efb1cae87df496416f49c55fca0
|
@@ -171,6 +172,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
171
172
|
end
|
172
173
|
|
173
174
|
def test_multi_easy_get_with_error
|
175
|
+
omit('Path/line parsing differs on Windows') if WINDOWS
|
174
176
|
begin
|
175
177
|
did_raise = false
|
176
178
|
n = 3
|
@@ -552,6 +554,25 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
552
554
|
end
|
553
555
|
end
|
554
556
|
|
557
|
+
def test_multi_easy_http_with_max_host_connections
|
558
|
+
urls = [
|
559
|
+
{ :url => TestServlet.url + '?q=1', :method => :get },
|
560
|
+
{ :url => TestServlet.url + '?q=2', :method => :get },
|
561
|
+
{ :url => TestServlet.url + '?q=3', :method => :get }
|
562
|
+
]
|
563
|
+
Curl::Multi.http(urls, {:pipeline => true, :max_host_connections => 1}) do|easy, code, method|
|
564
|
+
assert_equal 200, code
|
565
|
+
case method
|
566
|
+
when :post
|
567
|
+
assert_match(/POST/, easy.body)
|
568
|
+
when :get
|
569
|
+
assert_match(/GET/, easy.body)
|
570
|
+
when :put
|
571
|
+
assert_match(/PUT/, easy.body)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
555
576
|
def test_multi_recieves_500
|
556
577
|
m = Curl::Multi.new
|
557
578
|
e = Curl::Easy.new("http://127.0.0.1:9129/methods")
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'async'
|
5
|
+
rescue LoadError
|
6
|
+
HAS_ASYNC = false
|
7
|
+
else
|
8
|
+
HAS_ASYNC = true
|
9
|
+
end
|
10
|
+
|
11
|
+
# This test verifies that Curl::Easy#perform cooperates with Ruby's Fiber scheduler
|
12
|
+
# by running multiple requests concurrently in a single thread using the Async gem.
|
13
|
+
class TestCurbFiberScheduler < Test::Unit::TestCase
|
14
|
+
include BugTestServerSetupTeardown
|
15
|
+
|
16
|
+
ITERS = 4
|
17
|
+
MIN_S = 0.25
|
18
|
+
# Each request sleeps 0.25s; two concurrent requests should be ~0.25–0.5s.
|
19
|
+
# Allow some jitter in CI environments.
|
20
|
+
THRESHOLD = ((MIN_S*(ITERS/2.0)) + (MIN_S/2.0)) # add more jitter for slower CI environments
|
21
|
+
SERIAL_TIME_WOULD_BE_ABOUT = MIN_S * ITERS
|
22
|
+
|
23
|
+
def setup
|
24
|
+
@port = 9993
|
25
|
+
|
26
|
+
@response_proc = lambda do |res|
|
27
|
+
res['Content-Type'] = 'text/plain'
|
28
|
+
sleep MIN_S
|
29
|
+
res.body = '200'
|
30
|
+
end
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_multi_is_scheduler_friendly
|
35
|
+
if skip_no_async
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
url = "http://127.0.0.1:#{@port}/test"
|
40
|
+
|
41
|
+
started = Time.now
|
42
|
+
results = []
|
43
|
+
|
44
|
+
|
45
|
+
async_run do
|
46
|
+
m = Curl::Multi.new
|
47
|
+
ITERS.times.each do
|
48
|
+
c = Curl::Easy.new(url)
|
49
|
+
c.on_complete { results << c.code }
|
50
|
+
m.add(c)
|
51
|
+
end
|
52
|
+
m.perform
|
53
|
+
end
|
54
|
+
|
55
|
+
duration = Time.now - started
|
56
|
+
|
57
|
+
assert duration < THRESHOLD, "Requests did not run concurrently under fiber scheduler (#{duration}s) which exceeds the expected threshold of: #{THRESHOLD} serial time would be about: #{SERIAL_TIME_WOULD_BE_ABOUT}"
|
58
|
+
assert_equal ITERS, results.size
|
59
|
+
assert_equal ITERS.times.map {200}, results
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_easy_perform_is_scheduler_friendly
|
63
|
+
if skip_no_async
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
url = "http://127.0.0.1:#{@port}/test"
|
68
|
+
|
69
|
+
started = Time.now
|
70
|
+
results = []
|
71
|
+
|
72
|
+
async_run do |top|
|
73
|
+
tasks = ITERS.times.map do
|
74
|
+
top.async do
|
75
|
+
#t = Time.now.to_i
|
76
|
+
#puts "starting fiber [#{results.size}] -> #{t}"
|
77
|
+
c = Curl.get(url)
|
78
|
+
#puts "received result: #{results.size} -> #{Time.now.to_f - t.to_f}"
|
79
|
+
results << c.code
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
tasks.each(&:wait)
|
84
|
+
end
|
85
|
+
|
86
|
+
duration = Time.now - started
|
87
|
+
|
88
|
+
assert duration < THRESHOLD, "Requests did not run concurrently under fiber scheduler (#{duration}s) which exceeds the expected threshold of: #{THRESHOLD} serial time would be about: #{SERIAL_TIME_WOULD_BE_ABOUT}"
|
89
|
+
assert_equal ITERS, results.size
|
90
|
+
assert_equal ITERS.times.map {200}, results
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_multi_perform_yields_block_under_scheduler
|
94
|
+
if skip_no_async
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
url = "http://127.0.0.1:#{@port}/test"
|
99
|
+
yielded = 0
|
100
|
+
results = []
|
101
|
+
|
102
|
+
async_run do
|
103
|
+
m = Curl::Multi.new
|
104
|
+
ITERS.times do
|
105
|
+
c = Curl::Easy.new(url)
|
106
|
+
c.on_complete { results << c.code }
|
107
|
+
m.add(c)
|
108
|
+
end
|
109
|
+
m.perform do
|
110
|
+
yielded += 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
assert_operator yielded, :>=, 1, 'perform did not yield block while waiting under scheduler'
|
115
|
+
assert_equal ITERS, results.size
|
116
|
+
assert_equal ITERS.times.map {200}, results
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_multi_single_request_scheduler_path
|
120
|
+
if skip_no_async
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
url = "http://127.0.0.1:#{@port}/test"
|
125
|
+
result = nil
|
126
|
+
|
127
|
+
async_run do
|
128
|
+
m = Curl::Multi.new
|
129
|
+
c = Curl::Easy.new(url)
|
130
|
+
c.on_complete { result = c.code }
|
131
|
+
m.add(c)
|
132
|
+
m.perform
|
133
|
+
end
|
134
|
+
|
135
|
+
assert_equal 200, result
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_multi_reuse_after_scheduler_perform
|
139
|
+
unless HAS_ASYNC
|
140
|
+
warn 'Skipping fiber scheduler test (Async gem not available)'
|
141
|
+
return
|
142
|
+
end
|
143
|
+
|
144
|
+
url = "http://127.0.0.1:#{@port}/test"
|
145
|
+
results = []
|
146
|
+
|
147
|
+
async_run do
|
148
|
+
m = Curl::Multi.new
|
149
|
+
# First round
|
150
|
+
c1 = Curl::Easy.new(url)
|
151
|
+
c1.on_complete { results << c1.code }
|
152
|
+
m.add(c1)
|
153
|
+
m.perform
|
154
|
+
|
155
|
+
# Second round on same multi
|
156
|
+
c2 = Curl::Easy.new(url)
|
157
|
+
c2.on_complete { results << c2.code }
|
158
|
+
m.add(c2)
|
159
|
+
m.perform
|
160
|
+
end
|
161
|
+
|
162
|
+
assert_equal [200, 200], results
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
def skip_no_async
|
167
|
+
if WINDOWS
|
168
|
+
warn 'Skipping fiber scheduler tests on Windows'
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
unless HAS_ASYNC
|
172
|
+
warn 'Skipping fiber scheduler test (Async gem not available)'
|
173
|
+
return true
|
174
|
+
end
|
175
|
+
false
|
176
|
+
end
|
177
|
+
|
178
|
+
def async_run(&block)
|
179
|
+
# Prefer newer Async.run to avoid deprecated scheduler.async path.
|
180
|
+
if defined?(Async) && Async.respond_to?(:run)
|
181
|
+
Async.run(&block)
|
182
|
+
else
|
183
|
+
Async(&block)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1')
|
188
|
+
warn 'Skipping fiber scheduler tests on Ruby < 3.1'
|
189
|
+
return
|
190
|
+
end
|
data/tests/test_basic.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
|
3
|
+
class TestBasic < Test::Unit::TestCase
|
4
|
+
include TestServerMethods
|
5
|
+
|
6
|
+
def setup
|
7
|
+
server_setup
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_basic_request
|
11
|
+
puts "\n=== Testing basic request ==="
|
12
|
+
easy = Curl::Easy.new(TestServlet.url)
|
13
|
+
easy.perform
|
14
|
+
puts "Response code: #{easy.response_code}"
|
15
|
+
puts "Body (first 100 chars): #{easy.body_str[0..100]}"
|
16
|
+
assert_equal 200, easy.response_code
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_slow_request
|
20
|
+
puts "\n=== Testing slow request ==="
|
21
|
+
url = TestServlet.url_to("/slow?seconds=0.1")
|
22
|
+
puts "URL: #{url}"
|
23
|
+
easy = Curl::Easy.new(url)
|
24
|
+
easy.perform
|
25
|
+
puts "Response code: #{easy.response_code}"
|
26
|
+
puts "Body: #{easy.body_str}"
|
27
|
+
assert_equal 200, easy.response_code
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
require 'async'
|
3
|
+
|
4
|
+
class TestFiberDebug < Test::Unit::TestCase
|
5
|
+
include TestServerMethods
|
6
|
+
|
7
|
+
def setup
|
8
|
+
server_setup
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_simple_fiber_request
|
12
|
+
puts "\n=== Starting simple fiber request test ==="
|
13
|
+
|
14
|
+
run_async do |task|
|
15
|
+
puts "Inside Async block"
|
16
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
17
|
+
|
18
|
+
multi = Curl::Multi.new
|
19
|
+
easy = Curl::Easy.new(TestServlet.url)
|
20
|
+
easy.on_complete { |curl| puts "Request completed: #{curl.response_code}" }
|
21
|
+
|
22
|
+
multi.add(easy)
|
23
|
+
puts "Added easy handle to multi"
|
24
|
+
|
25
|
+
# Perform without block first
|
26
|
+
puts "Calling perform..."
|
27
|
+
multi.perform
|
28
|
+
puts "Perform completed"
|
29
|
+
end
|
30
|
+
|
31
|
+
puts "Test completed"
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_fiber_with_block
|
35
|
+
puts "\n=== Starting fiber with block test ==="
|
36
|
+
|
37
|
+
run_async do |task|
|
38
|
+
puts "Inside Async block"
|
39
|
+
|
40
|
+
multi = Curl::Multi.new
|
41
|
+
easy = Curl::Easy.new(TestServlet.url_to("/slow?seconds=0.1"))
|
42
|
+
easy.on_complete { |curl| puts "Request completed: #{curl.response_code}" }
|
43
|
+
|
44
|
+
multi.add(easy)
|
45
|
+
|
46
|
+
block_calls = 0
|
47
|
+
multi.perform do
|
48
|
+
block_calls += 1
|
49
|
+
puts "Block called: #{block_calls}"
|
50
|
+
if block_calls < 5 # Limit iterations to prevent infinite loop
|
51
|
+
Async::Task.yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
puts "Perform completed, block called #{block_calls} times"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Test completed"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def run_async(&block)
|
63
|
+
if defined?(Async) && Async.respond_to?(:run)
|
64
|
+
Async.run(&block)
|
65
|
+
else
|
66
|
+
Async(&block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
require 'async'
|
3
|
+
|
4
|
+
class TestFiberSimple < Test::Unit::TestCase
|
5
|
+
include TestServerMethods
|
6
|
+
|
7
|
+
def setup
|
8
|
+
server_setup
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_simple_concurrent
|
12
|
+
puts "\n=== Testing simple concurrent requests ==="
|
13
|
+
|
14
|
+
results = []
|
15
|
+
|
16
|
+
if Async.respond_to?(:run)
|
17
|
+
Async.run do |task|
|
18
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
19
|
+
|
20
|
+
multi = Curl::Multi.new
|
21
|
+
|
22
|
+
# Add 3 requests
|
23
|
+
3.times do |i|
|
24
|
+
easy = Curl::Easy.new(TestServlet.url_to("/slow?seconds=0.2&id=#{i}"))
|
25
|
+
easy.on_complete { |curl|
|
26
|
+
results << { id: i, code: curl.response_code }
|
27
|
+
puts "Request #{i} completed"
|
28
|
+
}
|
29
|
+
multi.add(easy)
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "Starting perform..."
|
33
|
+
start_time = Time.now
|
34
|
+
multi.perform # No block
|
35
|
+
elapsed = Time.now - start_time
|
36
|
+
puts "Perform completed in #{elapsed.round(2)}s"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
Async do |task|
|
40
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
41
|
+
|
42
|
+
multi = Curl::Multi.new
|
43
|
+
|
44
|
+
# Add 3 requests
|
45
|
+
3.times do |i|
|
46
|
+
easy = Curl::Easy.new(TestServlet.url_to("/slow?seconds=0.2&id=#{i}"))
|
47
|
+
easy.on_complete { |curl|
|
48
|
+
results << { id: i, code: curl.response_code }
|
49
|
+
puts "Request #{i} completed"
|
50
|
+
}
|
51
|
+
multi.add(easy)
|
52
|
+
end
|
53
|
+
|
54
|
+
puts "Starting perform..."
|
55
|
+
start_time = Time.now
|
56
|
+
multi.perform # No block
|
57
|
+
elapsed = Time.now - start_time
|
58
|
+
puts "Perform completed in #{elapsed.round(2)}s"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
assert_equal 3, results.size
|
63
|
+
results.each { |r| assert_equal 200, r[:code] }
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/curb'))
|
2
|
+
require 'async'
|
3
|
+
|
4
|
+
puts "Testing fiber scheduler with real URLs..."
|
5
|
+
|
6
|
+
# Test without fiber scheduler
|
7
|
+
puts "\n1. Without fiber scheduler:"
|
8
|
+
start = Time.now
|
9
|
+
multi = Curl::Multi.new
|
10
|
+
|
11
|
+
easies = []
|
12
|
+
3.times do |i|
|
13
|
+
easy = Curl::Easy.new("https://httpbin.org/delay/1")
|
14
|
+
easy.on_complete { |curl| puts "Request #{i} completed: #{curl.response_code}" }
|
15
|
+
multi.add(easy)
|
16
|
+
easies << easy
|
17
|
+
end
|
18
|
+
|
19
|
+
multi.perform
|
20
|
+
elapsed = Time.now - start
|
21
|
+
puts "Total time: #{elapsed.round(2)}s"
|
22
|
+
|
23
|
+
# Test with fiber scheduler
|
24
|
+
puts "\n2. With fiber scheduler:"
|
25
|
+
if Async.respond_to?(:run)
|
26
|
+
Async.run do
|
27
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
28
|
+
|
29
|
+
start = Time.now
|
30
|
+
multi = Curl::Multi.new
|
31
|
+
|
32
|
+
easies = []
|
33
|
+
3.times do |i|
|
34
|
+
easy = Curl::Easy.new("https://httpbin.org/delay/1")
|
35
|
+
easy.on_complete { |curl| puts "Request #{i} completed: #{curl.response_code}" }
|
36
|
+
multi.add(easy)
|
37
|
+
easies << easy
|
38
|
+
end
|
39
|
+
|
40
|
+
multi.perform
|
41
|
+
elapsed = Time.now - start
|
42
|
+
puts "Total time: #{elapsed.round(2)}s"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
Async do
|
46
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
47
|
+
|
48
|
+
start = Time.now
|
49
|
+
multi = Curl::Multi.new
|
50
|
+
|
51
|
+
easies = []
|
52
|
+
3.times do |i|
|
53
|
+
easy = Curl::Easy.new("https://httpbin.org/delay/1")
|
54
|
+
easy.on_complete { |curl| puts "Request #{i} completed: #{curl.response_code}" }
|
55
|
+
multi.add(easy)
|
56
|
+
easies << easy
|
57
|
+
end
|
58
|
+
|
59
|
+
multi.perform
|
60
|
+
elapsed = Time.now - start
|
61
|
+
puts "Total time: #{elapsed.round(2)}s"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "\nDone!"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/curb'))
|
2
|
+
require 'async'
|
3
|
+
|
4
|
+
puts "Testing simple fiber scheduler..."
|
5
|
+
|
6
|
+
if Async.respond_to?(:run)
|
7
|
+
Async.run do
|
8
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
9
|
+
|
10
|
+
multi = Curl::Multi.new
|
11
|
+
easy = Curl::Easy.new("https://httpbin.org/delay/1")
|
12
|
+
easy.on_complete { |curl| puts "Request completed: #{curl.response_code}" }
|
13
|
+
|
14
|
+
multi.add(easy)
|
15
|
+
puts "Starting perform..."
|
16
|
+
multi.perform
|
17
|
+
puts "Perform completed"
|
18
|
+
end
|
19
|
+
else
|
20
|
+
Async do
|
21
|
+
puts "Fiber scheduler available: #{Curl::Multi.fiber_scheduler_available?}"
|
22
|
+
|
23
|
+
multi = Curl::Multi.new
|
24
|
+
easy = Curl::Easy.new("https://httpbin.org/delay/1")
|
25
|
+
easy.on_complete { |curl| puts "Request completed: #{curl.response_code}" }
|
26
|
+
|
27
|
+
multi.add(easy)
|
28
|
+
puts "Starting perform..."
|
29
|
+
multi.perform
|
30
|
+
puts "Perform completed"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
puts "Done!"
|