curb 1.4.0 → 1.5.0
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/ChangeLog.md +447 -0
- data/README.md +39 -8
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +567 -186
- data/ext/curb_easy.h +10 -0
- data/ext/curb_macros.h +16 -0
- data/ext/curb_multi.c +83 -23
- data/ext/curb_multi.h +4 -0
- data/ext/curb_postfield.c +103 -114
- data/ext/curb_postfield.h +10 -1
- data/ext/extconf.rb +1 -0
- data/lib/curl/easy.rb +24 -4
- data/lib/curl/multi.rb +31 -9
- data/lib/curl.rb +78 -15
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +1 -1
- data/tests/bug_follow_redirect_288.rb +1 -1
- data/tests/bug_poison.rb +1 -1
- data/tests/bug_raise_on_callback.rb +1 -1
- data/tests/helper.rb +9 -8
- data/tests/tc_curl_easy.rb +304 -0
- data/tests/tc_curl_easy_cookielist.rb +10 -8
- data/tests/tc_curl_maxfilesize.rb +109 -5
- data/tests/tc_curl_multi.rb +32 -0
- data/tests/tc_curl_native_coverage.rb +57 -0
- data/tests/tc_curl_network_policy.rb +133 -33
- data/tests/tc_curl_postfield.rb +137 -0
- data/tests/tc_curl_protocols.rb +19 -7
- data/tests/tc_curl_security_hardening.rb +386 -0
- data/tests/tc_curl_transfer_guard.rb +112 -0
- data/tests/tc_fiber_scheduler.rb +46 -6
- data/tests/tc_ractor.rb +52 -1
- metadata +7 -2
|
@@ -12,6 +12,110 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
12
12
|
super
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def test_safe_bang_defaults_to_public_network_policy
|
|
16
|
+
assert_equal :public, Curl::SafetyConfig.new.network_policy
|
|
17
|
+
require_public_network_policy!
|
|
18
|
+
Curl.safe!
|
|
19
|
+
|
|
20
|
+
assert_raise(Curl::Err::UnsafeDestinationError) { Curl.get(TestServlet.url) }
|
|
21
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
22
|
+
easy.network_policy = :none
|
|
23
|
+
assert_raise(Curl::Err::UnsafeDestinationError) { easy.perform }
|
|
24
|
+
assert_equal :public, easy.network_policy
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_safe_bang_default_blocks_private_and_metadata_destinations
|
|
28
|
+
require_public_network_policy!
|
|
29
|
+
Curl.safe!
|
|
30
|
+
['10.0.0.1', '172.16.0.1', '192.168.0.1', '169.254.169.254', '[::1]'].each do |host|
|
|
31
|
+
assert_raise(Curl::Err::UnsafeDestinationError, host) do
|
|
32
|
+
Curl.get("http://#{host}/") do |easy|
|
|
33
|
+
easy.network_policy = :none
|
|
34
|
+
easy.connect_timeout_ms = 100
|
|
35
|
+
easy.timeout_ms = 200
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_safe_bang_default_applies_to_previously_added_multi_handles
|
|
42
|
+
require_public_network_policy!
|
|
43
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
44
|
+
multi = Curl::Multi.new
|
|
45
|
+
multi.add(easy)
|
|
46
|
+
Curl.safe!
|
|
47
|
+
multi.perform
|
|
48
|
+
|
|
49
|
+
assert_equal :public, easy.network_policy
|
|
50
|
+
assert_match(/127\.0\.0\.1/, easy.unsafe_destination_error)
|
|
51
|
+
ensure
|
|
52
|
+
multi.close if multi
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_safe_helpers_block_loopback_without_global_policy
|
|
56
|
+
require_public_network_policy!
|
|
57
|
+
[:get, :post, :put, :delete, :patch, :head, :options].each do |verb|
|
|
58
|
+
assert_raise(Curl::Err::UnsafeDestinationError, verb.to_s) do
|
|
59
|
+
Curl.public_send("safe_#{verb}", TestServlet.url) do |easy|
|
|
60
|
+
easy.network_policy = :none
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_safe_http_reapplies_public_policy_before_perform
|
|
67
|
+
require_public_network_policy!
|
|
68
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
69
|
+
assert_same easy, easy.safe_http!
|
|
70
|
+
assert_equal :public, easy.network_policy
|
|
71
|
+
easy.network_policy = :none
|
|
72
|
+
|
|
73
|
+
assert_raise(Curl::Err::UnsafeDestinationError) { easy.perform }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_safe_http_cannot_be_weakened_by_global_policy
|
|
77
|
+
require_public_network_policy!
|
|
78
|
+
Curl.safe! { |config| config.network_policy = :none }
|
|
79
|
+
|
|
80
|
+
assert_raise(Curl::Err::UnsafeDestinationError) { Curl.safe_get(TestServlet.url) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_safe_helpers_block_private_and_metadata_destinations
|
|
84
|
+
require_public_network_policy!
|
|
85
|
+
['10.0.0.1', '172.16.0.1', '192.168.0.1', '169.254.169.254', '[::1]'].each do |host|
|
|
86
|
+
assert_raise(Curl::Err::UnsafeDestinationError, host) do
|
|
87
|
+
Curl.safe_get("http://#{host}/") do |easy|
|
|
88
|
+
easy.connect_timeout_ms = 100
|
|
89
|
+
easy.timeout_ms = 200
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_safe_http_rejects_resolve_override_without_global_policy
|
|
96
|
+
require_public_network_policy!
|
|
97
|
+
assert_raise(Curl::Err::UnsafeDestinationError) do
|
|
98
|
+
Curl.safe_get('http://example.invalid/') do |easy|
|
|
99
|
+
easy.resolve = ['example.invalid:80:127.0.0.1']
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_safe_http_applies_to_multi
|
|
105
|
+
require_public_network_policy!
|
|
106
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
107
|
+
easy.safe_http!
|
|
108
|
+
easy.network_policy = :none
|
|
109
|
+
multi = Curl::Multi.new
|
|
110
|
+
multi.add(easy)
|
|
111
|
+
multi.perform
|
|
112
|
+
|
|
113
|
+
assert_equal :public, easy.network_policy
|
|
114
|
+
assert_match(/127\.0\.0\.1/, easy.unsafe_destination_error)
|
|
115
|
+
ensure
|
|
116
|
+
multi.close if multi
|
|
117
|
+
end
|
|
118
|
+
|
|
15
119
|
def test_network_policy_defaults_to_none
|
|
16
120
|
easy = Curl::Easy.new
|
|
17
121
|
|
|
@@ -95,6 +199,7 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
95
199
|
|
|
96
200
|
with_host_redirect_server(allowed_host, allowed_host) do |port, hits|
|
|
97
201
|
Curl.safe! do |config|
|
|
202
|
+
config.network_policy = :none # Isolate host filtering from peer filtering.
|
|
98
203
|
config.allowed_hosts = [allowed_host]
|
|
99
204
|
end
|
|
100
205
|
|
|
@@ -119,6 +224,7 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
119
224
|
|
|
120
225
|
with_host_redirect_server(allowed_host, blocked_host) do |port, hits|
|
|
121
226
|
Curl.safe! do |config|
|
|
227
|
+
config.network_policy = :none # Isolate host filtering from peer filtering.
|
|
122
228
|
config.allowed_hosts = [allowed_host]
|
|
123
229
|
end
|
|
124
230
|
|
|
@@ -142,6 +248,7 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
142
248
|
|
|
143
249
|
def test_safe_bang_allowed_cidrs_requires_public_network_policy
|
|
144
250
|
Curl.safe! do |config|
|
|
251
|
+
config.network_policy = :none # Isolate the host/CIDR policy from peer filtering.
|
|
145
252
|
config.allowed_cidrs = ['1.1.1.0/24']
|
|
146
253
|
end
|
|
147
254
|
|
|
@@ -911,43 +1018,36 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
911
1018
|
Curl.__send__(:clear_safe!) if Curl.respond_to?(:clear_safe!, true)
|
|
912
1019
|
end
|
|
913
1020
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1021
|
+
# The peer address is vetted at connect time for every resolution, so a
|
|
1022
|
+
# name that resolves to an allowed address once and a private one later is
|
|
1023
|
+
# still blocked. (This used a loopback DoH server as the changing resolver;
|
|
1024
|
+
# public policy now refuses non-public DoH endpoints, so CURLOPT_RESOLVE
|
|
1025
|
+
# supplies the two answers instead.)
|
|
1026
|
+
def test_public_network_policy_blocks_private_peer_after_dns_answer_changes
|
|
918
1027
|
require_public_network_policy!
|
|
919
1028
|
|
|
920
|
-
host = 'curb-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
end
|
|
927
|
-
|
|
928
|
-
first = Curl::Easy.new("http://#{host}/dns-rebind-target")
|
|
929
|
-
configure_live_doh_easy(first, doh_url)
|
|
930
|
-
|
|
931
|
-
first_error = assert_raise(Curl::Err::UnsafeDestinationError) do
|
|
932
|
-
first.perform
|
|
933
|
-
end
|
|
934
|
-
|
|
935
|
-
assert_include doh_a_answers(doh_state), '1.1.1.1'
|
|
936
|
-
assert_match(/outside allowed CIDR ranges/, first_error.message)
|
|
937
|
-
assert_match(/1\.1\.1\.1/, first_error.message)
|
|
938
|
-
|
|
939
|
-
set_doh_answer(doh_state, '127.0.0.1')
|
|
940
|
-
second = Curl::Easy.new("http://#{host}/dns-rebind-target")
|
|
941
|
-
configure_live_doh_easy(second, doh_url)
|
|
1029
|
+
host = 'curb-dns-rebind.invalid'
|
|
1030
|
+
Curl.safe! do |config|
|
|
1031
|
+
config.network_policy = :public
|
|
1032
|
+
config.allow_resolve = true
|
|
1033
|
+
config.allowed_cidrs = ['8.8.8.0/24']
|
|
1034
|
+
end
|
|
942
1035
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1036
|
+
first = Curl::Easy.new("http://#{host}/dns-rebind-target")
|
|
1037
|
+
first.resolve = ["#{host}:80:1.1.1.1"]
|
|
1038
|
+
first_error = assert_raise(Curl::Err::UnsafeDestinationError) do
|
|
1039
|
+
first.perform
|
|
1040
|
+
end
|
|
1041
|
+
assert_match(/outside allowed CIDR ranges/, first_error.message)
|
|
1042
|
+
assert_match(/1\.1\.1\.1/, first_error.message)
|
|
946
1043
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1044
|
+
second = Curl::Easy.new("http://#{host}/dns-rebind-target")
|
|
1045
|
+
second.resolve = ["#{host}:80:127.0.0.1"]
|
|
1046
|
+
error = assert_raise(Curl::Err::UnsafeDestinationError) do
|
|
1047
|
+
second.perform
|
|
950
1048
|
end
|
|
1049
|
+
assert_match(/public network policy/, error.message)
|
|
1050
|
+
assert_match(/127\.0\.0\.1/, error.message)
|
|
951
1051
|
ensure
|
|
952
1052
|
Curl.__send__(:clear_safe!) if Curl.respond_to?(:clear_safe!, true)
|
|
953
1053
|
end
|
|
@@ -1080,7 +1180,7 @@ class TestCurbCurlNetworkPolicy < Test::Unit::TestCase
|
|
|
1080
1180
|
multi = Curl::Multi.new
|
|
1081
1181
|
|
|
1082
1182
|
multi.add(easy)
|
|
1083
|
-
Curl.safe!
|
|
1183
|
+
Curl.safe! { |config| config.network_policy = :none }
|
|
1084
1184
|
multi.perform
|
|
1085
1185
|
|
|
1086
1186
|
assert_equal [['1']], request_headers
|
data/tests/tc_curl_postfield.rb
CHANGED
|
@@ -253,6 +253,143 @@ class TestCurbCurlPostfieldMultipartCoverage < Test::Unit::TestCase
|
|
|
253
253
|
server_setup
|
|
254
254
|
end
|
|
255
255
|
|
|
256
|
+
[:http_post, :http_patch, :http_put].each do |method|
|
|
257
|
+
[:close, :reset, :initialize, :perform, :http_post, :post_body=].each do |operation|
|
|
258
|
+
[:proc, :coercion].each do |source|
|
|
259
|
+
define_method("test_#{method}_guards_#{operation}_from_#{source}") do
|
|
260
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
261
|
+
curl.multipart_form_post = true
|
|
262
|
+
action = lambda do |*_|
|
|
263
|
+
if operation == :post_body=
|
|
264
|
+
curl.post_body = 'replacement'
|
|
265
|
+
elsif operation == :http_post
|
|
266
|
+
curl.http_post(Curl::PostField.content('nested', 'payload'))
|
|
267
|
+
else
|
|
268
|
+
curl.__send__(operation)
|
|
269
|
+
end
|
|
270
|
+
'payload'
|
|
271
|
+
end
|
|
272
|
+
field = if source == :proc
|
|
273
|
+
Curl::PostField.content('trigger', &action)
|
|
274
|
+
else
|
|
275
|
+
value = Object.new
|
|
276
|
+
value.define_singleton_method(:to_str, &action)
|
|
277
|
+
Curl::PostField.content('trigger', value)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
error = assert_raise(RuntimeError) do
|
|
281
|
+
curl.__send__(method, Curl::PostField.content('first', 'allocated'), field)
|
|
282
|
+
end
|
|
283
|
+
assert_match(/active curl handle/, error.message)
|
|
284
|
+
|
|
285
|
+
# Both partial forms and guards must unwind before reuse.
|
|
286
|
+
curl.multipart_form_post = false
|
|
287
|
+
curl.perform
|
|
288
|
+
assert_equal 'GET', curl.body_str
|
|
289
|
+
assert_nothing_raised { curl.reset }
|
|
290
|
+
assert_nothing_raised { curl.close }
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
[:content, :file].each do |kind|
|
|
297
|
+
[false, true].each do |dynamic|
|
|
298
|
+
define_method("test_multipart_snapshots_#{kind}_#{dynamic ? 'proc' : 'direct'}") do
|
|
299
|
+
name = +'original_name'
|
|
300
|
+
payload = +"original\0payload"
|
|
301
|
+
field = multipart_field(kind, dynamic, nil, payload)
|
|
302
|
+
field.name = name
|
|
303
|
+
later = Curl::PostField.content('later') do
|
|
304
|
+
Thread.new do
|
|
305
|
+
name.replace('changed_name' * 1000)
|
|
306
|
+
payload.replace('changed_payload' * 1000)
|
|
307
|
+
field.content = nil
|
|
308
|
+
field.name = nil
|
|
309
|
+
GC.start
|
|
310
|
+
GC.verify_compaction_references(double_heap: true, toward: :empty) if GC.respond_to?(:verify_compaction_references)
|
|
311
|
+
end.join
|
|
312
|
+
'later_value'
|
|
313
|
+
end
|
|
314
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
315
|
+
curl.multipart_form_post = true
|
|
316
|
+
curl.http_post(field, later)
|
|
317
|
+
|
|
318
|
+
assert_include curl.body_str, 'name="original_name"'
|
|
319
|
+
assert_include curl.body_str, "original\0payload"
|
|
320
|
+
assert_include curl.body_str, 'filename="remote.txt"' if kind == :file
|
|
321
|
+
assert_not_include curl.body_str, 'changed_payload'
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def test_multipart_snapshots_before_later_coercions_in_same_field
|
|
327
|
+
name = +'original_name'
|
|
328
|
+
payload = +'original_payload'
|
|
329
|
+
content_type = Object.new
|
|
330
|
+
content_type.define_singleton_method(:to_str) do
|
|
331
|
+
name.replace('changed_name' * 1000)
|
|
332
|
+
payload.replace('changed_payload' * 1000)
|
|
333
|
+
'text/plain'
|
|
334
|
+
end
|
|
335
|
+
field = Curl::PostField.content(name, payload, content_type)
|
|
336
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
337
|
+
curl.multipart_form_post = true
|
|
338
|
+
curl.http_post(field)
|
|
339
|
+
|
|
340
|
+
assert_include curl.body_str, 'name="original_name"'
|
|
341
|
+
assert_include curl.body_str, 'original_payload'
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Exercise each pointer/length pair with and without a content type.
|
|
345
|
+
[:content, :file].each do |kind|
|
|
346
|
+
[false, true].each do |dynamic|
|
|
347
|
+
[nil, 'text/plain'].each do |content_type|
|
|
348
|
+
variant = "#{kind}_#{dynamic ? 'proc' : 'direct'}_#{content_type ? 'typed' : 'untyped'}"
|
|
349
|
+
|
|
350
|
+
define_method("test_multipart_coerces_#{variant}") do
|
|
351
|
+
payload = "before\0after"
|
|
352
|
+
string_like = Object.new
|
|
353
|
+
string_like.define_singleton_method(:to_str) { payload }
|
|
354
|
+
field = multipart_field(kind, dynamic, content_type, string_like)
|
|
355
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
356
|
+
curl.multipart_form_post = true
|
|
357
|
+
|
|
358
|
+
curl.http_post(field)
|
|
359
|
+
|
|
360
|
+
assert_include curl.body_str, payload
|
|
361
|
+
assert_equal payload, field.content unless dynamic
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
define_method("test_multipart_rejects_non_string_#{variant}") do
|
|
365
|
+
field = multipart_field(kind, dynamic, content_type, 5)
|
|
366
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
367
|
+
curl.multipart_form_post = true
|
|
368
|
+
|
|
369
|
+
assert_raise(TypeError) { curl.http_post(field) }
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def multipart_field(kind, dynamic, content_type, value)
|
|
376
|
+
field = if dynamic
|
|
377
|
+
if kind == :file
|
|
378
|
+
Curl::PostField.file('upload', 'remote.txt') { value }
|
|
379
|
+
else
|
|
380
|
+
Curl::PostField.content('value') { value }
|
|
381
|
+
end
|
|
382
|
+
elsif kind == :file
|
|
383
|
+
upload = Curl::PostField.file('upload', 'remote.txt')
|
|
384
|
+
upload.content = value
|
|
385
|
+
upload
|
|
386
|
+
else
|
|
387
|
+
Curl::PostField.content('value', value)
|
|
388
|
+
end
|
|
389
|
+
field.content_type = content_type
|
|
390
|
+
field
|
|
391
|
+
end
|
|
392
|
+
|
|
256
393
|
def test_multipart_content_variants_include_dynamic_and_typed_fields
|
|
257
394
|
curl = Curl::Easy.new(TestServlet.url)
|
|
258
395
|
curl.multipart_form_post = true
|
data/tests/tc_curl_protocols.rb
CHANGED
|
@@ -104,11 +104,10 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
assert_match(/GET/, easy.body_str)
|
|
107
|
+
def test_safe_get_blocks_local_http_destination
|
|
108
|
+
assert_raise(Curl::Err::UnsafeDestinationError) do
|
|
109
|
+
Curl.safe_get(TestServlet.url)
|
|
110
|
+
end
|
|
112
111
|
end
|
|
113
112
|
|
|
114
113
|
def test_safe_bang_denies_file_protocol_for_easy_perform
|
|
@@ -159,8 +158,8 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
159
158
|
end
|
|
160
159
|
end
|
|
161
160
|
|
|
162
|
-
def
|
|
163
|
-
Curl.safe!
|
|
161
|
+
def test_safe_bang_explicit_none_allows_trusted_http_destination
|
|
162
|
+
Curl.safe! { |config| config.network_policy = :none }
|
|
164
163
|
easy = Curl.get(TestServlet.url)
|
|
165
164
|
|
|
166
165
|
assert_equal 200, easy.response_code
|
|
@@ -184,6 +183,7 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
184
183
|
with_file_redirect_server do |url|
|
|
185
184
|
easy = Curl::Easy.new(url)
|
|
186
185
|
easy.follow_location = true
|
|
186
|
+
stub_public_network_policy_for_local_fixture(easy)
|
|
187
187
|
easy.safe_http!
|
|
188
188
|
|
|
189
189
|
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
@@ -198,6 +198,7 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
198
198
|
|
|
199
199
|
easy = Curl::Easy.new(url)
|
|
200
200
|
easy.follow_location = true
|
|
201
|
+
stub_public_network_policy_for_local_fixture(easy)
|
|
201
202
|
|
|
202
203
|
assert_raises Curl::Err::UnsupportedProtocolError do
|
|
203
204
|
easy.perform
|
|
@@ -211,6 +212,7 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
211
212
|
easy.follow_location = true
|
|
212
213
|
easy.ssl_verify_peer = false
|
|
213
214
|
easy.ssl_verify_host = false
|
|
215
|
+
stub_public_network_policy_for_local_fixture(easy)
|
|
214
216
|
easy.safe_http!
|
|
215
217
|
|
|
216
218
|
easy.perform
|
|
@@ -226,6 +228,7 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
226
228
|
easy.follow_location = true
|
|
227
229
|
easy.ssl_verify_peer = false
|
|
228
230
|
easy.ssl_verify_host = false
|
|
231
|
+
stub_public_network_policy_for_local_fixture(easy)
|
|
229
232
|
easy.safe_http!
|
|
230
233
|
|
|
231
234
|
easy.perform
|
|
@@ -240,6 +243,7 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
240
243
|
easy = Curl::Easy.new(url)
|
|
241
244
|
easy.follow_location = true
|
|
242
245
|
easy.max_redirects = 2
|
|
246
|
+
stub_public_network_policy_for_local_fixture(easy)
|
|
243
247
|
easy.safe_http!
|
|
244
248
|
|
|
245
249
|
assert_raises Curl::Err::TooManyRedirectsError do
|
|
@@ -252,6 +256,14 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
|
|
|
252
256
|
|
|
253
257
|
private
|
|
254
258
|
|
|
259
|
+
# Isolate protocol/redirect behavior using local fixtures. Destination blocking
|
|
260
|
+
# is exercised without this stub in tc_curl_network_policy.rb.
|
|
261
|
+
def stub_public_network_policy_for_local_fixture(easy)
|
|
262
|
+
def easy.network_policy=(policy)
|
|
263
|
+
raise "unexpected policy: #{policy}" unless policy == :public
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
255
267
|
def with_file_redirect_server
|
|
256
268
|
port_socket = TCPServer.new('127.0.0.1', 0)
|
|
257
269
|
port = port_socket.addr[1]
|