ghazel-curb 0.6.2.3 → 0.7.9.1

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/curb.rb CHANGED
@@ -26,16 +26,24 @@ module Curl
26
26
  def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
27
27
  curl = Curl::Easy.new(url, &blk)
28
28
 
29
- File.open(filename, "wb") do |output|
30
- old_on_body = curl.on_body do |data|
29
+ output = if filename.is_a? IO
30
+ filename.binmode if filename.respond_to?(:binmode)
31
+ filename
32
+ else
33
+ File.open(filename, 'wb')
34
+ end
35
+
36
+ begin
37
+ old_on_body = curl.on_body do |data|
31
38
  result = old_on_body ? old_on_body.call(data) : data.length
32
39
  output << data if result == data.length
33
40
  result
34
41
  end
35
-
36
42
  curl.perform
37
- end
38
-
43
+ ensure
44
+ output.close rescue IOError
45
+ end
46
+
39
47
  return curl
40
48
  end
41
49
  end
@@ -142,6 +150,7 @@ module Curl
142
150
  m = Curl::Multi.new
143
151
  # configure the multi handle
144
152
  multi_options.each { |k,v| m.send("#{k}=", v) }
153
+ callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_body,:on_header]
145
154
 
146
155
  urls_with_config.each do|conf|
147
156
  c = conf.dup # avoid being destructive to input
@@ -151,6 +160,12 @@ module Curl
151
160
 
152
161
  easy = Curl::Easy.new(url)
153
162
 
163
+ # assign callbacks
164
+ callbacks.each do |cb|
165
+ cbproc = c.delete(cb)
166
+ easy.send(cb,&cbproc) if cbproc
167
+ end
168
+
154
169
  case method
155
170
  when :post
156
171
  fields = c.delete(:post_fields)
@@ -181,6 +196,80 @@ module Curl
181
196
  end
182
197
  m.perform
183
198
  end
199
+
200
+ # call-seq:
201
+ #
202
+ # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
203
+ #
204
+ # will create 2 new files file1.txt and file2.txt
205
+ #
206
+ # 2 files will be opened, and remain open until the call completes
207
+ #
208
+ # when using the :post or :put method, urls should be a hash, including the individual post fields per post
209
+ #
210
+ def download(urls,easy_options={},multi_options={},download_paths=nil,&blk)
211
+ errors = []
212
+ procs = []
213
+ files = []
214
+ urls_with_config = []
215
+ url_to_download_paths = {}
216
+
217
+ urls.each_with_index do|urlcfg,i|
218
+ if urlcfg.is_a?(Hash)
219
+ url = url[:url]
220
+ else
221
+ url = urlcfg
222
+ end
223
+
224
+ if download_paths and download_paths[i]
225
+ download_path = download_paths[i]
226
+ else
227
+ download_path = File.basename(url)
228
+ end
229
+
230
+ file = lambda do|dp|
231
+ file = File.open(dp,"wb")
232
+ procs << (lambda {|data| file.write data; data.size })
233
+ files << file
234
+ file
235
+ end.call(download_path)
236
+
237
+ if urlcfg.is_a?(Hash)
238
+ urls_with_config << urlcfg.merge({:on_body => procs.last}.merge(easy_options))
239
+ else
240
+ urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options)
241
+ end
242
+ url_to_download_paths[url] = {:path => download_path, :file => file} # store for later
243
+ end
244
+
245
+ if blk
246
+ # when injecting the block, ensure file is closed before yielding
247
+ Curl::Multi.http(urls_with_config, multi_options) do |c,code,method|
248
+ info = url_to_download_paths[c.url]
249
+ begin
250
+ file = info[:file]
251
+ files.reject!{|f| f == file }
252
+ file.close
253
+ rescue => e
254
+ errors << e
255
+ end
256
+ blk.call(c,info[:path])
257
+ end
258
+ else
259
+ Curl::Multi.http(urls_with_config, multi_options)
260
+ end
261
+
262
+ ensure
263
+ files.each {|f|
264
+ begin
265
+ f.close
266
+ rescue => e
267
+ errors << e
268
+ end
269
+ }
270
+ raise errors unless errors.empty?
271
+ end
272
+
184
273
  end
185
274
  end
186
275
  end
