curb 0.9.4 → 0.9.10

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.
data/ext/curb_multi.h CHANGED
@@ -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
 
data/ext/extconf.rb CHANGED
@@ -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"
@@ -322,14 +332,14 @@ have_constant "curlopt_sslengine"
322
332
  have_constant "curlopt_sslengine_default"
323
333
  have_constant "curlopt_sslversion"
324
334
  have_constant "curl_sslversion_default"
325
- have_constant "curl_sslversion_tlsv1"
326
- have_constant "curl_sslversion_sslv2"
327
- have_constant "curl_sslversion_sslv3"
335
+ have_constant :CURL_SSLVERSION_TLSv1
336
+ have_constant :CURL_SSLVERSION_SSLv2
337
+ have_constant :CURL_SSLVERSION_SSLv3
328
338
 
329
339
  # Added in 7.34.0
330
- have_constant "curl_sslversion_tlsv1_0"
331
- have_constant "curl_sslversion_tlsv1_1"
332
- have_constant "curl_sslversion_tlsv1_2"
340
+ have_constant :CURL_SSLVERSION_TLSv1_0
341
+ have_constant :CURL_SSLVERSION_TLSv1_1
342
+ have_constant :CURL_SSLVERSION_TLSv1_2
333
343
 
334
344
  have_constant "curlopt_ssl_verifypeer"
335
345
  have_constant "curlopt_cainfo"
@@ -364,6 +374,16 @@ have_constant "curle_not_built_in"
364
374
 
365
375
  have_constant "curle_obsolete" # removed in 7.24 ?
366
376
 
377
+ have_constant "curle_ftp_pret_failed"
378
+ have_constant "curle_rtsp_cseq_error"
379
+ have_constant "curle_rtsp_session_error"
380
+ have_constant "curle_ftp_bad_file_list"
381
+ have_constant "curle_chunk_failed"
382
+ have_constant "curle_no_connection_available"
383
+ have_constant "curle_ssl_pinnedpubkeynotmatch"
384
+ have_constant "curle_ssl_invalidcertstatus"
385
+ have_constant "curle_http2_stream"
386
+
367
387
  # gssapi/spnego delegation related constants
368
388
  have_constant "curlopt_gssapi_delegation"
369
389
  have_constant "curlgssapi_delegation_policy_flag"
@@ -377,6 +397,9 @@ have_constant "curlopt_unix_socket_path"
377
397
  # added in 7.42.0
378
398
  have_constant "curlopt_path_as_is"
379
399
 
400
+ # added in 7.43.0
401
+ have_constant "curlopt_pipewait"
402
+
380
403
  if try_compile('int main() { return 0; }','-Wall')
381
404
  $CFLAGS << ' -Wall'
382
405
  end
@@ -404,6 +427,8 @@ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
404
427
 
405
428
  have_func('rb_thread_blocking_region')
406
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')
407
432
 
408
433
  create_header('curb_config.h')
409
434
  create_makefile('curb_core')
data/lib/curb.rb CHANGED
@@ -1 +1,2 @@
1
+ # frozen_string_literal: true
1
2
  require 'curl'
data/lib/curl.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'curb_core'
2
3
  require 'curl/easy'
3
4
  require 'curl/multi'
@@ -6,7 +7,7 @@ require 'cgi'
6
7
 
7
8
  # expose shortcut methods
8
9
  module Curl
9
-
10
+
10
11
  def self.http(verb, url, post_body=nil, put_data=nil, &block)
11
12
  handle = Thread.current[:curb_curl] ||= Curl::Easy.new
12
13
  handle.reset
@@ -48,7 +49,11 @@ module Curl
48
49
 
49
50
  def self.urlalize(url, params={})
50
51
  uri = URI(url)
