ghazel-curb 0.5.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +51 -0
- data/README +157 -0
- data/Rakefile +297 -0
- data/doc.rb +42 -0
- data/ext/curb.c +339 -0
- data/ext/curb.h +50 -0
- data/ext/curb_easy.c +2932 -0
- data/ext/curb_easy.h +103 -0
- data/ext/curb_errors.c +630 -0
- data/ext/curb_errors.h +128 -0
- data/ext/curb_macros.h +114 -0
- data/ext/curb_multi.c +487 -0
- data/ext/curb_multi.h +26 -0
- data/ext/curb_postfield.c +511 -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 +162 -0
- data/lib/curb.rb +184 -0
- data/lib/curl.rb +2 -0
- data/tests/alltests.rb +3 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
- data/tests/bug_multi_segfault.rb +10 -0
- data/tests/bug_require_last_or_segfault.rb +40 -0
- data/tests/helper.rb +168 -0
- data/tests/require_last_or_segfault_script.rb +36 -0
- data/tests/tc_curl_download.rb +32 -0
- data/tests/tc_curl_easy.rb +664 -0
- data/tests/tc_curl_multi.rb +421 -0
- data/tests/tc_curl_postfield.rb +141 -0
- data/tests/unittests.rb +2 -0
- metadata +89 -0
data/ext/extconf.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
dir_config('curl')
|
4
|
+
|
5
|
+
if find_executable('curl-config')
|
6
|
+
$CFLAGS << " #{`curl-config --cflags`.strip}"
|
7
|
+
if ENV['STATIC_BUILD']
|
8
|
+
$LIBS << " #{`curl-config --static-libs`.strip}"
|
9
|
+
else
|
10
|
+
$LIBS << " #{`curl-config --libs`.strip}"
|
11
|
+
end
|
12
|
+
ca_bundle_path=`curl-config --ca`.strip
|
13
|
+
if !ca_bundle_path.nil? and ca_bundle_path != ''
|
14
|
+
$defs.push( %{-D HAVE_CURL_CONFIG_CA} )
|
15
|
+
$defs.push( %{-D CURL_CONFIG_CA='#{ca_bundle_path.inspect}'} )
|
16
|
+
end
|
17
|
+
elsif !have_library('curl') or !have_header('curl/curl.h')
|
18
|
+
fail <<-EOM
|
19
|
+
Can't find libcurl or curl/curl.h
|
20
|
+
|
21
|
+
Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
|
22
|
+
options to extconf.
|
23
|
+
EOM
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check arch flags
|
27
|
+
archs = $CFLAGS.scan(/-arch\s(.*?)\s/).first # get the first arch flag
|
28
|
+
if archs and archs.size >= 1
|
29
|
+
# need to reduce the number of archs...
|
30
|
+
# guess the first one is correct... at least the first one is probably the ruby installed arch...
|
31
|
+
# this could lead to compiled binaries that crash at runtime...
|
32
|
+
$CFLAGS.gsub!(/-arch\s(.*?)\s/,' ')
|
33
|
+
$CFLAGS << " -arch #{archs.first}"
|
34
|
+
puts "Selected arch: #{archs.first}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def define(s)
|
38
|
+
$defs.push( format("-D HAVE_%s", s.to_s.upcase) )
|
39
|
+
end
|
40
|
+
|
41
|
+
def have_constant(name)
|
42
|
+
checking_for name do
|
43
|
+
src = %{
|
44
|
+
#include <curl/curl.h>
|
45
|
+
int main() {
|
46
|
+
int test = (int)#{name.upcase};
|
47
|
+
return 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
if try_compile(src,"#{$CFLAGS} #{$LIBS}")
|
51
|
+
define name
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
have_constant "curlinfo_redirect_time"
|
60
|
+
have_constant "curlinfo_response_code"
|
61
|
+
have_constant "curlinfo_filetime"
|
62
|
+
have_constant "curlinfo_redirect_count"
|
63
|
+
have_constant "curlinfo_os_errno"
|
64
|
+
have_constant "curlinfo_num_connects"
|
65
|
+
have_constant "curlinfo_ftp_entry_path"
|
66
|
+
have_constant "curl_version_ssl"
|
67
|
+
have_constant "curl_version_libz"
|
68
|
+
have_constant "curl_version_ntlm"
|
69
|
+
have_constant "curl_version_gssnegotiate"
|
70
|
+
have_constant "curl_version_debug"
|
71
|
+
have_constant "curl_version_asynchdns"
|
72
|
+
have_constant "curl_version_spnego"
|
73
|
+
have_constant "curl_version_largefile"
|
74
|
+
have_constant "curl_version_idn"
|
75
|
+
have_constant "curl_version_sspi"
|
76
|
+
have_constant "curl_version_conv"
|
77
|
+
have_constant "curlproxy_http"
|
78
|
+
have_constant "curlproxy_socks4"
|
79
|
+
have_constant "curlproxy_socks5"
|
80
|
+
have_constant "curlauth_basic"
|
81
|
+
have_constant "curlauth_digest"
|
82
|
+
have_constant "curlauth_gssnegotiate"
|
83
|
+
have_constant "curlauth_ntlm"
|
84
|
+
have_constant "curlauth_anysafe"
|
85
|
+
have_constant "curlauth_any"
|
86
|
+
have_constant "curle_tftp_notfound"
|
87
|
+
have_constant "curle_tftp_perm"
|
88
|
+
have_constant "curle_tftp_diskfull"
|
89
|
+
have_constant "curle_tftp_illegal"
|
90
|
+
have_constant "curle_tftp_unknownid"
|
91
|
+
have_constant "curle_tftp_exists"
|
92
|
+
have_constant "curle_tftp_nosuchuser"
|
93
|
+
# older versions of libcurl 7.12
|
94
|
+
have_constant "curle_send_fail_rewind"
|
95
|
+
have_constant "curle_ssl_engine_initfailed"
|
96
|
+
have_constant "curle_login_denied"
|
97
|
+
|
98
|
+
# older than 7.16.3
|
99
|
+
have_constant "curlmopt_maxconnects"
|
100
|
+
|
101
|
+
# additional consts
|
102
|
+
have_constant "curle_conv_failed"
|
103
|
+
have_constant "curle_conv_reqd"
|
104
|
+
have_constant "curle_ssl_cacert_badfile"
|
105
|
+
have_constant "curle_remote_file_not_found"
|
106
|
+
have_constant "curle_ssh"
|
107
|
+
have_constant "curle_ssl_shutdown_failed"
|
108
|
+
have_constant "curle_again"
|
109
|
+
have_constant "curle_ssl_crl_badfile"
|
110
|
+
have_constant "curle_ssl_issuer_error"
|
111
|
+
|
112
|
+
# centos 4.5 build of libcurl
|
113
|
+
have_constant "curlm_bad_socket"
|
114
|
+
have_constant "curlm_unknown_option"
|
115
|
+
have_func("curl_multi_timeout")
|
116
|
+
have_func("curl_multi_fdset")
|
117
|
+
have_func("curl_multi_perform")
|
118
|
+
|
119
|
+
if try_compile('int main() { return 0; }','-Wall')
|
120
|
+
$CFLAGS << ' -Wall'
|
121
|
+
end
|
122
|
+
|
123
|
+
# do some checking to detect ruby 1.8 hash.c vs ruby 1.9 hash.c
|
124
|
+
def test_for(name, const, src)
|
125
|
+
checking_for name do
|
126
|
+
if try_compile(src,"#{$CFLAGS} #{$LIBS}")
|
127
|
+
define const
|
128
|
+
true
|
129
|
+
else
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
test_for("Ruby 1.9 Hash", "RUBY19_HASH", %{
|
135
|
+
#include <ruby.h>
|
136
|
+
int main() {
|
137
|
+
VALUE hash = rb_hash_new();
|
138
|
+
if( RHASH(hash)->ntbl->num_entries ) {
|
139
|
+
return 0;
|
140
|
+
}
|
141
|
+
return 1;
|
142
|
+
}
|
143
|
+
})
|
144
|
+
test_for("Ruby 1.9 st.h", "RUBY19_ST_H", %{
|
145
|
+
#include <ruby.h>
|
146
|
+
#include <ruby/st.h>
|
147
|
+
int main() {
|
148
|
+
return 0;
|
149
|
+
}
|
150
|
+
})
|
151
|
+
|
152
|
+
test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
153
|
+
#include <curl/curl.h>
|
154
|
+
int main() {
|
155
|
+
CURL *easy = curl_easy_init();
|
156
|
+
curl_easy_escape(easy,"hello",5);
|
157
|
+
return 0;
|
158
|
+
}
|
159
|
+
})
|
160
|
+
|
161
|
+
create_header('curb_config.h')
|
162
|
+
create_makefile('curb_core')
|
data/lib/curb.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'curb_core'
|
2
|
+
|
3
|
+
module Curl
|
4
|
+
class Easy
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# call-seq:
|
8
|
+
# Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... }
|
9
|
+
#
|
10
|
+
# Stream the specified url (via perform) and save the data directly to the
|
11
|
+
# supplied filename (defaults to the last component of the URL path, which will
|
12
|
+
# usually be the filename most simple urls).
|
13
|
+
#
|
14
|
+
# If a block is supplied, it will be passed the curl instance prior to the
|
15
|
+
# perform call.
|
16
|
+
#
|
17
|
+
# *Note* that the semantics of the on_body handler are subtly changed when using
|
18
|
+
# download, to account for the automatic routing of data to the specified file: The
|
19
|
+
# data string is passed to the handler *before* it is written
|
20
|
+
# to the file, allowing the handler to perform mutative operations where
|
21
|
+
# necessary. As usual, the transfer will be aborted if the on_body handler
|
22
|
+
# returns a size that differs from the data chunk size - in this case, the
|
23
|
+
# offending chunk will *not* be written to the file, the file will be closed,
|
24
|
+
# and a Curl::Err::AbortedByCallbackError will be raised.
|
25
|
+
def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
|
26
|
+
curl = Curl::Easy.new(url, &blk)
|
27
|
+
|
28
|
+
File.open(filename, "wb") do |output|
|
29
|
+
old_on_body = curl.on_body do |data|
|
30
|
+
result = old_on_body ? old_on_body.call(data) : data.length
|
31
|
+
output << data if result == data.length
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
curl.perform
|
36
|
+
end
|
37
|
+
|
38
|
+
return curl
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Allow the incoming cert string to be file:password
|
43
|
+
# but be careful to not use a colon from a windows file path
|
44
|
+
# as the split point. Mimic what curl's main does
|
45
|
+
alias_method :native_cert=, :cert=
|
46
|
+
def cert=(cert_file)
|
47
|
+
pos = cert_file.rindex(':')
|
48
|
+
if pos && pos > 1
|
49
|
+
self.native_cert= cert_file[0..pos-1]
|
50
|
+
self.certpassword= cert_file[pos+1..-1]
|
51
|
+
else
|
52
|
+
self.native_cert= cert_file
|
53
|
+
end
|
54
|
+
self.cert
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class Multi
|
58
|
+
class << self
|
59
|
+
# call-seq:
|
60
|
+
# Curl::Multi.get('url1','url2','url3','url4','url5', :follow_location => true) do|easy|
|
61
|
+
# easy
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Blocking call to fetch multiple url's in parallel.
|
65
|
+
def get(urls, easy_options={}, multi_options={}, &blk)
|
66
|
+
url_confs = []
|
67
|
+
urls.each do|url|
|
68
|
+
url_confs << {:url => url, :method => :get}.merge(easy_options)
|
69
|
+
end
|
70
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# call-seq:
|
74
|
+
#
|
75
|
+
# Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
76
|
+
# {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
77
|
+
# {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}],
|
78
|
+
# { :follow_location => true, :multipart_form_post => true },
|
79
|
+
# {:pipeline => true }) do|easy|
|
80
|
+
# easy_handle_on_request_complete
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# Blocking call to POST multiple form's in parallel.
|
84
|
+
#
|
85
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
86
|
+
# easy_options: are a set of common options to set on all easy handles
|
87
|
+
# multi_options: options to set on the Curl::Multi handle
|
88
|
+
#
|
89
|
+
def post(urls_with_config, easy_options={}, multi_options={}, &blk)
|
90
|
+
url_confs = []
|
91
|
+
urls_with_config.each do|uconf|
|
92
|
+
url_confs << uconf.merge(:method => :post).merge(easy_options)
|
93
|
+
end
|
94
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# call-seq:
|
98
|
+
#
|
99
|
+
# Curl::Multi.put([{:url => 'url1', :put_data => "some message"},
|
100
|
+
# {:url => 'url2', :put_data => IO.read('filepath')},
|
101
|
+
# {:url => 'url3', :put_data => "maybe another string or socket?"],
|
102
|
+
# {:follow_location => true},
|
103
|
+
# {:pipeline => true }) do|easy|
|
104
|
+
# easy_handle_on_request_complete
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# Blocking call to POST multiple form's in parallel.
|
108
|
+
#
|
109
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
110
|
+
# easy_options: are a set of common options to set on all easy handles
|
111
|
+
# multi_options: options to set on the Curl::Multi handle
|
112
|
+
#
|
113
|
+
def put(urls_with_config, easy_options={}, multi_options={}, &blk)
|
114
|
+
url_confs = []
|
115
|
+
urls_with_config.each do|uconf|
|
116
|
+
url_confs << uconf.merge(:method => :put).merge(easy_options)
|
117
|
+
end
|
118
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# call-seq:
|
123
|
+
#
|
124
|
+
# Curl::Multi.http( [
|
125
|
+
# { :url => 'url1', :method => :post,
|
126
|
+
# :post_fields => {'field1' => 'value1', 'field2' => 'value2'} },
|
127
|
+
# { :url => 'url2', :method => :get,
|
128
|
+
# :follow_location => true, :max_redirects => 3 },
|
129
|
+
# { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') },
|
130
|
+
# { :url => 'url4', :method => :head }
|
131
|
+
# ], {:pipeline => true})
|
132
|
+
#
|
133
|
+
# Blocking call to issue multiple HTTP requests with varying verb's.
|
134
|
+
#
|
135
|
+
# 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
|
136
|
+
# multi_options: options for the multi handle
|
137
|
+
# blk: a callback, that yeilds when a handle is completed
|
138
|
+
#
|
139
|
+
def http(urls_with_config, multi_options={}, &blk)
|
140
|
+
m = Curl::Multi.new
|
141
|
+
# configure the multi handle
|
142
|
+
multi_options.each { |k,v| m.send("#{k}=", v) }
|
143
|
+
|
144
|
+
urls_with_config.each do|conf|
|
145
|
+
c = conf.dup # avoid being destructive to input
|
146
|
+
url = c.delete(:url)
|
147
|
+
method = c.delete(:method)
|
148
|
+
headers = c.delete(:headers)
|
149
|
+
|
150
|
+
easy = Curl::Easy.new(url)
|
151
|
+
|
152
|
+
case method
|
153
|
+
when :post
|
154
|
+
fields = c.delete(:post_fields)
|
155
|
+
# set the post post using the url fields
|
156
|
+
easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&')
|
157
|
+
when :put
|
158
|
+
easy.put_data = c.delete(:put_data)
|
159
|
+
when :head
|
160
|
+
easy.head = true
|
161
|
+
when :delete
|
162
|
+
easy.delete = true
|
163
|
+
when :get
|
164
|
+
else
|
165
|
+
# XXX: nil is treated like a GET
|
166
|
+
end
|
167
|
+
|
168
|
+
# headers is a special key
|
169
|
+
headers.each {|k,v| easy.headers[k] = v } if headers
|
170
|
+
|
171
|
+
#
|
172
|
+
# use the remaining options as specific configuration to the easy handle
|
173
|
+
# bad options should raise an undefined method error
|
174
|
+
#
|
175
|
+
c.each { |k,v| easy.send("#{k}=",v) }
|
176
|
+
|
177
|
+
easy.on_complete {|curl,code| blk.call(curl,code,method) } if blk
|
178
|
+
m.add(easy)
|
179
|
+
end
|
180
|
+
m.perform
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/lib/curl.rb
ADDED
data/tests/alltests.rb
ADDED
@@ -0,0 +1,52 @@
|
|
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 BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
7
|
+
def test_bug
|
8
|
+
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
9
|
+
server.mount_proc("/test") do|req,res|
|
10
|
+
sleep 0.5
|
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
|
+
threads = []
|
20
|
+
timer = Time.now
|
21
|
+
|
22
|
+
5.times do |i|
|
23
|
+
t = Thread.new do
|
24
|
+
c = Curl::Easy.perform('http://localhost:9999/test')
|
25
|
+
c.header_str
|
26
|
+
end
|
27
|
+
threads << t
|
28
|
+
end
|
29
|
+
|
30
|
+
multi_responses = threads.collect do|t|
|
31
|
+
t.value
|
32
|
+
end
|
33
|
+
|
34
|
+
multi_time = (Time.now - timer)
|
35
|
+
puts "requested in #{multi_time}"
|
36
|
+
|
37
|
+
timer = Time.now
|
38
|
+
single_responses = []
|
39
|
+
5.times do |i|
|
40
|
+
c = Curl::Easy.perform('http://localhost:9999/test')
|
41
|
+
single_responses << c.header_str
|
42
|
+
end
|
43
|
+
|
44
|
+
single_time = (Time.now - timer)
|
45
|
+
puts "requested in #{single_time}"
|
46
|
+
|
47
|
+
assert single_time > multi_time
|
48
|
+
|
49
|
+
server.shutdown
|
50
|
+
thread.join
|
51
|
+
end
|
52
|
+
end
|
@@ -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,10 @@
|
|
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
|
+
multi = Curl::Multi.new
|
10
|
+
exit
|
@@ -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/helper.rb
ADDED
@@ -0,0 +1,168 @@
|
|
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
|
+
|
15
|
+
$TEST_URL = "file://#{URI.escape(File.expand_path(__FILE__).tr('\\','/').tr(':','|'))}"
|
16
|
+
|
17
|
+
require 'thread'
|
18
|
+
require 'webrick'
|
19
|
+
|
20
|
+
# set this to true to avoid testing with multiple threads
|
21
|
+
# or to test with multiple threads set it to false
|
22
|
+
# this is important since, some code paths will change depending
|
23
|
+
# on the presence of multiple threads
|
24
|
+
TEST_SINGLE_THREADED=false
|
25
|
+
|
26
|
+
# keep webrick quiet
|
27
|
+
class ::WEBrick::HTTPServer
|
28
|
+
def access_log(config, req, res)
|
29
|
+
# nop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
class ::WEBrick::BasicLog
|
33
|
+
def log(level, data)
|
34
|
+
# nop
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Simple test server to record number of times a request is sent/recieved of a specific
|
40
|
+
# request type, e.g. GET,POST,PUT,DELETE
|
41
|
+
#
|
42
|
+
class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
43
|
+
|
44
|
+
def self.port=(p)
|
45
|
+
@port = p
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.port
|
49
|
+
(@port or 9129)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.path
|
53
|
+
'/methods'
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.url
|
57
|
+
"http://127.0.0.1:#{port}#{path}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_with(method,req,res)
|
61
|
+
res.body = method.to_s
|
62
|
+
res['Content-Type'] = "text/plain"
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_GET(req,res)
|
66
|
+
respond_with(:GET,req,res)
|
67
|
+
end
|
68
|
+
|
69
|
+
def do_HEAD(req,res)
|
70
|
+
res['Location'] = "/nonexistent"
|
71
|
+
respond_with(:HEAD, req, res)
|
72
|
+
end
|
73
|
+
|
74
|
+
def do_POST(req,res)
|
75
|
+
if req.body
|
76
|
+
params = {}
|
77
|
+
req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
|
78
|
+
end
|
79
|
+
if params and params['s'] == '500'
|
80
|
+
res.status = 500
|
81
|
+
else
|
82
|
+
respond_with("POST\n#{req.body}",req,res)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_PUT(req,res)
|
87
|
+
res['X-Requested-Content-Type'] = req.content_type
|
88
|
+
respond_with("PUT\n#{req.body}",req,res)
|
89
|
+
end
|
90
|
+
|
91
|
+
def do_DELETE(req,res)
|
92
|
+
respond_with(:DELETE,req,res)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
module TestServerMethods
|
98
|
+
def locked_file
|
99
|
+
File.join(File.dirname(__FILE__),"server_lock-#{@__port}")
|
100
|
+
end
|
101
|
+
|
102
|
+
def server_setup(port=9129,servlet=TestServlet)
|
103
|
+
@__port = port
|
104
|
+
if @server.nil? and !File.exist?(locked_file)
|
105
|
+
|
106
|
+
File.open(locked_file,'w') {|f| f << 'locked' }
|
107
|
+
if TEST_SINGLE_THREADED
|
108
|
+
rd, wr = IO.pipe
|
109
|
+
@__pid = fork do
|
110
|
+
rd.close
|
111
|
+
rd = nil
|
112
|
+
|
113
|
+
# start up a webrick server for testing delete
|
114
|
+
server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
115
|
+
|
116
|
+
server.mount(servlet.path, servlet)
|
117
|
+
server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
|
118
|
+
|
119
|
+
trap("INT") { server.shutdown }
|
120
|
+
GC.start
|
121
|
+
wr.flush
|
122
|
+
wr.close
|
123
|
+
server.start
|
124
|
+
end
|
125
|
+
wr.close
|
126
|
+
rd.read
|
127
|
+
rd.close
|
128
|
+
else
|
129
|
+
# start up a webrick server for testing delete
|
130
|
+
@server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
131
|
+
|
132
|
+
@server.mount(servlet.path, servlet)
|
133
|
+
@server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
|
134
|
+
queue = Queue.new # synchronize the thread startup to the main thread
|
135
|
+
|
136
|
+
@test_thread = Thread.new { queue << 1; @server.start }
|
137
|
+
|
138
|
+
# wait for the queue
|
139
|
+
value = queue.pop
|
140
|
+
if !value
|
141
|
+
STDERR.puts "Failed to startup test server!"
|
142
|
+
exit(1)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
exit_code = lambda do
|
148
|
+
begin
|
149
|
+
if File.exist?(locked_file)
|
150
|
+
File.unlink locked_file
|
151
|
+
if TEST_SINGLE_THREADED
|
152
|
+
Process.kill 'INT', @__pid
|
153
|
+
else
|
154
|
+
@server.shutdown unless @server.nil?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
#@server.shutdown unless @server.nil?
|
158
|
+
rescue Object => e
|
159
|
+
puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
trap("INT"){exit_code.call}
|
164
|
+
at_exit{exit_code.call}
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|