curb 0.9.4 → 0.9.10

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