curb 0.7.15 → 0.7.18

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/lib/curl/multi.rb ADDED
@@ -0,0 +1,244 @@
1
+ module Curl
2
+
3
+ class Multi
4
+ class << self
5
+ # call-seq:
6
+ # Curl::Multi.get('url1','url2','url3','url4','url5', :follow_location => true) do|easy|
7
+ # easy
8
+ # end
9
+ #
10
+ # Blocking call to fetch multiple url's in parallel.
11
+ def get(urls, easy_options={}, multi_options={}, &blk)
12
+ url_confs = []
13
+ urls.each do|url|
14
+ url_confs << {:url => url, :method => :get}.merge(easy_options)
15
+ end
16
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) if blk }
17
+ end
18
+
19
+ # call-seq:
20
+ #
21
+ # Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
22
+ # {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
23
+ # {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}],
24
+ # { :follow_location => true, :multipart_form_post => true },
25
+ # {:pipeline => true }) do|easy|
26
+ # easy_handle_on_request_complete
27
+ # end
28
+ #
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
32
+ # easy_options: are a set of common options to set on all easy handles
33
+ # multi_options: options to set on the Curl::Multi handle
34
+ #
35
+ def post(urls_with_config, easy_options={}, multi_options={}, &blk)
36
+ url_confs = []
37
+ urls_with_config.each do|uconf|
38
+ url_confs << uconf.merge(:method => :post).merge(easy_options)
39
+ end
40
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
41
+ end
42
+
43
+ # call-seq:
44
+ #
45
+ # Curl::Multi.put([{:url => 'url1', :put_data => "some message"},
46
+ # {:url => 'url2', :put_data => IO.read('filepath')},
47
+ # {:url => 'url3', :put_data => "maybe another string or socket?"],
48
+ # {:follow_location => true},
49
+ # {:pipeline => true }) do|easy|
50
+ # easy_handle_on_request_complete
51
+ # end
52
+ #
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
56
+ # easy_options: are a set of common options to set on all easy handles
57
+ # multi_options: options to set on the Curl::Multi handle
58
+ #
59
+ def put(urls_with_config, easy_options={}, multi_options={}, &blk)
60
+ url_confs = []
61
+ urls_with_config.each do|uconf|
62
+ url_confs << uconf.merge(:method => :put).merge(easy_options)
63
+ end
64
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
65
+ end
66
+
67
+
68
+ # call-seq:
69
+ #
70
+ # Curl::Multi.http( [
71
+ # { :url => 'url1', :method => :post,
72
+ # :post_fields => {'field1' => 'value1', 'field2' => 'value2'} },
73
+ # { :url => 'url2', :method => :get,
74
+ # :follow_location => true, :max_redirects => 3 },
75
+ # { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') },
76
+ # { :url => 'url4', :method => :head }
77
+ # ], {:pipeline => true})
78
+ #
79
+ # Blocking call to issue multiple HTTP requests with varying verb's.
80
+ #
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
83
+ # blk: a callback, that yeilds when a handle is completed
84
+ #
85
+ def http(urls_with_config, multi_options={}, &blk)
86
+ m = Curl::Multi.new
87
+
88
+ # maintain a sane number of easy handles
89
+ multi_options[:max_connects] = max_connects = multi_options.key?(:max_connects) ? multi_options[:max_connects] : 10
90
+
91
+ free_handles = [] # keep a list of free easy handles
92
+
93
+ # configure the multi handle
94
+ multi_options.each { |k,v| m.send("#{k}=", v) }
95
+ callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_body,:on_header]
96
+
97
+ add_free_handle = proc do|conf, easy|
98
+ c = conf.dup # avoid being destructive to input
99
+ url = c.delete(:url)
100
+ method = c.delete(:method)
101
+ headers = c.delete(:headers)
102
+
103
+ easy = Curl::Easy.new if easy.nil?
104
+
105
+ easy.url = url
106
+
107
+ # assign callbacks
108
+ callbacks.each do |cb|
109
+ cbproc = c.delete(cb)
110
+ easy.send(cb,&cbproc) if cbproc
111
+ end
112
+
113
+ case method
114
+ when :post
115
+ fields = c.delete(:post_fields)
116
+ # set the post post using the url fields
117
+ easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&')
118
+ when :put
119
+ easy.put_data = c.delete(:put_data)
120
+ when :head
121
+ easy.head = true
122
+ when :delete
123
+ easy.delete = true
124
+ when :get
125
+ else
126
+ # XXX: nil is treated like a GET
127
+ end
128
+
129
+ # headers is a special key
130
+ headers.each {|k,v| easy.headers[k] = v } if headers
131
+
132
+ #
133
+ # use the remaining options as specific configuration to the easy handle
134
+ # bad options should raise an undefined method error
135
+ #
136
+ c.each { |k,v| easy.send("#{k}=",v) }
137
+
138
+ easy.on_complete {|curl,code|
139
+ free_handles << curl
140
+ blk.call(curl,code,method) if blk
141
+ }
142
+ m.add(easy)
143
+ end
144
+
145
+ max_connects.times do
146
+ conf = urls_with_config.pop
147
+ add_free_handle.call conf, nil
148
+ break if urls_with_config.empty?
149
+ end
150
+
151
+ consume_free_handles = proc do
152
+ # as we idle consume free handles
153
+ if urls_with_config.size > 0 && free_handles.size > 0
154
+ easy = free_handles.pop
155
+ conf = urls_with_config.pop
156
+ add_free_handle.call conf, easy
157
+ end
158
+ end
159
+
160
+ until urls_with_config.empty?
161
+ m.perform do
162
+ consume_free_handles.call
163
+ end
164
+ consume_free_handles.call
165
+ end
166
+ free_handles = nil
167
+ end
168
+
169
+ # call-seq:
170
+ #
171
+ # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
172
+ #
173
+ # will create 2 new files file1.txt and file2.txt
174
+ #
175
+ # 2 files will be opened, and remain open until the call completes
176
+ #
177
+ # when using the :post or :put method, urls should be a hash, including the individual post fields per post
178
+ #
179
+ def download(urls,easy_options={},multi_options={},download_paths=nil,&blk)
180
+ errors = []
181
+ procs = []
182
+ files = []
183
+ urls_with_config = []
184
+ url_to_download_paths = {}
185
+
186
+ urls.each_with_index do|urlcfg,i|
187
+ if urlcfg.is_a?(Hash)
188
+ url = url[:url]
189
+ else
190
+ url = urlcfg
191
+ end
192
+
193
+ if download_paths and download_paths[i]
194
+ download_path = download_paths[i]
195
+ else
196
+ download_path = File.basename(url)
197
+ end
198
+
199
+ file = lambda do|dp|
200
+ file = File.open(dp,"wb")
201
+ procs << (lambda {|data| file.write data; data.size })
202
+ files << file
203
+ file
204
+ end.call(download_path)
205
+
206
+ if urlcfg.is_a?(Hash)
207
+ urls_with_config << urlcfg.merge({:on_body => procs.last}.merge(easy_options))
208
+ else
209
+ urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options)
210
+ end
211
+ url_to_download_paths[url] = {:path => download_path, :file => file} # store for later
212
+ end
213
+
214
+ if blk
215
+ # when injecting the block, ensure file is closed before yielding
216
+ Curl::Multi.http(urls_with_config, multi_options) do |c,code,method|
217
+ info = url_to_download_paths[c.url]
218
+ begin
219
+ file = info[:file]
220
+ files.reject!{|f| f == file }
221
+ file.close
222
+ rescue => e
223
+ errors << e
224
+ end
225
+ blk.call(c,info[:path])
226
+ end
227
+ else
228
+ Curl::Multi.http(urls_with_config, multi_options)
229
+ end
230
+
231
+ ensure
232
+ files.each {|f|
233
+ begin
234
+ f.close
235
+ rescue => e
236
+ errors << e
237
+ end
238
+ }
239
+ raise errors unless errors.empty?
240
+ end
241
+
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+ require 'webrick'
3
+ class ::WEBrick::HTTPServer ; def access_log(config, req, res) ; end ; end
4
+ class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
5
+
6
+ class BugCrashOnDebug < Test::Unit::TestCase
7
+
8
+ def test_on_debug
9
+ server = WEBrick::HTTPServer.new( :Port => 9999 )
10
+ server.mount_proc("/test") do|req,res|
11
+ res.body = "hi"
12
+ res['Content-Type'] = "text/html"
13
+ end
14
+
15
+ thread = Thread.new(server) do|srv|
16
+ srv.start
17
+ end
18
+
19
+ c = Curl::Easy.new('http://localhost:9999/test')
20
+ c.on_progress do|x|
21
+ raise "error"
22
+ end
23
+ c.perform
24
+
25
+ assert false, "should not reach this point"
26
+
27
+ rescue => e
28
+ assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
29
+ c.close
30
+ ensure
31
+ server.shutdown
32
+ end
33
+ end
@@ -8,7 +8,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
8
8
  end
