codders-curb 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/LICENSE +51 -0
  2. data/README +194 -0
  3. data/Rakefile +320 -0
  4. data/doc.rb +42 -0
  5. data/ext/curb.c +977 -0
  6. data/ext/curb.h +52 -0
  7. data/ext/curb_easy.c +3404 -0
  8. data/ext/curb_easy.h +90 -0
  9. data/ext/curb_errors.c +647 -0
  10. data/ext/curb_errors.h +129 -0
  11. data/ext/curb_macros.h +159 -0
  12. data/ext/curb_multi.c +633 -0
  13. data/ext/curb_multi.h +26 -0
  14. data/ext/curb_postfield.c +523 -0
  15. data/ext/curb_postfield.h +40 -0
  16. data/ext/curb_upload.c +80 -0
  17. data/ext/curb_upload.h +30 -0
  18. data/ext/extconf.rb +399 -0
  19. data/lib/curb.rb +4 -0
  20. data/lib/curl.rb +57 -0
  21. data/lib/curl/easy.rb +468 -0
  22. data/lib/curl/multi.rb +248 -0
  23. data/tests/alltests.rb +3 -0
  24. data/tests/bug_crash_on_debug.rb +39 -0
  25. data/tests/bug_crash_on_progress.rb +33 -0
  26. data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
  27. data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +83 -0
  28. data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
  29. data/tests/bug_multi_segfault.rb +14 -0
  30. data/tests/bug_postfields_crash.rb +26 -0
  31. data/tests/bug_postfields_crash2.rb +57 -0
  32. data/tests/bug_require_last_or_segfault.rb +40 -0
  33. data/tests/bugtests.rb +9 -0
  34. data/tests/helper.rb +199 -0
  35. data/tests/mem_check.rb +65 -0
  36. data/tests/require_last_or_segfault_script.rb +36 -0
  37. data/tests/tc_curl_download.rb +75 -0
  38. data/tests/tc_curl_easy.rb +1011 -0
  39. data/tests/tc_curl_easy_setopt.rb +31 -0
  40. data/tests/tc_curl_multi.rb +485 -0
  41. data/tests/tc_curl_postfield.rb +143 -0
  42. data/tests/timeout.rb +100 -0
  43. data/tests/timeout_server.rb +33 -0
  44. data/tests/unittests.rb +2 -0
  45. metadata +133 -0