@@ -0,0 +1,83 @@
1
+ =begin
2
+ From jwhitmire
3
+ Todd, I'm trying to use curb to post data to a REST url. We're using it to post support questions from our iphone app directly to tender. The post looks good to me, but curl is not adding the content-length header so I get a 411 length required response from the server.
4
+
5
+ Here's my post block, do you see anything obvious? Do I need to manually add the Content-Length header?
6
+
7
+ c = Curl::Easy.http_post(url) do |curl|
8
+ curl.headers["User-Agent"] = "Curl/Ruby"
9
+ if user
10
+ curl.headers["X-Multipass"] = user.multipass
11
+ else
12
+ curl.headers["X-Tender-Auth"] = TOKEN
13
+ end
14
+ curl.headers["Accept"] = "application/vnd.tender-v1+json"
15
+
16
+ curl.post_body = params.map{|f,k| "#{curl.escape(f)}=#{curl.escape(k)}"}.join('&')
17
+
18
+ curl.verbose = true
19
+ curl.follow_location = true
20
+ curl.enable_cookies = true
21
+ end
22
+ Any insight you care to share would be helpful. Thanks.
23
+ =end
24
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
25
+ require 'webrick'
26
+ class ::WEBrick::HTTPServer ; def access_log(config, req, res) ; end ; end
27
+ class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
28
+
29
+ class BugCurbEasyPostWithStringNoContentLengthHeader < Test::Unit::TestCase
30
+ def test_bug_workaround
31
+ server = WEBrick::HTTPServer.new( :Port => 9999 )
32
+ server.mount_proc("/test") do|req,res|
33
+ assert_equal '15', req['Content-Length']
34
+ res.body = "hi"
35
+ res['Content-Type'] = "text/html"
36
+ end
37
+
38
+ thread = Thread.new(server) do|srv|
39
+ srv.start
40
+ end
41
+ params = {:cat => "hat", :foo => "bar"}
42
+
43
+ post_body = params.map{|f,k| "#{Curl::Easy.new.escape(f)}=#{Curl::Easy.new.escape(k)}"}.join('&')
44
+ c = Curl::Easy.http_post("http://127.0.0.1:9999/test",post_body) do |curl|
45
+ curl.headers["User-Agent"] = "Curl/Ruby"
46
+ curl.headers["X-Tender-Auth"] = "A Token"
47
+ curl.headers["Accept"] = "application/vnd.tender-v1+json"
48
+
49
+ curl.follow_location = true
50
+ curl.enable_cookies = true
51
+ end
52
+
53
+ server.shutdown
54
+ thread.join
55
+ end
56
+ def test_bug
57
+ server = WEBrick::HTTPServer.new( :Port => 9999 )
58
+ server.mount_proc("/test") do|req,res|
59
+ assert_equal '15', req['Content-Length']
60
+ res.body = "hi"
61
+ res['Content-Type'] = "text/html"
62
+ end
63
+
64
+ thread = Thread.new(server) do|srv|
65
+ srv.start
66
+ end
67
+ params = {:cat => "hat", :foo => "bar"}
68
+
69
+ c = Curl::Easy.http_post("http://127.0.0.1:9999/test") do |curl|
70
+ curl.headers["User-Agent"] = "Curl/Ruby"
71
+ curl.headers["X-Tender-Auth"] = "A Token"
72
+ curl.headers["Accept"] = "application/vnd.tender-v1+json"
73
+
74
+ curl.post_body = params.map{|f,k| "#{curl.escape(f)}=#{curl.escape(k)}"}.join('&')
75
+
76
+ curl.follow_location = true
77
+ curl.enable_cookies = true
78
+ end
79
+
80
+ server.shutdown
81
+ thread.join
82
+ end
83
+ end
@@ -6,5 +6,9 @@
6
6
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','ext'))
7
7
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
8
8
  require 'curb'