9
9
 
10
10
  def test_download_url_to_file_via_string
11
- dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
11
+ dl_url = "http://localhost:9129/ext/curb_easy.c"
12
12
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
13
13
 
14
14
  curb = Curl::Easy.download(dl_url, dl_path)
@@ -19,7 +19,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  def test_download_url_to_file_via_file_io
22
- dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
22
+ dl_url = "http://localhost:9129/ext/curb_easy.c"
23
23
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
24
24
  io = File.open(dl_path, 'wb')
25
25
 
@@ -32,7 +32,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
32
32
  end
33
33
 
34
34
  def test_download_url_to_file_via_io
35
- dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
35
+ dl_url = "http://localhost:9129/ext/curb_easy.c"
36
36
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
37
37
  reader, writer = IO.pipe
38
38
 
@@ -62,7 +62,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
62
62
  end
63
63
 
64
64
  def test_download_bad_url_gives_404
65
- dl_url = "http://127.0.0.1:9129/this_file_does_not_exist.html"
65
+ dl_url = "http://localhost:9129/this_file_does_not_exist.html"
66
66
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
67
67
 
68
68
  curb = Curl::Easy.download(dl_url, dl_path)
@@ -461,7 +461,8 @@ class TestCurbCurlEasy < Test::Unit::TestCase
461
461
  def test_ssl_verify_host
