curb 0.9.3 → 1.0.1
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 +76 -21
- data/Rakefile +33 -20
- data/ext/banned.h +32 -0
- data/ext/curb.c +186 -14
- data/ext/curb.h +18 -5
- data/ext/curb_easy.c +493 -84
- data/ext/curb_easy.h +7 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_multi.c +141 -218
- data/ext/curb_multi.h +0 -1
- data/ext/curb_postfield.c +7 -7
- data/ext/extconf.rb +74 -6
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +16 -9
- data/lib/curl/multi.rb +52 -13
- data/lib/curl.rb +20 -12
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +96 -13
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +3 -3
- data/tests/tc_curl_easy.rb +163 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +118 -15
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +21 -5
- metadata +33 -25
data/ext/extconf.rb
CHANGED
@@ -18,6 +18,8 @@ elsif !have_library('curl') or !have_header('curl/curl.h')
|
|
18
18
|
fail <<-EOM
|
19
19
|
Can't find libcurl or curl/curl.h
|
20
20
|
|
21
|
+
Make sure development libs (ie libcurl4-openssl-dev) are installed on the system.
|
22
|
+
|
21
23
|
Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
|
22
24
|
options to extconf.
|
23
25
|
EOM
|
@@ -59,6 +61,9 @@ def have_constant(name)
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
64
|
+
have_constant "curlopt_tcp_keepalive"
|
65
|
+
have_constant "curlopt_tcp_keepidle"
|
66
|
+
have_constant "curlopt_tcp_keepintvl"
|
62
67
|
have_constant "curlinfo_appconnect_time"
|
63
68
|
have_constant "curlinfo_redirect_time"
|
64
69
|
have_constant "curlinfo_response_code"
|
@@ -66,6 +71,7 @@ have_constant "curlinfo_filetime"
|
|
66
71
|
have_constant "curlinfo_redirect_count"
|
67
72
|
have_constant "curlinfo_os_errno"
|
68
73
|
have_constant "curlinfo_num_connects"
|
74
|
+
have_constant "curlinfo_cookielist"
|
69
75
|
have_constant "curlinfo_ftp_entry_path"
|
70
76
|
have_constant "curl_version_ssl"
|
71
77
|
have_constant "curl_version_libz"
|
@@ -83,6 +89,7 @@ have_constant "curlproxy_http"
|
|
83
89
|
have_constant "curlproxy_socks4"
|
84
90
|
have_constant "curlproxy_socks4a"
|
85
91
|
have_constant "curlproxy_socks5"
|
92
|
+
have_constant "curlproxy_socks5_hostname"
|
86
93
|
have_constant "curlauth_basic"
|
87
94
|
have_constant "curlauth_digest"
|
88
95
|
have_constant "curlauth_gssnegotiate"
|
@@ -101,6 +108,12 @@ have_constant "curle_send_fail_rewind"
|
|
101
108
|
have_constant "curle_ssl_engine_initfailed"
|
102
109
|
have_constant "curle_login_denied"
|
103
110
|
|
111
|
+
# older than 7.10.0
|
112
|
+
have_constant "curlopt_nosignal"
|
113
|
+
|
114
|
+
# older than 7.16.0
|
115
|
+
have_constant "curlmopt_pipelining"
|
116
|
+
|
104
117
|
# older than 7.16.3
|
105
118
|
have_constant "curlmopt_maxconnects"
|
106
119
|
|
@@ -145,6 +158,8 @@ have_func("curl_multi_timeout")
|
|
145
158
|
have_func("curl_multi_fdset")
|
146
159
|
have_func("curl_multi_perform")
|
147
160
|
|
161
|
+
have_constant "curlopt_haproxyprotocol"
|
162
|
+
|
148
163
|
# constants
|
149
164
|
have_constant "curlopt_interleavefunction"
|
150
165
|
have_constant "curlopt_interleavedata"
|
@@ -209,6 +224,7 @@ have_constant "curlopt_httppost"
|
|
209
224
|
have_constant "curlopt_referer"
|
210
225
|
have_constant "curlopt_useragent"
|
211
226
|
have_constant "curlopt_httpheader"
|
227
|
+
have_constant "curlopt_proxyheader"
|
212
228
|
have_constant "curlopt_http200aliases"
|
213
229
|
have_constant "curlopt_cookie"
|
214
230
|
have_constant "curlopt_cookiefile"
|
@@ -318,14 +334,17 @@ have_constant "curlopt_sslengine"
|
|
318
334
|
have_constant "curlopt_sslengine_default"
|
319
335
|
have_constant "curlopt_sslversion"
|
320
336
|
have_constant "curl_sslversion_default"
|
321
|
-
have_constant
|
322
|
-
have_constant
|
323
|
-
have_constant
|
337
|
+
have_constant :CURL_SSLVERSION_TLSv1
|
338
|
+
have_constant :CURL_SSLVERSION_SSLv2
|
339
|
+
have_constant :CURL_SSLVERSION_SSLv3
|
324
340
|
|
325
341
|
# Added in 7.34.0
|
326
|
-
have_constant
|
327
|
-
have_constant
|
328
|
-
have_constant
|
342
|
+
have_constant :CURL_SSLVERSION_TLSv1_0
|
343
|
+
have_constant :CURL_SSLVERSION_TLSv1_1
|
344
|
+
have_constant :CURL_SSLVERSION_TLSv1_2
|
345
|
+
|
346
|
+
# Added in 7.52.0
|
347
|
+
have_constant :CURL_SSLVERSION_TLSv1_3
|
329
348
|
|
330
349
|
have_constant "curlopt_ssl_verifypeer"
|
331
350
|
have_constant "curlopt_cainfo"
|
@@ -360,6 +379,16 @@ have_constant "curle_not_built_in"
|
|
360
379
|
|
361
380
|
have_constant "curle_obsolete" # removed in 7.24 ?
|
362
381
|
|
382
|
+
have_constant "curle_ftp_pret_failed"
|
383
|
+
have_constant "curle_rtsp_cseq_error"
|
384
|
+
have_constant "curle_rtsp_session_error"
|
385
|
+
have_constant "curle_ftp_bad_file_list"
|
386
|
+
have_constant "curle_chunk_failed"
|
387
|
+
have_constant "curle_no_connection_available"
|
388
|
+
have_constant "curle_ssl_pinnedpubkeynotmatch"
|
389
|
+
have_constant "curle_ssl_invalidcertstatus"
|
390
|
+
have_constant "curle_http2_stream"
|
391
|
+
|
363
392
|
# gssapi/spnego delegation related constants
|
364
393
|
have_constant "curlopt_gssapi_delegation"
|
365
394
|
have_constant "curlgssapi_delegation_policy_flag"
|
@@ -370,6 +399,43 @@ have_constant "CURLM_ADDED_ALREADY"
|
|
370
399
|
# added in 7.40.0
|
371
400
|
have_constant "curlopt_unix_socket_path"
|
372
401
|
|
402
|
+
# added in 7.42.0
|
403
|
+
have_constant "curlopt_path_as_is"
|
404
|
+
|
405
|
+
# added in 7.43.0
|
406
|
+
have_constant "curlopt_pipewait"
|
407
|
+
|
408
|
+
# protocol constants
|
409
|
+
have_constant "curlproto_all"
|
410
|
+
have_constant "curlproto_dict"
|
411
|
+
have_constant "curlproto_file"
|
412
|
+
have_constant "curlproto_ftp"
|
413
|
+
have_constant "curlproto_ftps"
|
414
|
+
have_constant "curlproto_gopher"
|
415
|
+
have_constant "curlproto_http"
|
416
|
+
have_constant "curlproto_https"
|
417
|
+
have_constant "curlproto_imap"
|
418
|
+
have_constant "curlproto_imaps"
|
419
|
+
have_constant "curlproto_ldap"
|
420
|
+
have_constant "curlproto_ldaps"
|
421
|
+
have_constant "curlproto_pop3"
|
422
|
+
have_constant "curlproto_pop3s"
|
423
|
+
have_constant "curlproto_rtmp"
|
424
|
+
have_constant "curlproto_rtmpe"
|
425
|
+
have_constant "curlproto_rtmps"
|
426
|
+
have_constant "curlproto_rtmpt"
|
427
|
+
have_constant "curlproto_rtmpte"
|
428
|
+
have_constant "curlproto_rtmpts"
|
429
|
+
have_constant "curlproto_rtsp"
|
430
|
+
have_constant "curlproto_scp"
|
431
|
+
have_constant "curlproto_sftp"
|
432
|
+
have_constant "curlproto_smb"
|
433
|
+
have_constant "curlproto_smbs"
|
434
|
+
have_constant "curlproto_smtp"
|
435
|
+
have_constant "curlproto_smtps"
|
436
|
+
have_constant "curlproto_telnet"
|
437
|
+
have_constant "curlproto_tftp"
|
438
|
+
|
373
439
|
if try_compile('int main() { return 0; }','-Wall')
|
374
440
|
$CFLAGS << ' -Wall'
|
375
441
|
end
|
@@ -397,6 +463,8 @@ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
|
397
463
|
|
398
464
|
have_func('rb_thread_blocking_region')
|
399
465
|
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
466
|
+
have_header('ruby/io.h')
|
467
|
+
have_func('rb_io_stdio_file')
|
400
468
|
|
401
469
|
create_header('curb_config.h')
|
402
470
|
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.to_s.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,15 @@ 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
|
-
|
73
|
-
|
78
|
+
(err_class, err_summary) = Curl::Easy.error(self.last_result)
|
79
|
+
err_detail = self.last_error
|
80
|
+
raise err_class.new([err_summary, err_detail].compact.join(": "))
|
74
81
|
end
|
75
82
|
|
76
83
|
ret
|
@@ -314,7 +321,7 @@ module Curl
|
|
314
321
|
|
315
322
|
#
|
316
323
|
# call-seq:
|
317
|
-
# Curl::Easy.perform(url) { |easy| ... } =>
|
324
|
+
# Curl::Easy.perform(url) { |easy| ... } => #<Curl::Easy...>
|
318
325
|
#
|
319
326
|
# Convenience method that creates a new Curl::Easy instance with
|
320
327
|
# the specified URL and calls the general +perform+ method, before returning
|
@@ -332,7 +339,7 @@ module Curl
|
|
332
339
|
|
333
340
|
#
|
334
341
|
# call-seq:
|
335
|
-
# Curl::Easy.http_get(url) { |easy| ... } =>
|
342
|
+
# Curl::Easy.http_get(url) { |easy| ... } => #<Curl::Easy...>
|
336
343
|
#
|
337
344
|
# Convenience method that creates a new Curl::Easy instance with
|
338
345
|
# the specified URL and calls +http_get+, before returning the new instance.
|
@@ -349,7 +356,7 @@ module Curl
|
|
349
356
|
|
350
357
|
#
|
351
358
|
# call-seq:
|
352
|
-
# Curl::Easy.http_head(url) { |easy| ... } =>
|
359
|
+
# Curl::Easy.http_head(url) { |easy| ... } => #<Curl::Easy...>
|
353
360
|
#
|
354
361
|
# Convenience method that creates a new Curl::Easy instance with
|
355
362
|
# the specified URL and calls +http_head+, before returning the new instance.
|
@@ -403,7 +410,7 @@ module Curl
|
|
403
410
|
|
404
411
|
#
|
405
412
|
# call-seq:
|
406
|
-
# Curl::Easy.http_delete(url) { |easy| ... } =>
|
413
|
+
# Curl::Easy.http_delete(url) { |easy| ... } => #<Curl::Easy...>
|
407
414
|
#
|
408
415
|
# Convenience method that creates a new Curl::Easy instance with
|
409
416
|
# the specified URL and calls +http_delete+, before returning the new instance.
|
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,14 +7,22 @@ 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
|
-
|
12
|
+
if Thread.current[:curb_curl_yielding]
|
13
|
+
handle = Curl::Easy.new # we can't reuse this
|
14
|
+
else
|
15
|
+
handle = Thread.current[:curb_curl] ||= Curl::Easy.new
|
16
|
+
handle.reset
|
17
|
+
end
|
13
18
|
handle.url = url
|
14
19
|
handle.post_body = post_body if post_body
|
15
20
|
handle.put_data = put_data if put_data
|
16
|
-
|
21
|
+
if block_given?
|
22
|
+
Thread.current[:curb_curl_yielding] = true
|
23
|
+
yield handle
|
24
|
+
Thread.current[:curb_curl_yielding] = false
|
25
|
+
end
|
17
26
|
handle.http(verb)
|
18
27
|
handle
|
19
28
|
end
|
@@ -47,14 +56,13 @@ module Curl
|
|
47
56
|
end
|
48
57
|
|
49
58
|
def self.urlalize(url, params={})
|
50
|
-
|
51
|
-
if
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
59
|
+
uri = URI(url)
|
60
|
+
# early return if we didn't specify any extra params
|
61
|
+
return uri.to_s if (params || {}).empty?
|
62
|
+
|
63
|
+
params_query = URI.encode_www_form(params || {})
|
64
|
+
uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
|
65
|
+
uri.to_s
|
58
66
|
end
|
59
67
|
|
60
68
|
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,8 +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)
|
140
|
-
|
145
|
+
if (@server ||= nil).nil? and !File.exist?(locked_file)
|
141
146
|
File.open(locked_file,'w') {|f| f << 'locked' }
|
142
147
|
if TEST_SINGLE_THREADED
|
143
148
|
rd, wr = IO.pipe
|
@@ -145,7 +150,7 @@ module TestServerMethods
|
|
145
150
|
rd.close
|
146
151
|
rd = nil
|
147
152
|
|
148
|
-
# start up a webrick server for testing delete
|
153
|
+
# start up a webrick server for testing delete
|
149
154
|
server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
150
155
|
|
151
156
|
server.mount(servlet.path, servlet)
|
@@ -161,7 +166,7 @@ module TestServerMethods
|
|
161
166
|
rd.read
|
162
167
|
rd.close
|
163
168
|
else
|
164
|
-
# start up a webrick server for testing delete
|
169
|
+
# start up a webrick server for testing delete
|
165
170
|
@server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))
|
166
171
|
|
167
172
|
@server.mount(servlet.path, servlet)
|
@@ -202,3 +207,81 @@ module TestServerMethods
|
|
202
207
|
rescue Errno::EADDRINUSE
|
203
208
|
end
|
204
209
|
end
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
# Backport for Ruby 1.8
|
214
|
+
module Backports
|
215
|
+
module Ruby18
|
216
|
+
module URIFormEncoding
|
217
|
+
TBLENCWWWCOMP_ = {}
|
218
|
+
TBLDECWWWCOMP_ = {}
|
219
|
+
|
220
|
+
def encode_www_form_component(str)
|
221
|
+
if TBLENCWWWCOMP_.empty?
|
222
|
+
256.times do |i|
|
223
|
+
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
224
|
+
end
|
225
|
+
TBLENCWWWCOMP_[' '] = '+'
|
226
|
+
TBLENCWWWCOMP_.freeze
|
227
|
+
end
|
228
|
+
str.to_s.gsub( /([^*\-.0-9A-Z_a-z])/ ) {|*| TBLENCWWWCOMP_[$1] }
|
229
|
+
end
|
230
|
+
|
231
|
+
def decode_www_form_component(str)
|
232
|
+
if TBLDECWWWCOMP_.empty?
|
233
|
+
256.times do |i|
|
234
|
+
h, l = i>>4, i&15
|
235
|
+
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
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
|
+
end
|
240
|
+
TBLDECWWWCOMP_['+'] = ' '
|
241
|
+
TBLDECWWWCOMP_.freeze
|
242
|
+
end
|
243
|
+
|
244
|
+
raise ArgumentError, "invalid %-encoding (#{str.dump})" unless /\A(?:%[[:xdigit:]]{2}|[^%]+)*\z/ =~ str
|
245
|
+
str.gsub( /(\+|%[[:xdigit:]]{2})/ ) {|*| TBLDECWWWCOMP_[$1] }
|
246
|
+
end
|
247
|
+
|
248
|
+
def encode_www_form( enum )
|
249
|
+
enum.map do |k,v|
|
250
|
+
if v.nil?
|
251
|
+
encode_www_form_component(k)
|
252
|
+
elsif v.respond_to?(:to_ary)
|
253
|
+
v.to_ary.map do |w|
|
254
|
+
str = encode_www_form_component(k)
|
255
|
+
unless w.nil?
|
256
|
+
str << '='
|
257
|
+
str << encode_www_form_component(w)
|
258
|
+
end
|
259
|
+
end.join('&')
|
260
|
+
else
|
261
|
+
str = encode_www_form_component(k)
|
262
|
+
str << '='
|
263
|
+
str << encode_www_form_component(v)
|
264
|
+
end
|
265
|
+
end.join('&')
|
266
|
+
end
|
267
|
+
|
268
|
+
WFKV_ = '(?:%\h\h|[^%#=;&])'
|
269
|
+
def decode_www_form(str, _)
|
270
|
+
return [] if str.to_s == ''
|
271
|
+
|
272
|
+
unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/ =~ str
|
273
|
+
raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})"
|
274
|
+
end
|
275
|
+
ary = []
|
276
|
+
$&.scan(/([^=;&]+)=([^;&]*)/) do
|
277
|
+
ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)]
|
278
|
+
end
|
279
|
+
ary
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
unless URI.methods.include?(:encode_www_form)
|
286
|
+
URI.extend(Backports::Ruby18::URIFormEncoding)
|
287
|
+
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
|