9
- multi = Curl::Multi.new
10
- exit
9
+
10
+ class BugMultiSegfault < Test::Unit::TestCase
11
+ def test_bug
12
+ multi = Curl::Multi.new
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # From GICodeWarrior:
2
+ #
3
+ # $ ruby crash_curb.rb
4
+ # crash_curb.rb:7: [BUG] Segmentation fault
5
+ # ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]
6
+ #
7
+ # Aborted
8
+ # crash_curb.rb:
9
+ # #!/usr/bin/ruby
10
+ # require 'rubygems'
11
+ # require 'curb'
12
+ #
13
+ # curl = Curl::Easy.new('http://example.com/')
14
+ # curl.multipart_form_post = true
15
+ # curl.http_post(Curl::PostField.file('test', 'test.xml'){'example data'})
16
+ # Ubuntu 9.10
17
+ # curb gem version 0.6.2.1
18
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
19
+
20
+ class BugPostFieldsCrash < Test::Unit::TestCase
21
+ def test_crash
22
+ curl = Curl::Easy.new('http://example.com/')
23
+ curl.multipart_form_post = true
24
+ curl.http_post(Curl::PostField.file('test', 'test.xml'){'example data'})
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # Not sure if this is an IRB bug, but thought you guys should know.
2
+ #
3
+ # ** My Ruby version: ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]
4
+ # ** Version of Rubygems: 1.3.5
5
+ # ** Version of the Curb gem: 0.6.6.0
6
+ #
7
+ #
8
+ # Transcript of IRB session:
9
+ # ------------------------------------------------------------------------------------------------------------------
10
+ # irb(main):001:0> a = {
11
+ # irb(main):002:1* :type => :pie,
12
+ # irb(main):003:1* :series => {
13
+ # irb(main):004:2* :names => [:a,:b],
14
+ # irb(main):005:2* :values => [70,30],
15
+ # irb(main):006:2* :colors => [:red,:green]
16
+ # irb(main):007:2> },
17
+ # irb(main):008:1* :output_format => :png
18
+ # irb(main):009:1> }
19
+ # => {:type=>:pie, :output_format=>:png, :series=>{:names=>[:a, :b], :values=>[70, 30], :colors=>[:red, :green]}}
20
+ # irb(main):010:0> post = []
21
+ # => []
22
+ # irb(main):011:0> require 'rubygems'
23
+ # => true
24
+ # irb(main):012:0> require 'curb'
25
+ # => true
26
+ # irb(main):013:0> include Curl
27
+ # => Object
28
+ # irb(main):014:0> a.each_pair do |k,v|
29
+ # irb(main):015:1* post << PostField.content(k,v)
30
+ # irb(main):016:1> end
31
+ # => {:type=>:pie, :output_format=>:png, :series=>{:names=>[:a, :b], :values=>[70, 30], :colors=>[:red, :green]}}
32
+ # irb(main):017:0> post
33
+ # /usr/lib/ruby/1.8/irb.rb:302: [BUG] Segmentation fault
34
+ # ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]
35
+ #
36
+ # Aborted
37
+ # ------------------------------------------------------------------------------------------------------------------
38
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
39
+
40
+ class BugPostFieldsCrash2 < Test::Unit::TestCase
41
+ def test_crash
42
+ a = {
43
+ :type => :pie,
44
+ :series => {
45
+ :names => [:a,:b],
46
+ :values => [70,30],
47
+ :colors => [:red,:green]
48
+ },
49
+ :output_format => :png
50
+ }
51
+ post = []
52
+ a.each_pair do |k,v|
53
+ post << Curl::PostField.content(k,v)
54
+ end
55
+ post.inspect
56
+ end
57
+ end
data/tests/bugtests.rb ADDED
@@ -0,0 +1,9 @@
1
+ $: << $TESTDIR = File.expand_path(File.dirname(__FILE__))
2
+ puts "start"
3
+ begin
4
+ Dir[File.join($TESTDIR, 'bug_*.rb')].each { |lib| require lib }
5
+ rescue Object => e
6
+ puts e.message
7
+ ensure
8
+ puts "done"
9
+ end
data/tests/helper.rb CHANGED
@@ -11,6 +11,7 @@ $:.unshift($EXTDIR)
11
11
 
12
12
  require 'curb'
13
13
  require 'test/unit'
14
+ require 'fileutils'
14
15
 
15
16
  $TEST_URL = "file://#{URI.escape(File.expand_path(__FILE__).tr('\\','/').tr(':','|'))}"
16
17
 
@@ -73,14 +74,18 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
73
74
  end
74
75
 
75
76
  def do_POST(req,res)
76
- if req.body
77
- params = {}
78
- req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
79
- end
80
- if params and params['s'] == '500'
81
- res.status = 500
77
+ if req.query['filename'].nil?
78
+ if req.body
79
+ params = {}
80
+ req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
81
+ end
82
+ if params and params['s'] == '500'
83
+ res.status = 500
84
+ else
85
+ respond_with("POST\n#{req.body}",req,res)
86
+ end
82
87
  else
83
- respond_with("POST\n#{req.body}",req,res)
88
+ respond_with(req.query['filename'],req,res)
84
89
  end
85
90
  end
86
91
 
@@ -93,6 +98,14 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
93
98
  respond_with(:DELETE,req,res)
94
99
  end
95
100
 
101
+ def do_PURGE(req,res)
102
+ respond_with(:PURGE,req,res)
103
+ end
104
+
105
+ def do_COPY(req,res)
106
+ respond_with(:COPY,req,res)
107
+ end
108
+
96
109
  end