@@ -0,0 +1,53 @@
1
+ # From Vlad Jebelev:
2
+ #
3
+ # - Second thing - I think you just probably didn't have the time to update
4
+ # instance methods yet but when I POST with a reusal of a previous curl
5
+ # instance, it doesnt' work for me, e.g. when I create a curl previously and
6
+ # then issue:
7
+ #
8
+ # c.http_post(login_url, *fields)
9
+ #
10
+ # instead of:
11
+ #
12
+ # c = Curl::Easy.http_post(login_url, *fields) do |curl|
13
+ # ...
14
+ # end
15
+ #
16
+ # then the result I am getting is quite different.
17
+ #
18
+ # ================
19
+ #
20
+ # Update:
21
+ #
22
+ # It seems that class httpost is incorrectly passing arguments down to
23
+ # instance httppost. This bug is intermittent, but results in an
24
+ # exception from the first post when it occurs.
25
+ #
26
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
27
+
28
+ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
29
+ def test_bug
30
+ 5.times do |i|
31
+ puts "Test ##{i}"
32
+ do_test
33
+ sleep 2
34
+ end
35
+ end
36
+
37
+ def do_test
38
+ c = Curl::Easy.http_post('https://www.google.com/accounts/ServiceLoginAuth',
39
+ Curl::PostField.content('ltmpl','m_blanco'))
40
+ body_c, header_c = c.body_str, c.header_str
41
+
42
+ sleep 2
43
+
44
+ c.http_post('https://www.google.com/accounts/ServiceLoginAuth',
45
+ Curl::PostField.content('ltmpl','m_blanco'))
46
+ body_i, header_i = c.body_str, c.header_str
47
+
48
+ # timestamps will differ, just check first bit. We wont get here if
49
+ # the bug bites anyway...
50
+ assert_equal header_c[0..50], header_i[0..50]
51
+ end
52
+ end
53
+
@@ -0,0 +1,14 @@
1
+ # From safis http://github.com/taf2/curb/issues#issue/5
2
+ # irb: require 'curb'
3
+ # irb: multi = Curl::Multi.new
4
+ # irb: exit
5
+ # <main>:47140: [BUG] Bus Error
6
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','ext'))
7
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
8
+ require 'curb'
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
@@ -0,0 +1,40 @@
1
+ # From Vlad Jebelev:
2
+ #
3
+ # - if I have a require statement after "require 'curb'" and there is a
4
+ # POST with at least 1 field, the script will fail with a segmentation
5
+ # fault, e.g. the following sequence fails every time for me (Ruby 1.8.5):
6
+ # -----------------------------------------------------------------
7
+ # require 'curb'
8
+ # require 'uri'
9
+ #
10
+ # url = 'https://www.google.com/accounts/ServiceLoginAuth'
11
+ #
12
+ # c = Curl::Easy.http_post(
13
+ # 'https://www.google.com/accounts/ServiceLoginAuth',
14
+ # [Curl:: PostField.content('ltmpl','m_blanco')] ) do |curl|
15
+ # end
16
+ # ------------------------------------------------------------------
17
+ # :..dev/util$ ruby seg.rb
18
+ # seg.rb:6: [BUG] Segmentation fault
19
+ # ruby 1.8.5 (2006-08-25) [i686-linux]
20
+ #
21
+ # Aborted
22
+ # ------------------------------------------------------------------
23
+ #
24
+ require 'test/unit'
25
+ require 'rbconfig'
26
+
27
+ $rubycmd = Config::CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
28
+
29
+ class BugTestRequireLastOrSegfault < Test::Unit::TestCase
30
+ def test_bug
31
+ 5.times do |i|
32
+ puts "Test ##{i}"
33
+
34
+ # will be empty string if it segfaults...
35
+ assert_equal 'success', `#$rubycmd #{File.dirname(__FILE__)}/require_last_or_segfault_script.rb`.chomp
36
+ sleep 5
37
+ end
38
+ end
39
+ end
40
+
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 ADDED
@@ -0,0 +1,199 @@
1
+ # DO NOT REMOVE THIS COMMENT - PART OF TESTMODEL.
2
+ # Copyright (c)2006 Ross Bamford. See LICENSE.
3
+ $CURB_TESTING = true
4
+ require 'uri'
5
+
6
+ $TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
7
+ $EXTDIR = File.join($TOPDIR, 'ext')
8
+ $LIBDIR = File.join($TOPDIR, 'lib')
9
+ $:.unshift($LIBDIR)
10
+ $:.unshift($EXTDIR)
11
+
12
+ require 'curb'
13
+ require 'test/unit'
14
+ require 'fileutils'
15
+
16
+ $TEST_URL = "file://#{URI.escape(File.expand_path(__FILE__).tr('\\','/').tr(':','|'))}"
17
+
18
+ require 'thread'
19
+ require 'webrick'
20
+
21
+ # set this to true to avoid testing with multiple threads
22
+ # or to test with multiple threads set it to false
23
+ # this is important since, some code paths will change depending
24
+ # on the presence of multiple threads
25
+ TEST_SINGLE_THREADED=false
26
+
27
+ # keep webrick quiet
28
+ class ::WEBrick::HTTPServer
29
+ def access_log(config, req, res)
30
+ # nop
31
+ end
32
+ end
33
+ class ::WEBrick::BasicLog
34
+ def log(level, data)
35
+ # nop
36
+ end
37
+ end
38
+
39
+ #
40
+ # Simple test server to record number of times a request is sent/recieved of a specific
41
+ # request type, e.g. GET,POST,PUT,DELETE
42
+ #
43
+ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
44
+
45
+ def self.port=(p)
46
+ @port = p
47
+ end
48
+
49
+ def self.port
50
+ (@port or 9129)
51
+ end
52
+
53
+ def self.path
54
+ '/methods'
55
+ end
56
+
57
+ def self.url
58
+ "http://127.0.0.1:#{port}#{path}"
59
+ end
60
+
61
+ def respond_with(method,req,res)
62
+ res.body = method.to_s
63
+ $auth_header = req['Authorization']
64
+ res['Content-Type'] = "text/plain"
65
+ end
66
+
67
+ def do_GET(req,res)
68
+ if req.path.match /redirect$/
69
+ res.status = 302
70
+ res['Location'] = '/foo'
71
+ elsif req.path.match /not_here$/
72
+ res.status = 404
73
+ elsif req.path.match /error$/
74
+ res.status = 500
75
+ end
76
+ respond_with("GET#{req.query_string}",req,res)
77
+ end
78
+
79
+ def do_HEAD(req,res)
80
+ res['Location'] = "/nonexistent"
81
+ respond_with("HEAD#{req.query_string}",req,res)
82
+ end
83
+
84
+ def do_POST(req,res)
85
+ if req.query['filename'].nil?
86
+ if req.body
87
+ params = {}
88
+ req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
89
+ end
90
+ if params and params['s'] == '500'
91
+ res.status = 500
92
+ else
93
+ respond_with("POST\n#{req.body}",req,res)
94
+ end
95
+ else
96
+ respond_with(req.query['filename'],req,res)
97
+ end
98
+ end
99
+
100
+ def do_PUT(req,res)
101
+ res['X-Requested-Content-Type'] = req.content_type
102
+ respond_with("PUT\n#{req.body}",req,res)
103
+ end
104
+
105
+ def do_DELETE(req,res)
106
+ respond_with("DELETE#{req.query_string}",req,res)
107
+ end
108
+
109
+ def do_PURGE(req,res)
110
+ respond_with("PURGE#{req.query_string}",req,res)
111
+ end
112
+
113
+ def do_COPY(req,res)
114
+ respond_with("COPY#{req.query_string}",req,res)
115
+ end
116
+
117
+ def do_PATCH(req,res)
118
+ respond_with("PATCH\n#{req.body}",req,res)
119
+ end
120
+
121
+ def do_OPTIONS(req,res)
122
+ respond_with("OPTIONS#{req.query_string}",req,res)
123
+ end
124
+
125
+ end
126
+
127
+ module TestServerMethods
128
+ def locked_file
129
+ File.join(File.dirname(__FILE__),"server_lock-#{@__port}")
130
+ end
131
+
132
+ def server_setup(port=9129,servlet=TestServlet)
133
+ @__port = port
134
+ if @server.nil? and !File.exist?(locked_file)
135
+
136
+ File.open(locked_file,'w') {|f| f << 'locked' }
137
+ if TEST_SINGLE_THREADED
138
+ rd, wr = IO.pipe
139
+ @__pid = fork do
140
+ rd.close
141
+ rd = nil
142
+
143
+ # start up a webrick server for testing delete
144
+ server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
145
+
146
+ server.mount(servlet.path, servlet)
147
+ server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
148
+
149
+ trap("INT") { server.shutdown }
150
+ GC.start
151
+ wr.flush
152
+ wr.close
153
+ server.start
154
+ end
155
+ wr.close
156
+ rd.read
157
+ rd.close
158
+ else
159
+ # start up a webrick server for testing delete
160
+ @server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
161
+
162
+ @server.mount(servlet.path, servlet)
163
+ @server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
164
+ queue = Queue.new # synchronize the thread startup to the main thread
165
+
166
+ @test_thread = Thread.new { queue << 1; @server.start }
167
+
168
+ # wait for the queue
169
+ value = queue.pop
170
+ if !value
171
+ STDERR.puts "Failed to startup test server!"
172
+ exit(1)
173
+ end
174
+
175
+ end
176
+
177
+ exit_code = lambda do
178
+ begin
179
+ if File.exist?(locked_file)
180
+ File.unlink locked_file
181
+ if TEST_SINGLE_THREADED
182
+ Process.kill 'INT', @__pid
183
+ else
184
+ @server.shutdown unless @server.nil?
185
+ end
186
+ end
187
+ #@server.shutdown unless @server.nil?
188
+ rescue Object => e
189
+ puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
190
+ end
191
+ end
192
+
193
+ trap("INT"){exit_code.call}
194
+ at_exit{exit_code.call}
195
+
196
+ end
197
+ rescue Errno::EADDRINUSE
198
+ end
199
+ 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