462
462
  c = Curl::Easy.new
463
463
  assert c.ssl_verify_host?
464
- assert !c.ssl_verify_host = false
464
+ c.ssl_verify_host = 0
465
+ c.ssl_verify_host = false
465
466
  assert !c.ssl_verify_host?
466
467
  end
467
468
 
@@ -507,6 +508,17 @@ class TestCurbCurlEasy < Test::Unit::TestCase
507
508
  assert c.ignore_content_length?
508
509
  end
509
510
 
511
+ def test_resolve_mode
512
+ c = Curl::Easy.new
513
+ assert_equal :auto, c.resolve_mode
514
+ c.resolve_mode = :ipv4
515
+ assert_equal :ipv4, c.resolve_mode
516
+ c.resolve_mode = :ipv6
517
+ assert_equal :ipv6, c.resolve_mode
518
+
519
+ assert_raises(ArgumentError) { c.resolve_mode = :bad }
520
+ end
521
+
510
522
  def test_enable_cookies
511
523
  c = Curl::Easy.new
512
524
  assert !c.enable_cookies?
@@ -572,13 +584,20 @@ class TestCurbCurlEasy < Test::Unit::TestCase
572
584
  def test_post_remote_is_easy_handle
573
585
  # see: http://pastie.org/560852 and
574
586
  # http://groups.google.com/group/curb---ruby-libcurl-bindings/browse_thread/thread/216bb2d9b037f347?hl=en
