curb 0.9.6 → 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(''))