curb 0.9.6 → 0.9.11

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.
@@ -14,7 +14,6 @@
14
14
  typedef struct {
15
15
  int active;
16
16
  int running;
17
- VALUE requests; /* hash of handles currently added */
18
17
  CURLM *handle;
19
18
  } ruby_curl_multi;
20
19
 
@@ -59,6 +59,9 @@ def have_constant(name)
59
59
  end
60
60
  end
61
61
 
62
+ have_constant "curlopt_tcp_keepalive"
63
+ have_constant "curlopt_tcp_keepidle"
64
+ have_constant "curlopt_tcp_keepintvl"
62
65
  have_constant "curlinfo_appconnect_time"
63
66
  have_constant "curlinfo_redirect_time"
64
67
  have_constant "curlinfo_response_code"
@@ -84,6 +87,7 @@ have_constant "curlproxy_http"
84
87
  have_constant "curlproxy_socks4"
85
88
  have_constant "curlproxy_socks4a"
86
89
  have_constant "curlproxy_socks5"
90
+ have_constant "curlproxy_socks5_hostname"
87
91
  have_constant "curlauth_basic"
88
92
  have_constant "curlauth_digest"
89
93
  have_constant "curlauth_gssnegotiate"
@@ -102,6 +106,9 @@ have_constant "curle_send_fail_rewind"
102
106
  have_constant "curle_ssl_engine_initfailed"
103
107
  have_constant "curle_login_denied"
104
108
 
109
+ # older than 7.10.0
110
+ have_constant "curlopt_nosignal"
111
+
105
112
  # older than 7.16.0
106
113
  have_constant "curlmopt_pipelining"
107
114
 
@@ -149,6 +156,8 @@ have_func("curl_multi_timeout")
149
156
  have_func("curl_multi_fdset")
150
157
  have_func("curl_multi_perform")
151
158
 
159
+ have_constant "curlopt_haproxyprotocol"
160
+
152
161
  # constants
153
162
  have_constant "curlopt_interleavefunction"
154
163
  have_constant "curlopt_interleavedata"
@@ -213,6 +222,7 @@ have_constant "curlopt_httppost"
213
222
  have_constant "curlopt_referer"
214
223
  have_constant "curlopt_useragent"
215
224
  have_constant "curlopt_httpheader"
225
+ have_constant "curlopt_proxyheader"
216
226
  have_constant "curlopt_http200aliases"
217
227
  have_constant "curlopt_cookie"
218
228
  have_constant "curlopt_cookiefile"
@@ -417,6 +427,8 @@ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
417
427
 
418
428
  have_func('rb_thread_blocking_region')
419
429
  have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
430
+ have_header('ruby/io.h')
431
+ have_func('rb_io_stdio_file')
420
432
 
421
433
  create_header('curb_config.h')
422
434
  create_makefile('curb_core')
@@ -1 +1,2 @@
1
+ # frozen_string_literal: true
1
2
  require 'curl'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'curb_core'
2
3
  require 'curl/easy'
3
4
  require 'curl/multi'
@@ -48,13 +49,11 @@ module Curl
48
49
 
49
50
  def self.urlalize(url, params={})
50
51
  uri = URI(url)
52
+ # early return if we didn't specify any extra params
53
+ return uri.to_s if (params || {}).empty?
54
+
51
55
  params_query = URI.encode_www_form(params || {})
52
- # both uri.query and params_query not blank
53
- if !(uri.query.nil? || uri.query.empty?) && !params_query.empty?
54
- uri.query = "#{uri.query}&#{params_query}"
55
- else
56
- uri.query = "#{uri.query}#{params_query}"
57
- end
56
+ uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
58
57
  uri.to_s
59
58
  end
60
59
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Curl
2
3
  class Easy
3
4
 
@@ -68,9 +69,14 @@ module Curl
68
69
  ret = self.multi.perform
69
70
  self.multi.remove self
70
71
 