575
- [:post, :get,:head,:delete].each do |method|
576
- count = 0
577
- curl = Curl::Easy.send("http_#{method}", TestServlet.url) do|c|
578
- count += 1
579
- assert_equal Curl::Easy, c.class
587
+ [:post, :get, :head, :delete].each do |method|
588
+ retries = 0
589
+ begin
590
+ count = 0
591
+ curl = Curl::Easy.send("http_#{method}", TestServlet.url) do|c|
592
+ count += 1
593
+ assert_equal Curl::Easy, c.class
594
+ end
595
+ assert_equal 1, count, "For request method: #{method.to_s.upcase}"
596
+ rescue Curl::Err::HostResolutionError => e # travis-ci.org fails to resolve... try again?
597
+ retries+=1
598
+ retry if retries < 3
599
+ raise e
580
600
  end
581
- assert_equal 1, count, "For request method: #{method.to_s.upcase}"
582
601
  end
583
602
  end
584
603
 
@@ -664,6 +683,17 @@ class TestCurbCurlEasy < Test::Unit::TestCase
664
683
  assert_match /message$/, curl.body_str
665
684
  end
666
685
 
686
+ # https://github.com/taf2/curb/issues/101
687
+ def test_put_data_null_bytes
688
+ curl = Curl::Easy.new(TestServlet.url)
689
+ curl.put_data = "a\0b"
690
+
691
+ curl.perform
692
+
693
+ assert_match /^PUT/, curl.body_str
694
+ assert_match "a\0b", curl.body_str
695
+ end
696
+
667
697
  def test_put_nil_data_no_crash
668
698
  curl = Curl::Easy.new(TestServlet.url)
669
699
  curl.put_data = nil
@@ -700,7 +730,9 @@ class TestCurbCurlEasy < Test::Unit::TestCase
700
730
 
701
731
  def test_cert_with_password
702
732
  curl = Curl::Easy.new(TestServlet.url)
703
- curl.cert= File.join(File.dirname(__FILE__),"cert.pem:password")
733
+ path = File.join(File.dirname(__FILE__),"cert.pem")
734
+ curl.certpassword = 'password'
735
+ curl.cert = path
704
736
  assert_match /cert.pem$/,curl.cert
705
737
  end
706
738
 
@@ -888,6 +920,40 @@ class TestCurbCurlEasy < Test::Unit::TestCase
888
920
 
889
921
  end
890
922
 
923
+ def test_get_set_multi_on_easy
924
+ easy = Curl::Easy.new
925
+ assert_nil easy.multi
926
+ multi = Curl::Multi.new
927
+ easy.multi = multi
928
+ assert_not_nil easy.multi
929
+ assert_equal multi, easy.multi
930
+ end
931
+
932
+ def test_raise_on_progress
933
+ c = Curl::Easy.new($TEST_URL)
934
+ c.on_progress {|w,x,y,z| raise "error" }
935
+ c.perform
936
+ rescue => e
937
+ assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
938
+ c.close
939
+ end
940
+
941
+ def test_raise_on_success
942
+ c = Curl::Easy.new($TEST_URL)
943
+ c.on_success {|x| raise "error" }
944
+ c.perform
945
+ rescue => e
946
+ assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
947
+ c.close
948
+ end
949
+
950
+ def test_raise_on_debug
951
+ c = Curl::Easy.new($TEST_URL)
952
+ c.on_debug { raise "error" }
953
+ c.perform
954
+ assert true, "raise in on debug has no effect"
955
+ end
956
+
891
957
  include TestServerMethods
892
958
 
893
959
  def setup
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class TestCurbCurlEasySetOpt < Test::Unit::TestCase
4
+ def setup
5
+ @easy = Curl::Easy.new
6
+ end
7
+
8
+ def test_opt_verbose
9
+ @easy.set :verbose, true
10
+ assert @easy.verbose?
11
+ end
12
+
13
+ def test_opt_header
14
+ @easy.set :header, true
15
+ end
16
+
17
+ def test_opt_noprogress
18
+ @easy.set :noprogress, true
19
+ end
20
+
21
+ def test_opt_nosignal
22
+ @easy.set :nosignal, true
23
+ end
24
+
25
+ def test_opt_url
26
+ url = "http://google.com/"
27
+ @easy.set :url, url
28
+ assert_equal url, @easy.url
29
+ end
30
+
31
+ end
metadata CHANGED
@@ -1,41 +1,36 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: curb
3
- version: !ruby/object:Gem::Version
4
- hash: 29
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.18
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 7
9
- - 15
10
- version: 0.7.15
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Ross Bamford
14
9
  - Todd A. Fisher
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2011-03-20 00:00:00 -04:00
20
- default_executable:
13
+ date: 2012-01-16 00:00:00.000000000 Z
21
14
  dependencies: []