97
110
 
98
111
  module TestServerMethods
@@ -165,5 +178,6 @@ module TestServerMethods
165
178
  at_exit{exit_code.call}
166
179
 
167
180
  end
181
+ rescue Errno::EADDRINUSE
168
182
  end
169
183
  end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+ #require 'rubygems'
3
+ #require 'rmem'
4
+
5
+ #
6
+ # Run some tests to measure the memory usage of curb, these tests require fork and ps
7
+ #
8
+ class TestCurbMemory < Test::Unit::TestCase
9
+
10
+ def test_easy_memory
11
+ easy_avg, easy_std = measure_object_memory(Curl::Easy)
12
+ printf "Easy average: %.2f kilobytes +/- %.2f kilobytes\n", easy_avg.to_f, easy_std.to_f
13
+
14
+ multi_avg, multi_std = measure_object_memory(Curl::Multi)
15
+ printf "Multi average: %.2f kilobytes +/- %.2f kilobytes\n", multi_avg.to_f, multi_std.to_f
16
+
17
+ # now that we have the average size of an easy handle lets see how much a multi request consumes with 10 requests
18
+ end
19
+
20
+ def c_avg(report)
21
+ sum = 0
22
+ report.each {|r| sum += r.last }
23
+ (sum.to_f / report.size)
24
+ end
25
+
26
+ def c_std(report,avg)
27
+ var = 0
28
+ report.each {|r| var += (r.last-avg)*(r.last-avg) }
29
+ Math.sqrt(var / (report.size-1))
30
+ end
31
+
32
+ def measure_object_memory(klass)
33
+ report = []
34
+ 200.times do
35
+ res = mem_check do
36
+ obj = klass.new
37
+ end
38
+ report << res
39
+ end
40
+ avg = c_avg(report)
41
+ std = c_std(report,avg)
42
+ [avg,std]
43
+ end
44
+
45
+ def mem_check
46
+ # see: http://gist.github.com/264060 for inspiration of ps command line
47
+ rd, wr = IO.pipe
48
+ memory_usage = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
49
+ fork do
50
+ before = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
51
+ rd.close
52
+ yield
53
+ after = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
54
+ wr.write((after - before))
55
+ wr.flush
56
+ wr.close
57
+ end
58
+ wr.close
59
+ total = rd.read.to_i
60
+ rd.close
61
+ Process.wait
62
+ # return the delta and the total
63
+ [memory_usage, total]
64
+ end
65
+ end
@@ -7,7 +7,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
7
7
  server_setup
8
8
  end
9
9
 
10
- def test_download_url_to_file
10
+ def test_download_url_to_file_via_string
11
11
  dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
12
12
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
13
13
 
@@ -18,6 +18,49 @@ class TestCurbCurlDownload < Test::Unit::TestCase
18
18
  File.unlink(dl_path) if File.exist?(dl_path)
19
19
  end
20
20
 
21
+ def test_download_url_to_file_via_file_io
22
+ dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
23
+ dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
24
+ io = File.open(dl_path, 'wb')
25
+
26
+ curb = Curl::Easy.download(dl_url, io)
27
+ assert io.closed?
28
+ assert File.exist?(dl_path)
29
+ assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
30
+ ensure
31
+ File.unlink(dl_path) if File.exist?(dl_path)
32
+ end
33
+
34
+ def test_download_url_to_file_via_io
35
+ dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
36
+ dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
37
+ reader, writer = IO.pipe
38
+
39
+ # Write to local file
40
+ fork do
41
+ begin
42
+ writer.close
43
+ File.open(dl_path, 'wb') { |file| file << reader.read }
44
+ ensure
45
+ reader.close rescue IOError # if the stream has already been closed
46
+ end
47
+ end
48
+
49
+ # Download remote source
50
+ begin
51
+ reader.close
52
+ curb = Curl::Easy.download(dl_url, writer)
53
+ Process.wait
54
+ ensure
55
+ writer.close rescue IOError # if the stream has already been closed, which occurs in Easy::download
56
+ end
57
+
58
+ assert File.exist?(dl_path)
59
+ assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
60
+ ensure
61
+ File.unlink(dl_path) if File.exist?(dl_path)
62
+ end
63
+
21
64
  def test_download_bad_url_gives_404
22
65
  dl_url = "http://127.0.0.1:9129/this_file_does_not_exist.html"
23
66
  dl_path = File.join(Dir::tmpdir, "dl_url_test.file")