codders-curb 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +51 -0
- data/README +194 -0
- data/Rakefile +320 -0
- data/doc.rb +42 -0
- data/ext/curb.c +977 -0
- data/ext/curb.h +52 -0
- data/ext/curb_easy.c +3404 -0
- data/ext/curb_easy.h +90 -0
- data/ext/curb_errors.c +647 -0
- data/ext/curb_errors.h +129 -0
- data/ext/curb_macros.h +159 -0
- data/ext/curb_multi.c +633 -0
- data/ext/curb_multi.h +26 -0
- data/ext/curb_postfield.c +523 -0
- data/ext/curb_postfield.h +40 -0
- data/ext/curb_upload.c +80 -0
- data/ext/curb_upload.h +30 -0
- data/ext/extconf.rb +399 -0
- data/lib/curb.rb +4 -0
- data/lib/curl.rb +57 -0
- data/lib/curl/easy.rb +468 -0
- data/lib/curl/multi.rb +248 -0
- data/tests/alltests.rb +3 -0
- data/tests/bug_crash_on_debug.rb +39 -0
- data/tests/bug_crash_on_progress.rb +33 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
- data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +83 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
- data/tests/bug_multi_segfault.rb +14 -0
- data/tests/bug_postfields_crash.rb +26 -0
- data/tests/bug_postfields_crash2.rb +57 -0
- data/tests/bug_require_last_or_segfault.rb +40 -0
- data/tests/bugtests.rb +9 -0
- data/tests/helper.rb +199 -0
- data/tests/mem_check.rb +65 -0
- data/tests/require_last_or_segfault_script.rb +36 -0
- data/tests/tc_curl_download.rb +75 -0
- data/tests/tc_curl_easy.rb +1011 -0
- data/tests/tc_curl_easy_setopt.rb +31 -0
- data/tests/tc_curl_multi.rb +485 -0
- data/tests/tc_curl_postfield.rb +143 -0
- data/tests/timeout.rb +100 -0
- data/tests/timeout_server.rb +33 -0
- data/tests/unittests.rb +2 -0
- 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
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
|
data/tests/mem_check.rb
ADDED
@@ -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
|