72
+ if Curl::Multi.autoclose
73
+ self.multi.close
74
+ self.multi = nil
75
+ end
76
+
71
77
  if self.last_result != 0 && self.on_failure.nil?
72
78
  error = Curl::Easy.error(self.last_result)
73
- raise error.first.new(error.last)
79
+ raise error.first.new(self.head)
74
80
  end
75
81
 
76
82
  ret
@@ -1,5 +1,5 @@
1
+ # frozen_string_literal: true
1
2
  module Curl
2
-
3
3
  class Multi
4
4
  class << self
5
5
  # call-seq:
@@ -144,7 +144,7 @@ module Curl
144
144
 
145
145
  max_connects.times do
146
146
  conf = urls_with_config.pop
147
- add_free_handle.call conf, nil
147
+ add_free_handle.call(conf, nil) if conf
148
148
  break if urls_with_config.empty?
149
149
  end
150
150
 
@@ -153,7 +153,7 @@ module Curl
153
153
  if urls_with_config.size > 0 && free_handles.size > 0
154
154
  easy = free_handles.pop
155
155
  conf = urls_with_config.pop
156
- add_free_handle.call conf, easy
156
+ add_free_handle.call(conf, easy) if conf
157
157
  end
158
158
  end
159
159
 
@@ -168,6 +168,7 @@ module Curl
168
168
  end
169
169
  free_handles = nil
170
170
  end
171
+
171
172
  end
172
173
 
173
174
  # call-seq:
@@ -242,7 +243,45 @@ module Curl
242
243
  }
243
244
  raise errors unless errors.empty?
244
245
  end
246
+ end
247
+
248
+ def cancel!
249
+ requests.each do |_,easy|
250
+ remove(easy)
251
+ end
252
+ end
253
+
254
+ def idle?
255
+ requests.empty?
256
+ end
245
257
 
258
+ def requests
259
+ @requests ||= {}
246
260
  end
261
+
262
+ def add(easy)
263
+ return self if requests[easy.object_id]
264
+ requests[easy.object_id] = easy
265
+ _add(easy)
266
+ self
267
+ end
268
+
269
+ def remove(easy)
270
+ return self if !requests[easy.object_id]
271
+ requests.delete(easy.object_id)
272
+ _remove(easy)
273
+ self
274
+ end
275
+
276
+ def close
277
+ requests.values.each {|easy|
278
+ _remove(easy)
279
+ }
280
+ @requests = {}
281
+ _close
282
+ self
283
+ end
284
+
285
+
247
286
  end
248
287
  end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+
4
+ require 'curb'
5
+
6
+ class BugIssue102 < Test::Unit::TestCase
7
+
8
+ def test_gc_closewait
9
+ 100.times do
10
+ responses = {}
11
+ requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"]
12
+ m = Curl::Multi.new
13
+ # add a few easy handles
14
+ requests.each do |url|
15
+ responses[url] = ""
16
+ c = Curl::Easy.new(url) do|curl|
17
+ curl.follow_location = true
18
+ curl.on_body{|data| responses[url] << data; data.size }
19
+ curl.on_success {|easy| #puts "success, add more easy handles"
20
+ }
21
+ end
22
+ m.add(c)
23
+ end
24
+
25
+ m.perform do
26
+ #puts "idling... can do some work here"
27
+ end
28
+ GC.start
29
+ end
30
+ end
31
+
32
+ end
@@ -2,6 +2,7 @@
2
2
  # Copyright (c)2006 Ross Bamford. See LICENSE.
3
3
  $CURB_TESTING = true
4
4
  require 'uri'
5
+ require 'stringio'
5
6
 
6
7
  $TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
7
8
  $EXTDIR = File.join($TOPDIR, 'ext')
@@ -97,7 +98,7 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
97
98
  if params and params['s'] == '500'
98
99
  res.status = 500
99
100
  elsif params and params['c']