22
-
23
- description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings for the libcurl(3), a fully-featured client-side URL transfer library. cURL and libcurl live at http://curl.haxx.se/
15
+ description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
16
+ for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
17
+ libcurl live at http://curl.haxx.se/
24
18
  email: todd.fisher@gmail.com
25
19
  executables: []
26
-
27
- extensions:
20
+ extensions:
28
21
  - ext/extconf.rb
29
- extra_rdoc_files:
22
+ extra_rdoc_files:
30
23
  - LICENSE
31
24
  - README
32
- files:
25
+ files:
33
26
  - LICENSE
34
27
  - README
35
28
  - Rakefile
36
29
  - doc.rb
37
30
  - ext/extconf.rb
38
31
  - lib/curb.rb
32
+ - lib/curl/easy.rb
33
+ - lib/curl/multi.rb
39
34
  - lib/curl.rb
40
35
  - ext/curb.c
41
36
  - ext/curb_easy.c
@@ -51,6 +46,7 @@ files:
51
46
  - ext/curb_postfield.h
52
47
  - ext/curb_upload.h
53
48
  - tests/alltests.rb
49
+ - tests/bug_crash_on_progress.rb
54
50
  - tests/bug_curb_easy_blocks_ruby_threads.rb
55
51
  - tests/bug_curb_easy_post_with_string_no_content_length_header.rb
56
52
  - tests/bug_instance_post_differs_from_class_post.rb
@@ -64,49 +60,42 @@ files:
64
60
  - tests/require_last_or_segfault_script.rb
65
61
  - tests/tc_curl_download.rb
66
62
  - tests/tc_curl_easy.rb
63
+ - tests/tc_curl_easy_setopt.rb
67
64
  - tests/tc_curl_multi.rb
68
65
  - tests/tc_curl_postfield.rb
69
66
  - tests/timeout.rb
70
67
  - tests/timeout_server.rb
71
68
  - tests/unittests.rb
72
- has_rdoc: true
73
69
  homepage: http://curb.rubyforge.org/
74
70
  licenses: []
75
-
76
71
  post_install_message:
77
- rdoc_options:
72
+ rdoc_options:
78
73
  - --main
79
74
  - README
80
- require_paths:
75
+ require_paths:
81
76
  - lib
82
77
  - ext
83
- required_ruby_version: !ruby/object:Gem::Requirement
78
+ required_ruby_version: !ruby/object:Gem::Requirement
84
79
  none: false
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- hash: 3
89
- segments:
90
- - 0
91
- version: "0"
92
- required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
85
  none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
101
90
  requirements: []
102
-
103
91
  rubyforge_project: curb
104
- rubygems_version: 1.4.1
92
+ rubygems_version: 1.8.10
105
93
  signing_key:
106
94
  specification_version: 3
107
95
  summary: Ruby libcurl bindings
108
- test_files:
96
+ test_files:
109
97
  - tests/alltests.rb
98
+ - tests/bug_crash_on_progress.rb
110
99
  - tests/bug_curb_easy_blocks_ruby_threads.rb
111
100
  - tests/bug_curb_easy_post_with_string_no_content_length_header.rb
112
101
  - tests/bug_instance_post_differs_from_class_post.rb
@@ -120,6 +109,7 @@ test_files:
120
109
  - tests/require_last_or_segfault_script.rb
121
110
  - tests/tc_curl_download.rb
122
111
  - tests/tc_curl_easy.rb
112
+ - tests/tc_curl_easy_setopt.rb
123
113
  - tests/tc_curl_multi.rb
124
114
  - tests/tc_curl_postfield.rb
125
115
  - tests/timeout.rb