51
- uri.query = params.map {|k,v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join("&")
52
+ # early return if we didn't specify any extra params
53
+ return uri.to_s if (params || {}).empty?
54
+
55
+ params_query = URI.encode_www_form(params || {})
56
+ uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
52
57
  uri.to_s
53
58
  end
54
59
 
data/lib/curl/easy.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Curl
2
3
  class Easy
3
4
 
@@ -19,9 +20,9 @@ module Curl
19
20
  # easy.status => String
20
21
  #
21
22
  def status
22
- # Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF'
23
- statuses = self.header_str.scan(/HTTP\/\d\.\d\s(\d+\s.*)\r\n/).map{ |match| match[0] }
24
- statuses.last.strip
23
+ # Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP (Opt:)Reason-Phrase CRLF'
24
+ statuses = self.header_str.scan(/HTTP\/\d(\.\d)?\s(\d+\s.*)\r\n/).map{ |match| match[1] }
25
+ statuses.last.strip if statuses.length > 0
25
26
  end
26
27
 
27
28
  #
@@ -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
data/lib/curl/multi.rb CHANGED
@@ -1,12 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  module Curl
2
-
3
3
  class Multi
4
4
  class << self
5
5
  # call-seq:
6
6
  # Curl::Multi.get(['url1','url2','url3','url4','url5'], :follow_location => true) do|easy|
7
7
  # easy
8
8
  # end
9
- #
9
+ #
10
10
  # Blocking call to fetch multiple url's in parallel.
11
11
  def get(urls, easy_options={}, multi_options={}, &blk)
12
12
  url_confs = []
@@ -25,10 +25,10 @@ module Curl
25
25
  # {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
26
26
  # easy_handle_on_request_complete
27
27
  # end
28
- #
28
+ #
29
29
  # Blocking call to POST multiple form's in parallel.
30
- #
31
- # urls_with_config: is a hash of url's pointing to the postfields to send
30
+ #
31
+ # urls_with_config: is a hash of url's pointing to the postfields to send
32
32
  # easy_options: are a set of common options to set on all easy handles
33
33
  # multi_options: options to set on the Curl::Multi handle
34
34
  #
@@ -49,10 +49,10 @@ module Curl
49
49
  # {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
50
50
  # easy_handle_on_request_complete
51
51
  # end
52
- #
52
+ #
53
53
  # Blocking call to POST multiple form's in parallel.
54
- #
55
- # urls_with_config: is a hash of url's pointing to the postfields to send
54
+ #
55
+ # urls_with_config: is a hash of url's pointing to the postfields to send
56
56
  # easy_options: are a set of common options to set on all easy handles
57
57
  # multi_options: options to set on the Curl::Multi handle
58
58
  #
@@ -79,7 +79,7 @@ module Curl
79
79
  # Blocking call to issue multiple HTTP requests with varying verb's.
80
80
  #
81
81
  # urls_with_config: is a hash of url's pointing to the easy handle options as well as the special option :method, that can by one of [:get, :post, :put, :delete, :head], when no verb is provided e.g. :method => nil -> GET is used
82
- # multi_options: options for the multi handle
82
+ # multi_options: options for the multi handle
83
83
  # blk: a callback, that yeilds when a handle is completed
84
84
  #
85
85
  def http(urls_with_config, multi_options={}, &blk)
@@ -128,7 +128,7 @@ module Curl
128
128
 
129
129
  # headers is a special key
130
130
  headers.each {|k,v| easy.headers[k] = v } if headers
131
-
131
+
132
132
  #
133
133
  # use the remaining options as specific configuration to the easy handle
134
134
  # bad options should raise an undefined method error
@@ -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:
@@ -175,7 +176,7 @@ module Curl
175
176
  # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
176
177
  #
177
178
  # will create 2 new files file1.txt and file2.txt
178
- #
179
+ #
179
180
  # 2 files will be opened, and remain open until the call completes
180
181
  #
181
182
  # when using the :post or :put method, urls should be a hash, including the individual post fields per post
@@ -242,7 +243,45 @@ module Curl
242
243
  }
243
244
  raise errors unless errors.empty?
244
245
  end
246
+ end
245
247
 
248
+ def cancel!
249
+ requests.each do |_,easy|
250
+ remove(easy)
251
+ end
246
252
  end
253
+
254
+ def idle?
255
+ requests.empty?
256
+ end
257
+
258
+ def requests
259
+ @requests ||= {}
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
data/tests/helper.rb CHANGED
@@ -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')
@@ -18,7 +19,7 @@ rescue LoadError
18
19
  end
19
20
  require 'fileutils'
20
21
 
21
- $TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/}#{URI.escape(File.expand_path(__FILE__).tr('\\','/'))}"
22
+ $TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/}#{File.expand_path(__FILE__).tr('\\','/')}"
22
23
 
23
24
  require 'thread'
24
25
  require 'webrick'
@@ -30,19 +31,22 @@ require 'webrick'
30
31
  TEST_SINGLE_THREADED=false
31
32
 
32
33
  # keep webrick quiet
33
- class ::WEBrick::HTTPServer
34
+ ::WEBrick::HTTPServer.send(:remove_method,:access_log) if ::WEBrick::HTTPServer.instance_methods.include?(:access_log)
35
+ ::WEBrick::BasicLog.send(:remove_method,:log) if ::WEBrick::BasicLog.instance_methods.include?(:log)
36
+
37
+ ::WEBrick::HTTPServer.class_eval do
34
38
  def access_log(config, req, res)
35
39
  # nop
36
40
  end
37
41
  end
38
- class ::WEBrick::BasicLog
42
+ ::WEBrick::BasicLog.class_eval do
39
43
  def log(level, data)
40
44
  # nop
41
45
  end
42
46
  end
43
47
 
44
48
  #
45
- # Simple test server to record number of times a request is sent/recieved of a specific
49
+ # Simple test server to record number of times a request is sent/recieved of a specific
46
50
  # request type, e.g. GET,POST,PUT,DELETE
47
51
  #
48
52
  class TestServlet < WEBrick::HTTPServlet::AbstractServlet
@@ -94,7 +98,7 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
94
98
  if params and params['s'] == '500'
95
99
  res.status = 500
96
100
  elsif params and params['c']
97
- cookie = URI.decode_www_form(params['c'])[0][0].split('=')
101
+ cookie = URI.decode_www_form_component(params['c']).split('=')
98
102
  res.cookies << WEBrick::Cookie.new(*cookie)
99
103
  else
100
104
  respond_with("POST\n#{req.body}",req,res)
@@ -147,7 +151,7 @@ module TestServerMethods
147
151
  rd.close
148
152
  rd = nil
149
153
 
150
- # start up a webrick server for testing delete
154
+ # start up a webrick server for testing delete
151
155
  server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
152
156
 
153
157
  server.mount(servlet.path, servlet)
@@ -163,7 +167,7 @@ module TestServerMethods
163
167
  rd.read
164
168
  rd.close
165
169
  else
166
- # start up a webrick server for testing delete
170
+ # start up a webrick server for testing delete
167
171
  @server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
168
172
 
169
173
  @server.mount(servlet.path, servlet)
@@ -204,3 +208,81 @@ module TestServerMethods
204
208
  rescue Errno::EADDRINUSE
205
209
  end
206
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