100
- cookie = URI.decode_www_form(params['c'])[0][0].split('=')
101
+ cookie = URI.decode_www_form_component(params['c']).split('=')
101
102
  res.cookies << WEBrick::Cookie.new(*cookie)
102
103
  else
103
104
  respond_with("POST\n#{req.body}",req,res)
@@ -207,3 +208,81 @@ module TestServerMethods
207
208
  rescue Errno::EADDRINUSE
208
209
  end
209
210
  end
211
+
212
+
213
+
214
+ # Backport for Ruby 1.8
215
+ module Backports
216
+ module Ruby18
217
+ module URIFormEncoding
218
+ TBLENCWWWCOMP_ = {}
219
+ TBLDECWWWCOMP_ = {}
220
+
221
+ def encode_www_form_component(str)
222
+ if TBLENCWWWCOMP_.empty?
223
+ 256.times do |i|
224
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
225
+ end
226
+ TBLENCWWWCOMP_[' '] = '+'
227
+ TBLENCWWWCOMP_.freeze
228
+ end
229
+ str.to_s.gsub( /([^*\-.0-9A-Z_a-z])/ ) {|*| TBLENCWWWCOMP_[$1] }
230
+ end
231
+
232
+ def decode_www_form_component(str)
233
+ if TBLDECWWWCOMP_.empty?
234
+ 256.times do |i|
235
+ h, l = i>>4, i&15
236
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
237
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
238
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
239
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
240
+ end
241
+ TBLDECWWWCOMP_['+'] = ' '
242
+ TBLDECWWWCOMP_.freeze
243
+ end
244
+
245
+ raise ArgumentError, "invalid %-encoding (#{str.dump})" unless /\A(?:%[[:xdigit:]]{2}|[^%]+)*\z/ =~ str
246
+ str.gsub( /(\+|%[[:xdigit:]]{2})/ ) {|*| TBLDECWWWCOMP_[$1] }
247
+ end
248
+
249
+ def encode_www_form( enum )
250
+ enum.map do |k,v|
251
+ if v.nil?
252
+ encode_www_form_component(k)
253
+ elsif v.respond_to?(:to_ary)
254
+ v.to_ary.map do |w|
255
+ str = encode_www_form_component(k)
256
+ unless w.nil?
257
+ str << '='
258
+ str << encode_www_form_component(w)
259
+ end
260
+ end.join('&')
261
+ else
262
+ str = encode_www_form_component(k)
263
+ str << '='
264
+ str << encode_www_form_component(v)
265
+ end
266
+ end.join('&')
267
+ end
268
+
269
+ WFKV_ = '(?:%\h\h|[^%#=;&])'
270
+ def decode_www_form(str, _)
271
+ return [] if str.to_s == ''
272
+
273
+ unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/ =~ str
274
+ raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})"
275
+ end
276
+ ary = []
277
+ $&.scan(/([^=;&]+)=([^;&]*)/) do
278
+ ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)]
279
+ end
280
+ ary
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ unless URI.methods.include?(:encode_www_form)
287
+ URI.extend(Backports::Ruby18::URIFormEncoding)
288
+ end
@@ -31,6 +31,36 @@ class TestCurl < Test::Unit::TestCase
31
31
  assert_equal "OPTIONSfoo=bar", curl.body_str
32
32
  end
33
33
 
