curb 1.3.5 → 1.3.7
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 +4 -4
- data/README.md +90 -13
- data/Rakefile +8 -3
- data/doc.rb +48 -8
- data/ext/curb.c +33 -0
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +1471 -87
- data/ext/curb_easy.h +26 -0
- data/ext/curb_errors.c +2 -0
- data/ext/curb_errors.h +1 -0
- data/ext/curb_multi.c +257 -130
- data/ext/curb_multi.h +1 -0
- data/ext/extconf.rb +12 -0
- data/lib/curl/download.rb +160 -0
- data/lib/curl/easy.rb +113 -13
- data/lib/curl/multi.rb +172 -39
- data/lib/curl.rb +471 -11
- data/tests/bug_poison.rb +29 -0
- data/tests/helper.rb +178 -1
- data/tests/io_select_less_scheduler_probe.rb +105 -0
- data/tests/tc_curl_download.rb +86 -0
- data/tests/tc_curl_easy.rb +77 -0
- data/tests/tc_curl_easy_resolve.rb +2 -0
- data/tests/tc_curl_maxfilesize.rb +201 -1
- data/tests/tc_curl_multi.rb +258 -0
- data/tests/tc_curl_native_coverage.rb +1 -0
- data/tests/tc_curl_network_policy.rb +1475 -0
- data/tests/tc_curl_protocols.rb +351 -0
- data/tests/tc_fiber_scheduler.rb +373 -4
- data/tests/tc_ftp_options.rb +58 -0
- data/tests/tc_gc_compact.rb +53 -0
- data/tests/tc_test_server_methods.rb +15 -0
- metadata +9 -2
data/tests/tc_curl_protocols.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
|
|
1
3
|
class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
2
4
|
include TestServerMethods
|
|
3
5
|
|
|
@@ -8,6 +10,11 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
8
10
|
server_setup
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
def teardown
|
|
14
|
+
Curl.__send__(:clear_safe!) if Curl.respond_to?(:clear_safe!, true)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
11
18
|
def test_protocol_allowed
|
|
12
19
|
@easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html"
|
|
13
20
|
@easy.perform
|
|
@@ -34,4 +41,348 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
34
41
|
@easy.perform
|
|
35
42
|
end
|
|
36
43
|
end
|
|
44
|
+
|
|
45
|
+
def test_protocols_str_setopt_supported
|
|
46
|
+
omit('CURLOPT_PROTOCOLS_STR not supported by this libcurl') unless Curl.const_defined?(:CURLOPT_PROTOCOLS_STR)
|
|
47
|
+
|
|
48
|
+
@easy.set :protocols_str, 'http,https'
|
|
49
|
+
@easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html"
|
|
50
|
+
@easy.perform
|
|
51
|
+
assert_equal 404, @easy.response_code
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_redir_protocols_str_setopt_supported
|
|
55
|
+
omit('CURLOPT_REDIR_PROTOCOLS_STR not supported by this libcurl') unless Curl.const_defined?(:CURLOPT_REDIR_PROTOCOLS_STR)
|
|
56
|
+
|
|
57
|
+
@easy.set :url, TestServlet.url + "/redirect"
|
|
58
|
+
@easy.set :redir_protocols_str, 'https'
|
|
59
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
60
|
+
@easy.perform
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_allowed_protocols_denies_file_protocol
|
|
65
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
66
|
+
easy.allowed_protocols = [:http, :https]
|
|
67
|
+
|
|
68
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
69
|
+
easy.perform
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_safe_http_denies_file_protocol
|
|
74
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
75
|
+
easy.safe_http!
|
|
76
|
+
|
|
77
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
78
|
+
easy.perform
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_safe_http_override_is_cleared_by_close
|
|
83
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
84
|
+
easy.safe_http!
|
|
85
|
+
easy.close
|
|
86
|
+
easy.url = $TEST_URL
|
|
87
|
+
|
|
88
|
+
easy.perform
|
|
89
|
+
|
|
90
|
+
assert_match(/TESTMODEL/, easy.body_str)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_safe_get_denies_file_protocol
|
|
94
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
95
|
+
Curl.safe_get($TEST_URL)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_safe_get_reapplies_protocols_after_user_block
|
|
100
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
101
|
+
Curl.safe_get($TEST_URL) do |easy|
|
|
102
|
+
easy.allowed_protocols = [:file]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_safe_get_allows_http_protocol
|
|
108
|
+
easy = Curl.safe_get(TestServlet.url)
|
|
109
|
+
|
|
110
|
+
assert_equal 200, easy.response_code
|
|
111
|
+
assert_match(/GET/, easy.body_str)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_safe_bang_denies_file_protocol_for_easy_perform
|
|
115
|
+
Curl.safe!
|
|
116
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
117
|
+
|
|
118
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
119
|
+
easy.perform
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_safe_bang_denies_file_protocol_for_shortcut_helpers
|
|
124
|
+
Curl.safe!
|
|
125
|
+
|
|
126
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
127
|
+
Curl.get($TEST_URL)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_safe_bang_reapplies_protocols_after_shortcut_block
|
|
132
|
+
Curl.safe!
|
|
133
|
+
|
|
134
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
135
|
+
Curl.get($TEST_URL) do |easy|
|
|
136
|
+
easy.allowed_protocols = [:file]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def test_safe_bang_allows_configured_protocols
|
|
142
|
+
Curl.safe! do |config|
|
|
143
|
+
config.protocols = [:http, :file]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
147
|
+
easy.perform
|
|
148
|
+
|
|
149
|
+
assert_match(/DO NOT REMOVE THIS COMMENT/, easy.body_str)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_safe_get_remains_http_safe_with_broader_global_policy
|
|
153
|
+
Curl.safe! do |config|
|
|
154
|
+
config.protocols = [:http, :file]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
158
|
+
Curl.safe_get($TEST_URL)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_safe_bang_allows_http_protocol
|
|
163
|
+
Curl.safe!
|
|
164
|
+
easy = Curl.get(TestServlet.url)
|
|
165
|
+
|
|
166
|
+
assert_equal 200, easy.response_code
|
|
167
|
+
assert_match(/GET/, easy.body_str)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_safe_bang_applies_to_multi_perform
|
|
171
|
+
Curl.safe!
|
|
172
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
173
|
+
multi = Curl::Multi.new
|
|
174
|
+
|
|
175
|
+
multi.add(easy)
|
|
176
|
+
multi.perform
|
|
177
|
+
|
|
178
|
+
assert_equal Curl::Err::UnsupportedProtocolError, Curl::Easy.error(easy.last_result).first
|
|
179
|
+
ensure
|
|
180
|
+
multi.close if defined?(multi) && multi
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_safe_http_denies_file_redirect
|
|
184
|
+
with_file_redirect_server do |url|
|
|
185
|
+
easy = Curl::Easy.new(url)
|
|
186
|
+
easy.follow_location = true
|
|
187
|
+
easy.safe_http!
|
|
188
|
+
|
|
189
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
190
|
+
easy.perform
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_safe_bang_denies_file_redirect
|
|
196
|
+
with_file_redirect_server do |url|
|
|
197
|
+
Curl.safe!
|
|
198
|
+
|
|
199
|
+
easy = Curl::Easy.new(url)
|
|
200
|
+
easy.follow_location = true
|
|
201
|
+
|
|
202
|
+
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
203
|
+
easy.perform
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def test_safe_http_allows_http_to_https_redirect
|
|
209
|
+
with_http_https_redirect_servers(:http, :https) do |url|
|
|
210
|
+
easy = Curl::Easy.new(url)
|
|
211
|
+
easy.follow_location = true
|
|
212
|
+
easy.ssl_verify_peer = false
|
|
213
|
+
easy.ssl_verify_host = false
|
|
214
|
+
easy.safe_http!
|
|
215
|
+
|
|
216
|
+
easy.perform
|
|
217
|
+
|
|
218
|
+
assert_equal 200, easy.response_code
|
|
219
|
+
assert_equal 'https target', easy.body_str
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def test_safe_http_allows_https_to_http_redirect
|
|
224
|
+
with_http_https_redirect_servers(:https, :http) do |url|
|
|
225
|
+
easy = Curl::Easy.new(url)
|
|
226
|
+
easy.follow_location = true
|
|
227
|
+
easy.ssl_verify_peer = false
|
|
228
|
+
easy.ssl_verify_host = false
|
|
229
|
+
easy.safe_http!
|
|
230
|
+
|
|
231
|
+
easy.perform
|
|
232
|
+
|
|
233
|
+
assert_equal 200, easy.response_code
|
|
234
|
+
assert_equal 'http target', easy.body_str
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_safe_http_honors_redirect_limit
|
|
239
|
+
with_redirect_loop_server do |url, hits|
|
|
240
|
+
easy = Curl::Easy.new(url)
|
|
241
|
+
easy.follow_location = true
|
|
242
|
+
easy.max_redirects = 2
|
|
243
|
+
easy.safe_http!
|
|
244
|
+
|
|
245
|
+
assert_raises Curl::Err::TooManyRedirectsError do
|
|
246
|
+
easy.perform
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
assert_operator hits[:loop], :>=, 2
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
def with_file_redirect_server
|
|
256
|
+
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
257
|
+
port = port_socket.addr[1]
|
|
258
|
+
port_socket.close
|
|
259
|
+
|
|
260
|
+
server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
261
|
+
server.mount_proc('/redirect-file') do |_req, res|
|
|
262
|
+
res.status = 302
|
|
263
|
+
res['Location'] = $TEST_URL
|
|
264
|
+
res.body = 'redirect'
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
thread = Thread.new(server) { |srv| srv.start }
|
|
268
|
+
wait_for_server_ready(port, thread: thread)
|
|
269
|
+
|
|
270
|
+
yield "http://127.0.0.1:#{port}/redirect-file"
|
|
271
|
+
ensure
|
|
272
|
+
server.shutdown if defined?(server) && server
|
|
273
|
+
thread.join(server_shutdown_timeout) if defined?(thread) && thread
|
|
274
|
+
if defined?(thread) && thread&.alive?
|
|
275
|
+
thread.kill
|
|
276
|
+
thread.join(server_shutdown_timeout)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def with_redirect_loop_server
|
|
281
|
+
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
282
|
+
port = port_socket.addr[1]
|
|
283
|
+
port_socket.close
|
|
284
|
+
|
|
285
|
+
hits = Hash.new(0)
|
|
286
|
+
server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
287
|
+
server.mount_proc('/loop') do |_req, res|
|
|
288
|
+
hits[:loop] += 1
|
|
289
|
+
res.status = 302
|
|
290
|
+
res['Location'] = '/loop'
|
|
291
|
+
res.body = 'redirect'
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
thread = Thread.new(server) { |srv| srv.start }
|
|
295
|
+
wait_for_server_ready(port, thread: thread)
|
|
296
|
+
|
|
297
|
+
yield "http://127.0.0.1:#{port}/loop", hits
|
|
298
|
+
ensure
|
|
299
|
+
server.shutdown if defined?(server) && server
|
|
300
|
+
thread.join(server_shutdown_timeout) if defined?(thread) && thread
|
|
301
|
+
if defined?(thread) && thread&.alive?
|
|
302
|
+
thread.kill
|
|
303
|
+
thread.join(server_shutdown_timeout)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def with_http_https_redirect_servers(initial_scheme, target_scheme)
|
|
308
|
+
require 'webrick/https'
|
|
309
|
+
require 'openssl'
|
|
310
|
+
|
|
311
|
+
omit('HTTPS redirect fixtures require OpenSSL') unless defined?(OpenSSL::PKey::RSA)
|
|
312
|
+
|
|
313
|
+
initial_server = nil
|
|
314
|
+
target_server = nil
|
|
315
|
+
initial_thread = nil
|
|
316
|
+
target_thread = nil
|
|
317
|
+
|
|
318
|
+
initial_port = unused_redirect_port
|
|
319
|
+
target_port = unused_redirect_port
|
|
320
|
+
target_server = redirect_matrix_server(target_scheme, target_port)
|
|
321
|
+
target_server.mount_proc('/target') do |_req, res|
|
|
322
|
+
res['Content-Type'] = 'text/plain'
|
|
323
|
+
res.body = "#{target_scheme} target"
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
target_thread = Thread.new(target_server) { |srv| srv.start }
|
|
327
|
+
wait_for_server_ready(target_port, thread: target_thread)
|
|
328
|
+
|
|
329
|
+
initial_server = redirect_matrix_server(initial_scheme, initial_port)
|
|
330
|
+
initial_server.mount_proc('/redirect') do |_req, res|
|
|
331
|
+
res.status = 302
|
|
332
|
+
res['Location'] = "#{target_scheme}://127.0.0.1:#{target_port}/target"
|
|
333
|
+
res.body = 'redirect'
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
initial_thread = Thread.new(initial_server) { |srv| srv.start }
|
|
337
|
+
wait_for_server_ready(initial_port, thread: initial_thread)
|
|
338
|
+
|
|
339
|
+
yield "#{initial_scheme}://127.0.0.1:#{initial_port}/redirect"
|
|
340
|
+
ensure
|
|
341
|
+
initial_server.shutdown if initial_server
|
|
342
|
+
target_server.shutdown if target_server
|
|
343
|
+
initial_thread.join(server_shutdown_timeout) if initial_thread
|
|
344
|
+
target_thread.join(server_shutdown_timeout) if target_thread
|
|
345
|
+
[initial_thread, target_thread].compact.each do |thread|
|
|
346
|
+
next unless thread.alive?
|
|
347
|
+
|
|
348
|
+
thread.kill
|
|
349
|
+
thread.join(server_shutdown_timeout)
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def redirect_matrix_server(scheme, port)
|
|
354
|
+
options = {:BindAddress => '127.0.0.1', :Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => []}
|
|
355
|
+
return WEBrick::HTTPServer.new(options) if scheme == :http
|
|
356
|
+
|
|
357
|
+
cert, key = redirect_matrix_certificate
|
|
358
|
+
WEBrick::HTTPServer.new(options.merge(:SSLEnable => true, :SSLCertificate => cert, :SSLPrivateKey => key))
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def redirect_matrix_certificate
|
|
362
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
|
363
|
+
cert = OpenSSL::X509::Certificate.new
|
|
364
|
+
cert.version = 2
|
|
365
|
+
cert.serial = 1
|
|
366
|
+
cert.subject = OpenSSL::X509::Name.parse('/CN=127.0.0.1')
|
|
367
|
+
cert.issuer = cert.subject
|
|
368
|
+
cert.public_key = key.public_key
|
|
369
|
+
cert.not_before = Time.now - 60
|
|
370
|
+
cert.not_after = Time.now + 3600
|
|
371
|
+
|
|
372
|
+
extensions = OpenSSL::X509::ExtensionFactory.new
|
|
373
|
+
extensions.subject_certificate = cert
|
|
374
|
+
extensions.issuer_certificate = cert
|
|
375
|
+
cert.add_extension(extensions.create_extension('basicConstraints', 'CA:FALSE', true))
|
|
376
|
+
cert.add_extension(extensions.create_extension('subjectAltName', 'IP:127.0.0.1', false))
|
|
377
|
+
cert.sign(key, OpenSSL::Digest::SHA256.new)
|
|
378
|
+
|
|
379
|
+
[cert, key]
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def unused_redirect_port
|
|
383
|
+
socket = TCPServer.new('127.0.0.1', 0)
|
|
384
|
+
socket.addr[1]
|
|
385
|
+
ensure
|
|
386
|
+
socket.close if socket
|
|
387
|
+
end
|
|
37
388
|
end
|