curb 0.9.3 → 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 +5 -5
- data/README.markdown +45 -19
- data/Rakefile +26 -10
- data/ext/banned.h +32 -0
- data/ext/curb.c +36 -6
- data/ext/curb.h +12 -4
- data/ext/curb_easy.c +421 -61
- data/ext/curb_easy.h +4 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_multi.c +131 -208
- data/ext/curb_multi.h +0 -1
- data/ext/extconf.rb +38 -6
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +10 -4
- data/lib/curl/multi.rb +52 -13
- data/lib/curl.rb +9 -9
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +96 -12
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +3 -3
- data/tests/tc_curl_easy.rb +155 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +77 -15
- data/tests/tc_curl_postfield.rb +29 -29
- metadata +31 -25
data/ext/curb_multi.h
CHANGED
data/ext/extconf.rb
CHANGED
@@ -59,6 +59,9 @@ def have_constant(name)
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
have_constant "curlopt_tcp_keepalive"
|
63
|
+
have_constant "curlopt_tcp_keepidle"
|
64
|
+
have_constant "curlopt_tcp_keepintvl"
|
62
65
|
have_constant "curlinfo_appconnect_time"
|
63
66
|
have_constant "curlinfo_redirect_time"
|
64
67
|
have_constant "curlinfo_response_code"
|
@@ -66,6 +69,7 @@ have_constant "curlinfo_filetime"
|
|
66
69
|
have_constant "curlinfo_redirect_count"
|
67
70
|
have_constant "curlinfo_os_errno"
|
68
71
|
have_constant "curlinfo_num_connects"
|
72
|
+
have_constant "curlinfo_cookielist"
|
69
73
|
have_constant "curlinfo_ftp_entry_path"
|
70
74
|
have_constant "curl_version_ssl"
|
71
75
|
have_constant "curl_version_libz"
|
@@ -83,6 +87,7 @@ have_constant "curlproxy_http"
|
|
83
87
|
have_constant "curlproxy_socks4"
|
84
88
|
have_constant "curlproxy_socks4a"
|
85
89
|
have_constant "curlproxy_socks5"
|
90
|
+
have_constant "curlproxy_socks5_hostname"
|
86
91
|
have_constant "curlauth_basic"
|
87
92
|
have_constant "curlauth_digest"
|
88
93
|
have_constant "curlauth_gssnegotiate"
|
@@ -101,6 +106,12 @@ have_constant "curle_send_fail_rewind"
|
|
101
106
|
have_constant "curle_ssl_engine_initfailed"
|
102
107
|
have_constant "curle_login_denied"
|
103
108
|
|
109
|
+
# older than 7.10.0
|
110
|
+
have_constant "curlopt_nosignal"
|
111
|
+
|
112
|
+
# older than 7.16.0
|
113
|
+
have_constant "curlmopt_pipelining"
|
114
|
+
|
104
115
|
# older than 7.16.3
|
105
116
|
have_constant "curlmopt_maxconnects"
|
106
117
|
|
@@ -145,6 +156,8 @@ have_func("curl_multi_timeout")
|
|
145
156
|
have_func("curl_multi_fdset")
|
146
157
|
have_func("curl_multi_perform")
|
147
158
|
|
159
|
+
have_constant "curlopt_haproxyprotocol"
|
160
|
+
|
148
161
|
# constants
|
149
162
|
have_constant "curlopt_interleavefunction"
|
150
163
|
have_constant "curlopt_interleavedata"
|
@@ -209,6 +222,7 @@ have_constant "curlopt_httppost"
|
|
209
222
|
have_constant "curlopt_referer"
|
210
223
|
have_constant "curlopt_useragent"
|
211
224
|
have_constant "curlopt_httpheader"
|
225
|
+
have_constant "curlopt_proxyheader"
|
212
226
|
have_constant "curlopt_http200aliases"
|
213
227
|
have_constant "curlopt_cookie"
|
214
228
|
have_constant "curlopt_cookiefile"
|
@@ -318,14 +332,14 @@ have_constant "curlopt_sslengine"
|
|
318
332
|
have_constant "curlopt_sslengine_default"
|
319
333
|
have_constant "curlopt_sslversion"
|
320
334
|
have_constant "curl_sslversion_default"
|
321
|
-
have_constant
|
322
|
-
have_constant
|
323
|
-
have_constant
|
335
|
+
have_constant :CURL_SSLVERSION_TLSv1
|
336
|
+
have_constant :CURL_SSLVERSION_SSLv2
|
337
|
+
have_constant :CURL_SSLVERSION_SSLv3
|
324
338
|
|
325
339
|
# Added in 7.34.0
|
326
|
-
have_constant
|
327
|
-
have_constant
|
328
|
-
have_constant
|
340
|
+
have_constant :CURL_SSLVERSION_TLSv1_0
|
341
|
+
have_constant :CURL_SSLVERSION_TLSv1_1
|
342
|
+
have_constant :CURL_SSLVERSION_TLSv1_2
|
329
343
|
|
330
344
|
have_constant "curlopt_ssl_verifypeer"
|
331
345
|
have_constant "curlopt_cainfo"
|
@@ -360,6 +374,16 @@ have_constant "curle_not_built_in"
|
|
360
374
|
|
361
375
|
have_constant "curle_obsolete" # removed in 7.24 ?
|
362
376
|
|
377
|
+
have_constant "curle_ftp_pret_failed"
|
378
|
+
have_constant "curle_rtsp_cseq_error"
|
379
|
+
have_constant "curle_rtsp_session_error"
|
380
|
+
have_constant "curle_ftp_bad_file_list"
|
381
|
+
have_constant "curle_chunk_failed"
|
382
|
+
have_constant "curle_no_connection_available"
|
383
|
+
have_constant "curle_ssl_pinnedpubkeynotmatch"
|
384
|
+
have_constant "curle_ssl_invalidcertstatus"
|
385
|
+
have_constant "curle_http2_stream"
|
386
|
+
|
363
387
|
# gssapi/spnego delegation related constants
|
364
388
|
have_constant "curlopt_gssapi_delegation"
|
365
389
|
have_constant "curlgssapi_delegation_policy_flag"
|
@@ -370,6 +394,12 @@ have_constant "CURLM_ADDED_ALREADY"
|
|
370
394
|
# added in 7.40.0
|
371
395
|
have_constant "curlopt_unix_socket_path"
|
372
396
|
|
397
|
+
# added in 7.42.0
|
398
|
+
have_constant "curlopt_path_as_is"
|
399
|
+
|
400
|
+
# added in 7.43.0
|
401
|
+
have_constant "curlopt_pipewait"
|
402
|
+
|
373
403
|
if try_compile('int main() { return 0; }','-Wall')
|
374
404
|
$CFLAGS << ' -Wall'
|
375
405
|
end
|
@@ -397,6 +427,8 @@ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
|
397
427
|
|
398
428
|
have_func('rb_thread_blocking_region')
|
399
429
|
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
430
|
+
have_header('ruby/io.h')
|
431
|
+
have_func('rb_io_stdio_file')
|
400
432
|
|
401
433
|
create_header('curb_config.h')
|
402
434
|
create_makefile('curb_core')
|
data/lib/curb.rb
CHANGED
data/lib/curl/easy.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Curl
|
2
3
|
class Easy
|
3
4
|
|
@@ -19,9 +20,9 @@ module Curl
|
|
19
20
|
# easy.status => String
|
20
21
|
#
|
21
22
|
def status
|
22
|
-
# Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF'
|
23
|
-
statuses = self.header_str.scan(/HTTP\/\d\.\d
|
24
|
-
statuses.last.strip
|
23
|
+
# Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP (Opt:)Reason-Phrase CRLF'
|
24
|
+
statuses = self.header_str.scan(/HTTP\/\d(\.\d)?\s(\d+\s.*)\r\n/).map{ |match| match[1] }
|
25
|
+
statuses.last.strip if statuses.length > 0
|
25
26
|
end
|
26
27
|
|
27
28
|
#
|
@@ -68,9 +69,14 @@ module Curl
|
|
68
69
|
ret = self.multi.perform
|
69
70
|
self.multi.remove self
|
70
71
|
|
72
|
+
if Curl::Multi.autoclose
|
73
|
+
self.multi.close
|
74
|
+
self.multi = nil
|
75
|
+
end
|
76
|
+
|
71
77
|
if self.last_result != 0 && self.on_failure.nil?
|
72
78
|
error = Curl::Easy.error(self.last_result)
|
73
|
-
raise error.first.new(
|
79
|
+
raise error.first.new(self.head)
|
74
80
|
end
|
75
81
|
|
76
82
|
ret
|
data/lib/curl/multi.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Curl
|
2
|
-
|
3
3
|
class Multi
|
4
4
|
class << self
|
5
5
|
# call-seq:
|
6
6
|
# Curl::Multi.get(['url1','url2','url3','url4','url5'], :follow_location => true) do|easy|
|
7
7
|
# easy
|
8
8
|
# end
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Blocking call to fetch multiple url's in parallel.
|
11
11
|
def get(urls, easy_options={}, multi_options={}, &blk)
|
12
12
|
url_confs = []
|
@@ -25,10 +25,10 @@ module Curl
|
|
25
25
|
# {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
|
26
26
|
# easy_handle_on_request_complete
|
27
27
|
# end
|
28
|
-
#
|
28
|
+
#
|
29
29
|
# Blocking call to POST multiple form's in parallel.
|
30
|
-
#
|
31
|
-
# urls_with_config: is a hash of url's pointing to the postfields to send
|
30
|
+
#
|
31
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
32
32
|
# easy_options: are a set of common options to set on all easy handles
|
33
33
|
# multi_options: options to set on the Curl::Multi handle
|
34
34
|
#
|
@@ -49,10 +49,10 @@ module Curl
|
|
49
49
|
# {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
|
50
50
|
# easy_handle_on_request_complete
|
51
51
|
# end
|
52
|
-
#
|
52
|
+
#
|
53
53
|
# Blocking call to POST multiple form's in parallel.
|
54
|
-
#
|
55
|
-
# urls_with_config: is a hash of url's pointing to the postfields to send
|
54
|
+
#
|
55
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
56
56
|
# easy_options: are a set of common options to set on all easy handles
|
57
57
|
# multi_options: options to set on the Curl::Multi handle
|
58
58
|
#
|
@@ -79,7 +79,7 @@ module Curl
|
|
79
79
|
# Blocking call to issue multiple HTTP requests with varying verb's.
|
80
80
|
#
|
81
81
|
# 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
|
82
|
-
# multi_options: options for the multi handle
|
82
|
+
# multi_options: options for the multi handle
|
83
83
|
# blk: a callback, that yeilds when a handle is completed
|
84
84
|
#
|
85
85
|
def http(urls_with_config, multi_options={}, &blk)
|
@@ -128,7 +128,7 @@ module Curl
|
|
128
128
|
|
129
129
|
# headers is a special key
|
130
130
|
headers.each {|k,v| easy.headers[k] = v } if headers
|
131
|
-
|
131
|
+
|
132
132
|
#
|
133
133
|
# use the remaining options as specific configuration to the easy handle
|
134
134
|
# bad options should raise an undefined method error
|
@@ -144,7 +144,7 @@ module Curl
|
|
144
144
|
|
145
145
|
max_connects.times do
|
146
146
|
conf = urls_with_config.pop
|
147
|
-
add_free_handle.call
|
147
|
+
add_free_handle.call(conf, nil) if conf
|
148
148
|
break if urls_with_config.empty?
|
149
149
|
end
|
150
150
|
|
@@ -153,7 +153,7 @@ module Curl
|
|
153
153
|
if urls_with_config.size > 0 && free_handles.size > 0
|
154
154
|
easy = free_handles.pop
|
155
155
|
conf = urls_with_config.pop
|
156
|
-
add_free_handle.call
|
156
|
+
add_free_handle.call(conf, easy) if conf
|
157
157
|
end
|
158
158
|
end
|
159
159
|
|
@@ -168,6 +168,7 @@ module Curl
|
|
168
168
|
end
|
169
169
|
free_handles = nil
|
170
170
|
end
|
171
|
+
|
171
172
|
end
|
172
173
|
|
173
174
|
# call-seq:
|
@@ -175,7 +176,7 @@ module Curl
|
|
175
176
|
# Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
|
176
177
|
#
|
177
178
|
# will create 2 new files file1.txt and file2.txt
|
178
|
-
#
|
179
|
+
#
|
179
180
|
# 2 files will be opened, and remain open until the call completes
|
180
181
|
#
|
181
182
|
# when using the :post or :put method, urls should be a hash, including the individual post fields per post
|
@@ -242,7 +243,45 @@ module Curl
|
|
242
243
|
}
|
243
244
|
raise errors unless errors.empty?
|
244
245
|
end
|
246
|
+
end
|
245
247
|
|
248
|
+
def cancel!
|
249
|
+
requests.each do |_,easy|
|
250
|
+
remove(easy)
|
251
|
+
end
|
246
252
|
end
|
253
|
+
|
254
|
+
def idle?
|
255
|
+
requests.empty?
|
256
|
+
end
|
257
|
+
|
258
|
+
def requests
|
259
|
+
@requests ||= {}
|
260
|
+
end
|
261
|
+
|
262
|
+
def add(easy)
|
263
|
+
return self if requests[easy.object_id]
|
264
|
+
requests[easy.object_id] = easy
|
265
|
+
_add(easy)
|
266
|
+
self
|
267
|
+
end
|
268
|
+
|
269
|
+
def remove(easy)
|
270
|
+
return self if !requests[easy.object_id]
|
271
|
+
requests.delete(easy.object_id)
|
272
|
+
_remove(easy)
|
273
|
+
self
|
274
|
+
end
|
275
|
+
|
276
|
+
def close
|
277
|
+
requests.values.each {|easy|
|
278
|
+
_remove(easy)
|
279
|
+
}
|
280
|
+
@requests = {}
|
281
|
+
_close
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
285
|
+
|
247
286
|
end
|
248
287
|
end
|
data/lib/curl.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'curb_core'
|
2
3
|
require 'curl/easy'
|
3
4
|
require 'curl/multi'
|
@@ -6,7 +7,7 @@ require 'cgi'
|
|
6
7
|
|
7
8
|
# expose shortcut methods
|
8
9
|
module Curl
|
9
|
-
|
10
|
+
|
10
11
|
def self.http(verb, url, post_body=nil, put_data=nil, &block)
|
11
12
|
handle = Thread.current[:curb_curl] ||= Curl::Easy.new
|
12
13
|
handle.reset
|
@@ -47,14 +48,13 @@ module Curl
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def self.urlalize(url, params={})
|
50
|
-
|
51
|
-
if
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
51
|
+
uri = URI(url)
|
52
|
+
# early return if we didn't specify any extra params
|
53
|
+
return uri.to_s if (params || {}).empty?
|
54
|
+
|
55
|
+
params_query = URI.encode_www_form(params || {})
|
56
|
+
uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
|
57
|
+
uri.to_s
|
58
58
|
end
|
59
59
|
|
60
60
|
def self.postalize(params={})
|
@@ -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')
|
@@ -18,7 +19,7 @@ rescue LoadError
|
|
18
19
|
end
|
19
20
|
require 'fileutils'
|
20
21
|
|
21
|
-
$TEST_URL = "file://#{
|
22
|
+
$TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/}#{File.expand_path(__FILE__).tr('\\','/')}"
|
22
23
|
|
23
24
|
require 'thread'
|
24
25
|
require 'webrick'
|
@@ -30,19 +31,22 @@ require 'webrick'
|
|
30
31
|
TEST_SINGLE_THREADED=false
|
31
32
|
|
32
33
|
# keep webrick quiet
|
33
|
-
|
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
|
34
38
|
def access_log(config, req, res)
|
35
39
|
# nop
|
36
40
|
end
|
37
41
|
end
|
38
|
-
|
42
|
+
::WEBrick::BasicLog.class_eval do
|
39
43
|
def log(level, data)
|
40
44
|
# nop
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
44
48
|
#
|
45
|
-
# 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
|
46
50
|
# request type, e.g. GET,POST,PUT,DELETE
|
47
51
|
#
|
48
52
|
class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
@@ -52,13 +56,12 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
52
56
|
end
|
53
57
|
|
54
58
|
def self.port
|
55
|
-
|
59
|
+
@port ||= 9129
|
56
60
|
end
|
57
61
|
|
58
62
|
def self.path
|
59
63
|
'/methods'
|
60
64
|
end
|
61
|
-
|
62
65
|
def self.url
|
63
66
|
"http://127.0.0.1:#{port}#{path}"
|
64
67
|
end
|
@@ -70,12 +73,12 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
70
73
|
end
|
71
74
|
|
72
75
|
def do_GET(req,res)
|
73
|
-
if req.path.match
|
76
|
+
if req.path.match(/redirect$/)
|
74
77
|
res.status = 302
|
75
78
|
res['Location'] = '/foo'
|
76
|
-
elsif req.path.match
|
79
|
+
elsif req.path.match(/not_here$/)
|
77
80
|
res.status = 404
|
78
|
-
elsif req.path.match
|
81
|
+
elsif req.path.match(/error$/)
|
79
82
|
res.status = 500
|
80
83
|
end
|
81
84
|
respond_with("GET#{req.query_string}",req,res)
|
@@ -94,6 +97,9 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
94
97
|
end
|
95
98
|
if params and params['s'] == '500'
|
96
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)
|
97
103
|
else
|
98
104
|
respond_with("POST\n#{req.body}",req,res)
|
99
105
|
end
|
@@ -136,7 +142,7 @@ module TestServerMethods
|
|
136
142
|
|
137
143
|
def server_setup(port=9129,servlet=TestServlet)
|
138
144
|
@__port = port
|
139
|
-
if @server.nil? and !File.exist?(locked_file)
|
145
|
+
if (@server ||= nil).nil? and !File.exist?(locked_file)
|
140
146
|
|
141
147
|
File.open(locked_file,'w') {|f| f << 'locked' }
|
142
148
|
if TEST_SINGLE_THREADED
|
@@ -145,7 +151,7 @@ module TestServerMethods
|
|
145
151
|
rd.close
|
146
152
|
rd = nil
|
147
153
|
|
148
|
-
# start up a webrick server for testing delete
|
154
|
+
# start up a webrick server for testing delete
|
149
155
|
server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
150
156
|
|
151
157
|
server.mount(servlet.path, servlet)
|
@@ -161,7 +167,7 @@ module TestServerMethods
|
|
161
167
|
rd.read
|
162
168
|
rd.close
|
163
169
|
else
|
164
|
-
# start up a webrick server for testing delete
|
170
|
+
# start up a webrick server for testing delete
|
165
171
|
@server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
166
172
|
|
167
173
|
@server.mount(servlet.path, servlet)
|
@@ -202,3 +208,81 @@ module TestServerMethods
|
|
202
208
|
rescue Errno::EADDRINUSE
|
203
209
|
end
|
204
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
@@ -11,7 +11,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
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
|