34
+ def test_urlalize_without_extra_params
35
+ url_no_params = 'http://localhost/test'
36
+ url_with_params = 'http://localhost/test?a=1'
37
+
38
+ assert_equal(url_no_params, Curl.urlalize(url_no_params))
39
+ assert_equal(url_with_params, Curl.urlalize(url_with_params))
40
+ end
41
+
42
+ def test_urlalize_with_nil_as_params
43
+ url = 'http://localhost/test'
44
+ assert_equal(url, Curl.urlalize(url, nil))
45
+ end
46
+
47
+ def test_urlalize_with_extra_params
48
+ url_no_params = 'http://localhost/test'
49
+ url_with_params = 'http://localhost/test?a=1'
50
+ extra_params = { :b => 2 }
51
+
52
+ expected_url_no_params = 'http://localhost/test?b=2'
53
+ expected_url_with_params = 'http://localhost/test?a=1&b=2'
54
+
55
+ assert_equal(expected_url_no_params, Curl.urlalize(url_no_params, extra_params))
56
+ assert_equal(expected_url_with_params, Curl.urlalize(url_with_params, extra_params))
57
+ end
58
+
59
+ def test_urlalize_does_not_strip_trailing_?
60
+ url_empty_params = 'http://localhost/test?'
61
+ assert_equal(url_empty_params, Curl.urlalize(url_empty_params))
62
+ end
63
+
34
64
  include TestServerMethods
35
65
 
36
66
  def setup
@@ -10,6 +10,66 @@ class TestCurbCurlEasy < Test::Unit::TestCase
10
10
  Curl.reset
11
11
  end
12
12
 
13
+ def test_curlopt_stderr_with_file
14
+ # does not work with Tempfile directly
15
+ path = Tempfile.new('curb_test_curlopt_stderr').path
16
+ File.open(path, 'w') do |file|
17
+ easy = Curl::Easy.new(TestServlet.url)
18
+ easy.verbose = true
19
+ easy.setopt(Curl::CURLOPT_STDERR, file)
20
+ easy.perform
21
+ end
22
+ output = File.read(path)
23
+
24
+ assert_match('HTTP/1.1 200 OK ', output)
25
+ assert_match('Host: 127.0.0.1:9129', output)
26
+ end
27
+
28
+ def test_curlopt_stderr_with_io
29
+ path = Tempfile.new('curb_test_curlopt_stderr').path
30
+ fd = IO.sysopen(path, 'w')
31
+ io = IO.for_fd(fd)
32
+
33
+ easy = Curl::Easy.new(TestServlet.url)
34
+ easy.verbose = true
35
+ easy.setopt(Curl::CURLOPT_STDERR, io)
36
+ easy.perform
37
+
38
+
39
+ output = File.read(path)
40
+
41
+ assert_match(output, 'HTTP/1.1 200 OK')
42
+ assert_match(output, 'Host: 127.0.0.1:9129')
43
+ end
44
+
45
+ def test_curlopt_stderr_fails_with_tempdir
46
+ Tempfile.open('curb_test_curlopt_stderr') do |tempfile|
47
+ easy = Curl::Easy.new(TestServlet.url)
48
+
49
+ assert_raise(TypeError) do
50
+ easy.setopt(Curl::CURLOPT_STDERR, tempfile)
51
+ end
52
+ end
53
+ end
54
+
55
+ def test_curlopt_stderr_fails_with_stringio
56
+ stringio = StringIO.new
57
+ easy = Curl::Easy.new(TestServlet.url)
58
+
59
+ assert_raise(TypeError) do
60
+ easy.setopt(Curl::CURLOPT_STDERR, stringio)
61
+ end
62
+ end
63
+
64
+ def test_curlopt_stderr_fails_with_string
65
+ string = String.new
66
+ easy = Curl::Easy.new(TestServlet.url)
67
+
68
+ assert_raise(TypeError) do
69
+ easy.setopt(Curl::CURLOPT_STDERR, string)
70
+ end
71
+ end
72
+
13
73
  def test_exception
14
74
  begin
15
75
  Curl.get('NOT_FOUND_URL')
@@ -28,8 +88,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
28
88
  c = Curl.get($TEST_URL)
29
89
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
30
90
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body)
31
- assert_equal "", c.header_str
32
- assert_equal "", c.head
33
91
  end
34
92
  end
35
93
  end
@@ -41,7 +99,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
41
99
  assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL)
42
100
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
43
101
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body)
44
- assert_equal "", c.header_str
45
102
  end
46
103
 
47
104
  def test_class_perform_02
@@ -49,7 +106,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
49
106
  assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL) { |curl| curl.on_body { |d| data << d; d.length } }
