curb 0.8.4 → 0.9.11
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.
- checksums.yaml +7 -0
- data/README.markdown +277 -0
- data/Rakefile +29 -18
- data/ext/banned.h +32 -0
- data/ext/curb.c +161 -46
- data/ext/curb.h +14 -10
- data/ext/curb_easy.c +612 -97
- data/ext/curb_easy.h +6 -0
- data/ext/curb_errors.c +113 -16
- data/ext/curb_errors.h +8 -5
- data/ext/curb_macros.h +7 -7
- data/ext/curb_multi.c +175 -167
- data/ext/curb_multi.h +0 -1
- data/ext/curb_upload.c +2 -2
- data/ext/extconf.rb +52 -17
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +79 -59
- data/lib/curl/multi.rb +59 -20
- data/lib/curl.rb +15 -9
- data/tests/bug_crash_on_progress.rb +41 -1
- data/tests/bug_issue102.rb +1 -1
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +102 -13
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +4 -4
- data/tests/tc_curl_easy.rb +197 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +82 -16
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/timeout.rb +10 -2
- metadata +51 -52
- data/README +0 -194
|
@@ -5,7 +5,7 @@ class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
|
|
|
5
5
|
|
|
6
6
|
class BugCrashOnDebug < Test::Unit::TestCase
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def test_on_progress_raise
|
|
9
9
|
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
|
10
10
|
server.mount_proc("/test") do|req,res|
|
|
11
11
|
res.body = "hi"
|
|
@@ -30,4 +30,44 @@ class BugCrashOnDebug < Test::Unit::TestCase
|
|
|
30
30
|
ensure
|
|
31
31
|
server.shutdown
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
def test_on_progress_abort
|
|
35
|
+
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
|
36
|
+
server.mount_proc("/test") do|req,res|
|
|
37
|
+
res.body = "hi"
|
|
38
|
+
res['Content-Type'] = "text/html"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
thread = Thread.new(server) do|srv|
|
|
42
|
+
srv.start
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# see: https://github.com/taf2/curb/issues/192,
|
|
46
|
+
# to pass:
|
|
47
|
+
#
|
|
48
|
+
# c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
49
|
+
# c.on_progress do|x|
|
|
50
|
+
# puts "we're in the progress callback"
|
|
51
|
+
# false
|
|
52
|
+
# end
|
|
53
|
+
# c.perform
|
|
54
|
+
#
|
|
55
|
+
# notice no return keyword
|
|
56
|
+
#
|
|
57
|
+
c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
58
|
+
c.on_progress do|x|
|
|
59
|
+
puts "we're in the progress callback"
|
|
60
|
+
return false
|
|
61
|
+
end
|
|
62
|
+
c.perform
|
|
63
|
+
|
|
64
|
+
assert false, "should not reach this point"
|
|
65
|
+
|
|
66
|
+
rescue => e
|
|
67
|
+
assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
|
|
68
|
+
c.close
|
|
69
|
+
ensure
|
|
70
|
+
server.shutdown
|
|
71
|
+
end
|
|
72
|
+
|
|
33
73
|
end
|
data/tests/bug_issue102.rb
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
require 'curb'
|
|
5
|
+
|
|
6
|
+
class BugIssue102 < Test::Unit::TestCase
|
|
7
|
+
|
|
8
|
+
def test_gc_closewait
|
|
9
|
+
100.times do
|
|
10
|
+
responses = {}
|
|
11
|
+
requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"]
|
|
12
|
+
m = Curl::Multi.new
|
|
13
|
+
# add a few easy handles
|
|
14
|
+
requests.each do |url|
|
|
15
|
+
responses[url] = ""
|
|
16
|
+
c = Curl::Easy.new(url) do|curl|
|
|
17
|
+
curl.follow_location = true
|
|
18
|
+
curl.on_body{|data| responses[url] << data; data.size }
|
|
19
|
+
curl.on_success {|easy| #puts "success, add more easy handles"
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
m.add(c)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
m.perform do
|
|
26
|
+
#puts "idling... can do some work here"
|
|
27
|
+
end
|
|
28
|
+
GC.start
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
data/tests/helper.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Copyright (c)2006 Ross Bamford. See LICENSE.
|
|
3
3
|
$CURB_TESTING = true
|
|
4
4
|
require 'uri'
|
|
5
|
+
require 'stringio'
|
|
5
6
|
|
|
6
7
|
$TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
7
8
|
$EXTDIR = File.join($TOPDIR, 'ext')
|
|
@@ -10,10 +11,15 @@ $:.unshift($LIBDIR)
|
|
|
10
11
|
$:.unshift($EXTDIR)
|
|
11
12
|
|
|
12
13
|
require 'curb'
|
|
13
|
-
|
|
14
|
+
begin
|
|
15
|
+
require 'test/unit'
|
|
16
|
+
rescue LoadError
|
|
17
|
+
gem 'test/unit'
|
|
18
|
+
require 'test/unit'
|
|
19
|
+
end
|
|
14
20
|
require 'fileutils'
|
|
15
21
|
|
|
16
|
-
$TEST_URL = "file://#{
|
|
22
|
+
$TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/}#{File.expand_path(__FILE__).tr('\\','/')}"
|
|
17
23
|
|
|
18
24
|
require 'thread'
|
|
19
25
|
require 'webrick'
|
|
@@ -25,19 +31,22 @@ require 'webrick'
|
|
|
25
31
|
TEST_SINGLE_THREADED=false
|
|
26
32
|
|
|
27
33
|
# keep webrick quiet
|
|
28
|
-
|
|
34
|
+
::WEBrick::HTTPServer.send(:remove_method,:access_log) if ::WEBrick::HTTPServer.instance_methods.include?(:access_log)
|
|
35
|
+
::WEBrick::BasicLog.send(:remove_method,:log) if ::WEBrick::BasicLog.instance_methods.include?(:log)
|
|
36
|
+
|
|
37
|
+
::WEBrick::HTTPServer.class_eval do
|
|
29
38
|
def access_log(config, req, res)
|
|
30
39
|
# nop
|
|
31
40
|
end
|
|
32
41
|
end
|
|
33
|
-
|
|
42
|
+
::WEBrick::BasicLog.class_eval do
|
|
34
43
|
def log(level, data)
|
|
35
44
|
# nop
|
|
36
45
|
end
|
|
37
46
|
end
|
|
38
47
|
|
|
39
48
|
#
|
|
40
|
-
# Simple test server to record number of times a request is sent/recieved of a specific
|
|
49
|
+
# Simple test server to record number of times a request is sent/recieved of a specific
|
|
41
50
|
# request type, e.g. GET,POST,PUT,DELETE
|
|
42
51
|
#
|
|
43
52
|
class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
@@ -47,13 +56,12 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
def self.port
|
|
50
|
-
|
|
59
|
+
@port ||= 9129
|
|
51
60
|
end
|
|
52
61
|
|
|
53
62
|
def self.path
|
|
54
63
|
'/methods'
|
|
55
64
|
end
|
|
56
|
-
|
|
57
65
|
def self.url
|
|
58
66
|
"http://127.0.0.1:#{port}#{path}"
|
|
59
67
|
end
|
|
@@ -65,12 +73,12 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
def do_GET(req,res)
|
|
68
|
-
if req.path.match
|
|
76
|
+
if req.path.match(/redirect$/)
|
|
69
77
|
res.status = 302
|
|
70
78
|
res['Location'] = '/foo'
|
|
71
|
-
elsif req.path.match
|
|
79
|
+
elsif req.path.match(/not_here$/)
|
|
72
80
|
res.status = 404
|
|
73
|
-
elsif req.path.match
|
|
81
|
+
elsif req.path.match(/error$/)
|
|
74
82
|
res.status = 500
|
|
75
83
|
end
|
|
76
84
|
respond_with("GET#{req.query_string}",req,res)
|
|
@@ -89,6 +97,9 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
|
89
97
|
end
|
|
90
98
|
if params and params['s'] == '500'
|
|
91
99
|
res.status = 500
|
|
100
|
+
elsif params and params['c']
|
|
101
|
+
cookie = URI.decode_www_form_component(params['c']).split('=')
|
|
102
|
+
res.cookies << WEBrick::Cookie.new(*cookie)
|
|
92
103
|
else
|
|
93
104
|
respond_with("POST\n#{req.body}",req,res)
|
|
94
105
|
end
|
|
@@ -131,7 +142,7 @@ module TestServerMethods
|
|
|
131
142
|
|
|
132
143
|
def server_setup(port=9129,servlet=TestServlet)
|
|
133
144
|
@__port = port
|
|
134
|
-
if @server.nil? and !File.exist?(locked_file)
|
|
145
|
+
if (@server ||= nil).nil? and !File.exist?(locked_file)
|
|
135
146
|
|
|
136
147
|
File.open(locked_file,'w') {|f| f << 'locked' }
|
|
137
148
|
if TEST_SINGLE_THREADED
|
|
@@ -140,7 +151,7 @@ module TestServerMethods
|
|
|
140
151
|
rd.close
|
|
141
152
|
rd = nil
|
|
142
153
|
|
|
143
|
-
# start up a webrick server for testing delete
|
|
154
|
+
# start up a webrick server for testing delete
|
|
144
155
|
server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
|
145
156
|
|
|
146
157
|
server.mount(servlet.path, servlet)
|
|
@@ -156,7 +167,7 @@ module TestServerMethods
|
|
|
156
167
|
rd.read
|
|
157
168
|
rd.close
|
|
158
169
|
else
|
|
159
|
-
# start up a webrick server for testing delete
|
|
170
|
+
# start up a webrick server for testing delete
|
|
160
171
|
@server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
|
161
172
|
|
|
162
173
|
@server.mount(servlet.path, servlet)
|
|
@@ -197,3 +208,81 @@ module TestServerMethods
|
|
|
197
208
|
rescue Errno::EADDRINUSE
|
|
198
209
|
end
|
|
199
210
|
end
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Backport for Ruby 1.8
|
|
215
|
+
module Backports
|
|
216
|
+
module Ruby18
|
|
217
|
+
module URIFormEncoding
|
|
218
|
+
TBLENCWWWCOMP_ = {}
|
|
219
|
+
TBLDECWWWCOMP_ = {}
|
|
220
|
+
|
|
221
|
+
def encode_www_form_component(str)
|
|
222
|
+
if TBLENCWWWCOMP_.empty?
|
|
223
|
+
256.times do |i|
|
|
224
|
+
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
|
225
|
+
end
|
|
226
|
+
TBLENCWWWCOMP_[' '] = '+'
|
|
227
|
+
TBLENCWWWCOMP_.freeze
|
|
228
|
+
end
|
|
229
|
+
str.to_s.gsub( /([^*\-.0-9A-Z_a-z])/ ) {|*| TBLENCWWWCOMP_[$1] }
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def decode_www_form_component(str)
|
|
233
|
+
if TBLDECWWWCOMP_.empty?
|
|
234
|
+
256.times do |i|
|
|
235
|
+
h, l = i>>4, i&15
|
|
236
|
+
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
|
237
|
+
TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
|
|
238
|
+
TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
|
|
239
|
+
TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
|
|
240
|
+
end
|
|
241
|
+
TBLDECWWWCOMP_['+'] = ' '
|
|
242
|
+
TBLDECWWWCOMP_.freeze
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
raise ArgumentError, "invalid %-encoding (#{str.dump})" unless /\A(?:%[[:xdigit:]]{2}|[^%]+)*\z/ =~ str
|
|
246
|
+
str.gsub( /(\+|%[[:xdigit:]]{2})/ ) {|*| TBLDECWWWCOMP_[$1] }
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def encode_www_form( enum )
|
|
250
|
+
enum.map do |k,v|
|
|
251
|
+
if v.nil?
|
|
252
|
+
encode_www_form_component(k)
|
|
253
|
+
elsif v.respond_to?(:to_ary)
|
|
254
|
+
v.to_ary.map do |w|
|
|
255
|
+
str = encode_www_form_component(k)
|
|
256
|
+
unless w.nil?
|
|
257
|
+
str << '='
|
|
258
|
+
str << encode_www_form_component(w)
|
|
259
|
+
end
|
|
260
|
+
end.join('&')
|
|
261
|
+
else
|
|
262
|
+
str = encode_www_form_component(k)
|
|
263
|
+
str << '='
|
|
264
|
+
str << encode_www_form_component(v)
|
|
265
|
+
end
|
|
266
|
+
end.join('&')
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
WFKV_ = '(?:%\h\h|[^%#=;&])'
|
|
270
|
+
def decode_www_form(str, _)
|
|
271
|
+
return [] if str.to_s == ''
|
|
272
|
+
|
|
273
|
+
unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/ =~ str
|
|
274
|
+
raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})"
|
|
275
|
+
end
|
|
276
|
+
ary = []
|
|
277
|
+
$&.scan(/([^=;&]+)=([^;&]*)/) do
|
|
278
|
+
ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)]
|
|
279
|
+
end
|
|
280
|
+
ary
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
unless URI.methods.include?(:encode_www_form)
|
|
287
|
+
URI.extend(Backports::Ruby18::URIFormEncoding)
|
|
288
|
+
end
|
data/tests/tc_curl.rb
CHANGED
|
@@ -31,7 +31,37 @@ class TestCurl < Test::Unit::TestCase
|
|
|
31
31
|
assert_equal "OPTIONSfoo=bar", curl.body_str
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
def test_urlalize_without_extra_params
|
|
35
|
+
url_no_params = 'http://localhost/test'
|
|
36
|
+
url_with_params = 'http://localhost/test?a=1'
|
|
37
|
+
|
|
38
|
+
assert_equal(url_no_params, Curl.urlalize(url_no_params))
|
|
39
|
+
assert_equal(url_with_params, Curl.urlalize(url_with_params))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_urlalize_with_nil_as_params
|
|
43
|
+
url = 'http://localhost/test'
|
|
44
|
+
assert_equal(url, Curl.urlalize(url, nil))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_urlalize_with_extra_params
|
|
48
|
+
url_no_params = 'http://localhost/test'
|
|
49
|
+
url_with_params = 'http://localhost/test?a=1'
|
|
50
|
+
extra_params = { :b => 2 }
|
|
51
|
+
|
|
52
|
+
expected_url_no_params = 'http://localhost/test?b=2'
|
|
53
|
+
expected_url_with_params = 'http://localhost/test?a=1&b=2'
|
|
54
|
+
|
|
55
|
+
assert_equal(expected_url_no_params, Curl.urlalize(url_no_params, extra_params))
|
|
56
|
+
assert_equal(expected_url_with_params, Curl.urlalize(url_with_params, extra_params))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_urlalize_does_not_strip_trailing_?
|
|
60
|
+
url_empty_params = 'http://localhost/test?'
|
|
61
|
+
assert_equal(url_empty_params, Curl.urlalize(url_empty_params))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
include TestServerMethods
|
|
35
65
|
|
|
36
66
|
def setup
|
|
37
67
|
server_setup
|
data/tests/tc_curl_download.rb
CHANGED
|
@@ -6,12 +6,12 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
|
6
6
|
def setup
|
|
7
7
|
server_setup
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
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
|
|
|
14
|
-
|
|
14
|
+
Curl::Easy.download(dl_url, dl_path)
|
|
15
15
|
assert File.exist?(dl_path)
|
|
16
16
|
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
|
|
17
17
|
ensure
|
|
@@ -23,7 +23,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
|
23
23
|
dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
|
|
24
24
|
io = File.open(dl_path, 'wb')
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Curl::Easy.download(dl_url, io)
|
|
27
27
|
assert io.closed?
|
|
28
28
|
assert File.exist?(dl_path)
|
|
29
29
|
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
|
|
@@ -49,7 +49,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
|
49
49
|
# Download remote source
|
|
50
50
|
begin
|
|
51
51
|
reader.close
|
|
52
|
-
|
|
52
|
+
Curl::Easy.download(dl_url, writer)
|
|
53
53
|
Process.wait
|
|
54
54
|
ensure
|
|
55
55
|
writer.close rescue IOError # if the stream has already been closed, which occurs in Easy::download
|