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.
@@ -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
- path = Tempfile.new('curb_test_curlopt_stderr').path
51
+ tmp = Tempfile.new('curb_test_curlopt_stderr')
52
+ path = tmp.path
38
53
  fd = IO.sysopen(path, 'w')
39
- io = IO.for_fd(fd)
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
- assert_match(output, 'HTTP/1.1 200 OK')
50
- assert_match(output, 'Host: 127.0.0.1:9129')
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.markdown'))),
809
- Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
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.markdown'))),
819
- Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
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.markdown')))
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.markdown'))
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.body_str.size)
1062
- assert_equal(easy.body_str.tr("\r", ''), File.read(readme))
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"
@@ -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
@@ -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!"