50
107
 
51
108
  assert_nil c.body_str
52
- assert_equal "", c.header_str
53
109
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, data)
54
110
  end
55
111
 
@@ -144,7 +200,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
144
200
  c = Curl::Easy.new($TEST_URL)
145
201
  assert_equal true, c.http_get
146
202
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
147
- assert_equal "", c.header_str
148
203
  end
149
204
 
150
205
  def test_get_02
@@ -156,7 +211,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
156
211
  assert_equal true, c.http_get
157
212
 
158
213
  assert_nil c.body_str
159
- assert_equal "", c.header_str
160
214
  assert_match(/^# DO NOT REMOVE THIS COMMENT/, data)
161
215
  end
162
216
 
@@ -339,29 +393,69 @@ class TestCurbCurlEasy < Test::Unit::TestCase
339
393
  c.max_redirects = nil
340
394
  assert_nil c.max_redirects
341
395
  end
342
-
396
+
397
+ def test_timeout_with_floats
398
+ c = Curl::Easy.new($TEST_URL)
399
+
400
+ c.timeout = 1.5
401
+ assert_equal 1500, c.timeout_ms
402
+ assert_equal 1.5, c.timeout
403
+ end
404
+
405
+ def test_timeout_with_negative
406
+ c = Curl::Easy.new($TEST_URL)
407
+
408
+ c.timeout = -1.5
409
+ assert_equal 0, c.timeout
410
+ assert_equal 0, c.timeout_ms
411
+
412
+ c.timeout = -4.8
413
+ assert_equal 0, c.timeout
414
+ assert_equal 0, c.timeout_ms
415
+ end
416
+
343
417
  def test_timeout_01
344
418
  c = Curl::Easy.new($TEST_URL)
345
-
346
- assert_nil c.timeout
347
-
419
+
420
+ assert_equal 0, c.timeout
421
+
348
422
  c.timeout = 3
349
423
  assert_equal 3, c.timeout
350
-
351
- c.timeout = nil
352
- assert_nil c.timeout
424
+
425
+ c.timeout = 0
426
+ assert_equal 0, c.timeout
353
427
  end
354
428
 
355
429
  def test_timeout_ms_01
356
430
  c = Curl::Easy.new($TEST_URL)
357
431
 
358
- assert_nil c.timeout_ms
432
+ assert_equal 0, c.timeout_ms
359
433
 
360
434
  c.timeout_ms = 100
361
435
  assert_equal 100, c.timeout_ms
362
436
 
363
437
  c.timeout_ms = nil
364
- assert_nil c.timeout_ms
438
+ assert_equal 0, c.timeout_ms
439
+ end
440
+
441
+ def test_timeout_ms_with_floats
442
+ c = Curl::Easy.new($TEST_URL)
443
+
444
+ c.timeout_ms = 55.5
445
+ assert_equal 55, c.timeout_ms
446
+ assert_equal 0.055, c.timeout
447
+ end
448
+
449
+ def test_timeout_ms_with_negative
450
+ c = Curl::Easy.new($TEST_URL)
451
+
452
+ c.timeout_ms = -1.5
453
+ assert_equal 0, c.timeout
454
+ assert_equal 0, c.timeout_ms
455
+
456
+ c.timeout_ms = -4.8
457
+ assert_equal 0, c.timeout
458
+ assert_equal 0, c.timeout_ms
365
459
  end
366
460
 
367
461
  def test_connect_timeout_01
@@ -608,7 +702,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
608
702
  def test_cookielist
609
703
  c = Curl::Easy.new TestServlet.url
610
704
  c.enable_cookies = true
611
- c.post_body = URI.encode_www_form c: 'somename=somevalue'
705
+ c.post_body = URI.encode_www_form('c' => 'somename=somevalue')
612
706
  assert_nil c.cookielist
613
707
  c.perform
614
708
  assert_match(/somevalue/, c.cookielist.join(''))