curb 0.8.4 → 1.3.5
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.md +522 -0
- data/Rakefile +79 -26
- data/ext/banned.h +32 -0
- data/ext/curb.c +549 -230
- data/ext/curb.h +19 -10
- data/ext/curb_easy.c +1935 -366
- data/ext/curb_easy.h +16 -0
- data/ext/curb_errors.c +115 -18
- data/ext/curb_errors.h +8 -5
- data/ext/curb_macros.h +33 -21
- data/ext/curb_multi.c +1858 -272
- data/ext/curb_multi.h +10 -3
- data/ext/curb_postfield.c +149 -77
- data/ext/curb_postfield.h +1 -0
- data/ext/curb_upload.c +38 -11
- data/ext/curb_upload.h +2 -0
- data/ext/extconf.rb +353 -35
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +314 -78
- data/lib/curl/multi.rb +134 -28
- data/lib/curl.rb +217 -11
- data/tests/bug_crash_on_debug.rb +14 -28
- data/tests/bug_crash_on_progress.rb +32 -16
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +10 -15
- data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +6 -30
- data/tests/bug_follow_redirect_288.rb +83 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +3 -5
- data/tests/bug_issue102.rb +1 -1
- data/tests/bug_issue_noproxy.rb +56 -0
- data/tests/bug_issue_post_redirect.rb +93 -0
- data/tests/bug_issue_spnego.rb +41 -0
- data/tests/bug_multi_segfault.rb +1 -0
- data/tests/bug_raise_on_callback.rb +30 -0
- data/tests/helper.rb +400 -44
- data/tests/leak_trace.rb +237 -0
- data/tests/mem_check.rb +3 -0
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +12 -7
- data/tests/tc_curl_easy.rb +911 -63
- data/tests/tc_curl_easy_cookielist.rb +277 -0
- data/tests/tc_curl_easy_request_target.rb +41 -0
- data/tests/tc_curl_easy_resolve.rb +48 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +855 -53
- data/tests/tc_curl_native_coverage.rb +145 -0
- data/tests/tc_curl_postfield.rb +207 -30
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/tc_fiber_scheduler.rb +543 -0
- data/tests/tc_ftp_options.rb +39 -0
- data/tests/tc_gc_compact.rb +223 -0
- data/tests/tc_test_server_methods.rb +110 -0
- data/tests/timeout.rb +30 -6
- metadata +59 -36
- data/README +0 -194
data/tests/tc_curl_easy.rb
CHANGED
|
@@ -4,6 +4,449 @@ class FooNoToS
|
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
class TestCurbCurlEasy < Test::Unit::TestCase
|
|
7
|
+
def test_global_reset
|
|
8
|
+
Curl.get($TEST_URL)
|
|
9
|
+
# in a Timeout block you should reset the thread current handle
|
|
10
|
+
Curl.reset
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_nested_easy_methods
|
|
14
|
+
easy = Curl.get(TestServlet.url) {|http|
|
|
15
|
+
res = Curl.get(TestServlet.url + '/not_here')
|
|
16
|
+
assert_equal 404, res.code
|
|
17
|
+
}
|
|
18
|
+
assert_equal 200, easy.code
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_curlopt_resolve
|
|
22
|
+
require 'resolv'
|
|
23
|
+
uri = URI.parse(TestServlet.url)
|
|
24
|
+
resolved_ip = Resolv.getaddress(uri.host) # perform DNS lookup once
|
|
25
|
+
mapping = "#{uri.host}:#{uri.port}:#{resolved_ip}"
|
|
26
|
+
|
|
27
|
+
http = Curl::Easy.new(TestServlet.url)
|
|
28
|
+
http.setopt(Curl::CURLOPT_RESOLVE, [mapping])
|
|
29
|
+
|
|
30
|
+
http.get
|
|
31
|
+
|
|
32
|
+
assert_match(/GET/, http.body)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_resolve_clear_keeps_handle_usable
|
|
36
|
+
host = "curb.invalid"
|
|
37
|
+
url = "http://#{host}:#{TestServlet.port}#{TestServlet.path}"
|
|
38
|
+
mapping = "#{host}:#{TestServlet.port}:127.0.0.1"
|
|
39
|
+
|
|
40
|
+
http = Curl::Easy.new(url)
|
|
41
|
+
http.dns_cache_timeout = 0
|
|
42
|
+
http.resolve = [mapping]
|
|
43
|
+
http.get
|
|
44
|
+
assert_match(/GET/, http.body)
|
|
45
|
+
|
|
46
|
+
http.resolve = nil
|
|
47
|
+
http.url = TestServlet.url
|
|
48
|
+
http.get
|
|
49
|
+
assert_match(/GET/, http.body)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_curlopt_stderr_with_file
|
|
53
|
+
# does not work with Tempfile directly
|
|
54
|
+
path = Tempfile.new('curb_test_curlopt_stderr').path
|
|
55
|
+
File.open(path, 'w') do |file|
|
|
56
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
57
|
+
easy.verbose = true
|
|
58
|
+
easy.setopt(Curl::CURLOPT_STDERR, file)
|
|
59
|
+
easy.perform
|
|
60
|
+
end
|
|
61
|
+
output = File.read(path)
|
|
62
|
+
|
|
63
|
+
assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
|
|
64
|
+
assert_match('Host: 127.0.0.1:9129', output)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_curlopt_stderr_with_io
|
|
68
|
+
tmp = Tempfile.new('curb_test_curlopt_stderr')
|
|
69
|
+
path = tmp.path
|
|
70
|
+
fd = IO.sysopen(path, 'w')
|
|
71
|
+
io = IO.new(fd, 'w')
|
|
72
|
+
io.sync = true
|
|
73
|
+
|
|
74
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
75
|
+
easy.verbose = true
|
|
76
|
+
easy.setopt(Curl::CURLOPT_STDERR, io)
|
|
77
|
+
easy.perform
|
|
78
|
+
|
|
79
|
+
io.flush
|
|
80
|
+
io.close
|
|
81
|
+
output = File.read(path)
|
|
82
|
+
|
|
83
|
+
assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
|
|
84
|
+
assert_match('Host: 127.0.0.1:9129', output)
|
|
85
|
+
ensure
|
|
86
|
+
tmp.close! if defined?(tmp) && tmp
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_curlopt_stderr_gc_safe
|
|
90
|
+
tmp = Tempfile.new('curb_test_curlopt_stderr_gc')
|
|
91
|
+
path = tmp.path
|
|
92
|
+
fd = IO.sysopen(path, 'w')
|
|
93
|
+
io = IO.new(fd, 'w')
|
|
94
|
+
|
|
95
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
96
|
+
easy.verbose = true
|
|
97
|
+
easy.setopt(Curl::CURLOPT_STDERR, io)
|
|
98
|
+
|
|
99
|
+
# Drop our Ruby reference and force GC; curb should retain it internally
|
|
100
|
+
io = nil
|
|
101
|
+
GC.start
|
|
102
|
+
|
|
103
|
+
easy.perform
|
|
104
|
+
|
|
105
|
+
output = File.read(path)
|
|
106
|
+
assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
|
|
107
|
+
assert_match('Host: 127.0.0.1:9129', output)
|
|
108
|
+
ensure
|
|
109
|
+
tmp.close! if defined?(tmp) && tmp
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_head_request_no_body_and_no_timeout
|
|
113
|
+
# Ensure a HEAD request completes and does not attempt to read a body
|
|
114
|
+
# even when the server advertises a Content-Length.
|
|
115
|
+
easy = nil
|
|
116
|
+
assert_nothing_raised do
|
|
117
|
+
easy = Curl.http(:HEAD, TestServlet.url)
|
|
118
|
+
end
|
|
119
|
+
assert_not_nil easy
|
|
120
|
+
# Header string should contain the HTTP status line.
|
|
121
|
+
assert_match(/HTTP\/1\.1\s\d+\s/, easy.header_str.to_s)
|
|
122
|
+
# Body should be empty for HEAD requests (libcurl won't call the write callback).
|
|
123
|
+
assert_equal "", easy.body_str.to_s
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_head_request_restores_easy_state_after_callback_exception
|
|
127
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
128
|
+
easy.on_complete { raise "head complete blew up" }
|
|
129
|
+
|
|
130
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { easy.http("HEAD") }
|
|
131
|
+
assert_equal "head complete blew up", error.message
|
|
132
|
+
|
|
133
|
+
easy.on_complete { |_curl| }
|
|
134
|
+
easy.perform
|
|
135
|
+
|
|
136
|
+
assert_equal "GET", easy.body_str
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_patch_request_does_not_leak_custom_method_to_next_perform
|
|
140
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
141
|
+
|
|
142
|
+
easy.http_patch("a=b")
|
|
143
|
+
assert_equal "PATCH\na=b", easy.body_str
|
|
144
|
+
|
|
145
|
+
easy.perform
|
|
146
|
+
assert_equal "GET", easy.body_str
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_put_request_does_not_leak_custom_method_to_next_perform
|
|
150
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
151
|
+
|
|
152
|
+
easy.http_put("payload")
|
|
153
|
+
assert_equal "PUT\npayload", easy.body_str
|
|
154
|
+
|
|
155
|
+
easy.perform
|
|
156
|
+
assert_equal "GET", easy.body_str
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_failed_put_data_setup_does_not_leave_easy_in_upload_mode
|
|
160
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
161
|
+
easy.headers = ["X-Test: true"]
|
|
162
|
+
|
|
163
|
+
assert_raise(RuntimeError) { easy.put_data = "payload" }
|
|
164
|
+
|
|
165
|
+
easy.headers = {}
|
|
166
|
+
easy.perform
|
|
167
|
+
assert_equal "GET", easy.body_str
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_perform_releases_failed_implicit_multi_without_follow_up_easy_perform
|
|
171
|
+
Curl::Easy.flush_deferred_multi_closes
|
|
172
|
+
assert_equal 0, Curl::Easy.deferred_multi_closes.length
|
|
173
|
+
|
|
174
|
+
easy, implicit_multi = capture_failed_implicit_multi_cleanup_state
|
|
175
|
+
|
|
176
|
+
assert_nil easy.multi
|
|
177
|
+
assert_equal({}, implicit_multi.requests)
|
|
178
|
+
assert_equal false, implicit_multi.instance_variable_get(:@deferred_close)
|
|
179
|
+
assert_equal 0, Curl::Easy.deferred_multi_closes.length
|
|
180
|
+
ensure
|
|
181
|
+
easy.multi = nil if defined?(easy) && easy
|
|
182
|
+
implicit_multi.close if defined?(implicit_multi) && implicit_multi
|
|
183
|
+
Curl::Easy.flush_deferred_multi_closes
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def test_deferred_multi_closes_stay_with_their_owner_thread_across_retry
|
|
187
|
+
Curl::Easy.flush_deferred_multi_closes(all_threads: true)
|
|
188
|
+
multi = deferred_close_stub_multi(failures: 1)
|
|
189
|
+
|
|
190
|
+
Curl::Easy.defer_multi_close(multi, nil)
|
|
191
|
+
|
|
192
|
+
Curl::Easy.flush_deferred_multi_closes
|
|
193
|
+
assert_equal 1, multi.close_attempts
|
|
194
|
+
assert_equal true, multi.instance_variable_get(:@deferred_close)
|
|
195
|
+
assert_equal 1, Curl::Easy.deferred_multi_closes.length
|
|
196
|
+
|
|
197
|
+
Thread.new { Curl::Easy.flush_deferred_multi_closes }.join
|
|
198
|
+
assert_equal 1, multi.close_attempts
|
|
199
|
+
assert_equal true, multi.instance_variable_get(:@deferred_close)
|
|
200
|
+
assert_equal 1, Curl::Easy.deferred_multi_closes.length
|
|
201
|
+
|
|
202
|
+
Curl::Easy.flush_deferred_multi_closes
|
|
203
|
+
assert_equal 2, multi.close_attempts
|
|
204
|
+
assert_equal true, multi.closed?
|
|
205
|
+
assert_equal false, multi.instance_variable_get(:@deferred_close)
|
|
206
|
+
assert_equal 0, Curl::Easy.deferred_multi_closes.length
|
|
207
|
+
ensure
|
|
208
|
+
Curl::Easy.flush_deferred_multi_closes(all_threads: true)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def capture_created_multis
|
|
212
|
+
created_multis = []
|
|
213
|
+
multi_singleton = class << Curl::Multi; self; end
|
|
214
|
+
|
|
215
|
+
multi_singleton.__send__(:alias_method, :__curb_test_original_new, :new)
|
|
216
|
+
multi_singleton.__send__(:define_method, :new) do |*args, &block|
|
|
217
|
+
multi = __curb_test_original_new(*args, &block)
|
|
218
|
+
created_multis << multi
|
|
219
|
+
multi
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
yield created_multis
|
|
223
|
+
ensure
|
|
224
|
+
if defined?(multi_singleton) && multi_singleton.method_defined?(:__curb_test_original_new)
|
|
225
|
+
multi_singleton.__send__(:remove_method, :new)
|
|
226
|
+
multi_singleton.__send__(:alias_method, :new, :__curb_test_original_new)
|
|
227
|
+
multi_singleton.__send__(:remove_method, :__curb_test_original_new)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def capture_failed_implicit_multi_cleanup_state
|
|
232
|
+
created_multis = nil
|
|
233
|
+
easy = nil
|
|
234
|
+
|
|
235
|
+
capture_created_multis do |captured_multis|
|
|
236
|
+
created_multis = captured_multis
|
|
237
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
238
|
+
easy.on_complete { raise "complete blew up" }
|
|
239
|
+
|
|
240
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { easy.perform }
|
|
241
|
+
assert_equal "complete blew up", error.message
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
assert_equal 1, created_multis.length, "test should observe one implicit multi created by Easy#perform"
|
|
245
|
+
|
|
246
|
+
[easy, created_multis.first]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def deferred_close_stub_multi(failures:)
|
|
250
|
+
Object.new.tap do |multi|
|
|
251
|
+
requests = {}
|
|
252
|
+
remaining_failures = failures
|
|
253
|
+
close_attempts = 0
|
|
254
|
+
closed = false
|
|
255
|
+
|
|
256
|
+
multi.define_singleton_method(:requests) { requests }
|
|
257
|
+
multi.define_singleton_method(:_close) do
|
|
258
|
+
close_attempts += 1
|
|
259
|
+
if remaining_failures.positive?
|
|
260
|
+
remaining_failures -= 1
|
|
261
|
+
raise "close failed"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
closed = true
|
|
265
|
+
end
|
|
266
|
+
multi.define_singleton_method(:close_attempts) { close_attempts }
|
|
267
|
+
multi.define_singleton_method(:closed?) { closed }
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def test_perform_restores_explicit_multi_after_callback_error
|
|
272
|
+
multi = Curl::Multi.new
|
|
273
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
274
|
+
easy.multi = multi
|
|
275
|
+
easy.on_complete { raise "complete blew up" }
|
|
276
|
+
|
|
277
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { easy.perform }
|
|
278
|
+
assert_equal "complete blew up", error.message
|
|
279
|
+
assert_same multi, easy.multi
|
|
280
|
+
assert_equal({}, multi.requests)
|
|
281
|
+
|
|
282
|
+
easy.on_complete { |_curl| }
|
|
283
|
+
easy.perform
|
|
284
|
+
|
|
285
|
+
assert_same multi, easy.multi
|
|
286
|
+
assert_equal "GET", easy.body_str
|
|
287
|
+
ensure
|
|
288
|
+
easy.multi = nil if defined?(easy) && easy && easy.multi.equal?(multi)
|
|
289
|
+
multi.close if defined?(multi) && multi
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def test_perform_restores_cached_implicit_multi_after_callback_error
|
|
293
|
+
Curl::Multi.autoclose = false
|
|
294
|
+
|
|
295
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
296
|
+
easy.perform
|
|
297
|
+
|
|
298
|
+
cached_multi = easy.multi
|
|
299
|
+
easy.on_complete { raise "complete blew up" }
|
|
300
|
+
|
|
301
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { easy.perform }
|
|
302
|
+
assert_equal "complete blew up", error.message
|
|
303
|
+
assert_same cached_multi, easy.multi
|
|
304
|
+
assert_equal({}, cached_multi.requests)
|
|
305
|
+
|
|
306
|
+
easy.on_complete { |_curl| }
|
|
307
|
+
easy.perform
|
|
308
|
+
|
|
309
|
+
assert_same cached_multi, easy.multi
|
|
310
|
+
assert_equal "GET", easy.body_str
|
|
311
|
+
ensure
|
|
312
|
+
easy.multi = nil if defined?(easy) && easy
|
|
313
|
+
cached_multi.close if defined?(cached_multi) && cached_multi
|
|
314
|
+
Curl::Multi.autoclose = false
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def test_close_clears_explicit_idle_easy_multi_reference
|
|
318
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
319
|
+
multi = Curl::Multi.new
|
|
320
|
+
easy.multi = multi
|
|
321
|
+
|
|
322
|
+
assert_same multi, easy.multi
|
|
323
|
+
assert_equal({}, multi.requests)
|
|
324
|
+
|
|
325
|
+
multi.close
|
|
326
|
+
|
|
327
|
+
assert_nil easy.multi
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def test_close_clears_cached_idle_easy_multi_reference
|
|
331
|
+
Curl::Multi.autoclose = false
|
|
332
|
+
|
|
333
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
334
|
+
easy.perform
|
|
335
|
+
|
|
336
|
+
cached_multi = easy.multi
|
|
337
|
+
assert_not_nil cached_multi
|
|
338
|
+
assert_equal({}, cached_multi.requests)
|
|
339
|
+
|
|
340
|
+
cached_multi.close
|
|
341
|
+
|
|
342
|
+
assert_nil easy.multi
|
|
343
|
+
ensure
|
|
344
|
+
easy.multi = nil if defined?(easy) && easy
|
|
345
|
+
Curl::Multi.autoclose = false
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def test_multi_assignment_rejects_non_multi_objects
|
|
349
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
350
|
+
|
|
351
|
+
error = assert_raise(TypeError) { easy.multi = Object.new }
|
|
352
|
+
|
|
353
|
+
assert_match(/Curl::Multi/, error.message)
|
|
354
|
+
assert_nil easy.multi
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def test_perform_can_reuse_explicit_multi_when_autoclose_is_enabled
|
|
358
|
+
Curl::Multi.autoclose = true
|
|
359
|
+
|
|
360
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
361
|
+
multi = Curl::Multi.new
|
|
362
|
+
|
|
363
|
+
easy.multi = multi
|
|
364
|
+
easy.perform
|
|
365
|
+
|
|
366
|
+
assert_nil easy.multi
|
|
367
|
+
assert_equal({}, multi.requests)
|
|
368
|
+
|
|
369
|
+
easy.multi = multi
|
|
370
|
+
easy.perform
|
|
371
|
+
|
|
372
|
+
assert_nil easy.multi
|
|
373
|
+
assert_equal({}, multi.requests)
|
|
374
|
+
assert_equal "GET", easy.body_str
|
|
375
|
+
ensure
|
|
376
|
+
easy.multi = nil if defined?(easy) && easy
|
|
377
|
+
multi.close if defined?(multi) && multi
|
|
378
|
+
Curl::Multi.autoclose = false
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def test_perform_reraises_on_body_exception_for_frozen_easy
|
|
382
|
+
easy = Curl::Easy.new($TEST_URL)
|
|
383
|
+
callback_error = Class.new(RuntimeError)
|
|
384
|
+
easy.on_body { raise callback_error, "body blew up" }
|
|
385
|
+
easy.freeze
|
|
386
|
+
|
|
387
|
+
error = assert_raise(callback_error) { easy.perform }
|
|
388
|
+
|
|
389
|
+
assert_equal "body blew up", error.message
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def test_perform_preserves_implicit_multi_when_autoclose_is_disabled
|
|
393
|
+
Curl::Multi.autoclose = false
|
|
394
|
+
|
|
395
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
396
|
+
easy.perform
|
|
397
|
+
|
|
398
|
+
first_multi = easy.multi
|
|
399
|
+
assert_not_nil first_multi
|
|
400
|
+
assert_equal({}, first_multi.requests)
|
|
401
|
+
|
|
402
|
+
easy.perform
|
|
403
|
+
|
|
404
|
+
assert_same first_multi, easy.multi
|
|
405
|
+
assert_equal "GET", easy.body_str
|
|
406
|
+
ensure
|
|
407
|
+
easy.multi = nil if defined?(easy) && easy
|
|
408
|
+
first_multi.close if defined?(first_multi) && first_multi
|
|
409
|
+
Curl::Multi.autoclose = false
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def test_curlopt_stderr_fails_with_tempdir
|
|
413
|
+
Tempfile.open('curb_test_curlopt_stderr') do |tempfile|
|
|
414
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
415
|
+
|
|
416
|
+
assert_raise(TypeError) do
|
|
417
|
+
easy.setopt(Curl::CURLOPT_STDERR, tempfile)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def test_curlopt_stderr_fails_with_stringio
|
|
423
|
+
stringio = StringIO.new
|
|
424
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
425
|
+
|
|
426
|
+
assert_raise(TypeError) do
|
|
427
|
+
easy.setopt(Curl::CURLOPT_STDERR, stringio)
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def test_curlopt_stderr_fails_with_string
|
|
432
|
+
string = String.new
|
|
433
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
434
|
+
|
|
435
|
+
assert_raise(TypeError) do
|
|
436
|
+
easy.setopt(Curl::CURLOPT_STDERR, string)
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def test_exception
|
|
441
|
+
begin
|
|
442
|
+
Curl.get('NOT_FOUND_URL')
|
|
443
|
+
rescue
|
|
444
|
+
assert true
|
|
445
|
+
rescue Exception
|
|
446
|
+
assert false, "We should raise StandardError"
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
7
450
|
def test_threads
|
|
8
451
|
t = []
|
|
9
452
|
5.times do
|
|
@@ -11,31 +454,30 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
11
454
|
5.times do
|
|
12
455
|
c = Curl.get($TEST_URL)
|
|
13
456
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
|
|
14
|
-
|
|
457
|
+
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body)
|
|
15
458
|
end
|
|
16
459
|
end
|
|
17
460
|
end
|
|
18
461
|
|
|
19
|
-
t.each {|
|
|
462
|
+
t.each {|x| x.join }
|
|
20
463
|
end
|
|
21
464
|
|
|
22
465
|
def test_class_perform_01
|
|
23
466
|
assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL)
|
|
24
467
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
|
|
25
|
-
|
|
468
|
+
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body)
|
|
26
469
|
end
|
|
27
470
|
|
|
28
471
|
def test_class_perform_02
|
|
29
|
-
data =
|
|
472
|
+
data = String.new
|
|
30
473
|
assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL) { |curl| curl.on_body { |d| data << d; d.length } }
|
|
31
474
|
|
|
32
475
|
assert_nil c.body_str
|
|
33
|
-
assert_equal "", c.header_str
|
|
34
476
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, data)
|
|
35
477
|
end
|
|
36
478
|
|
|
37
479
|
def test_class_perform_03
|
|
38
|
-
assert_raise(Curl::Err::CouldntReadError) {
|
|
480
|
+
assert_raise(Curl::Err::CouldntReadError) { Curl::Easy.perform($TEST_URL + "nonexistent") }
|
|
39
481
|
end
|
|
40
482
|
|
|
41
483
|
def test_new_01
|
|
@@ -125,11 +567,10 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
125
567
|
c = Curl::Easy.new($TEST_URL)
|
|
126
568
|
assert_equal true, c.http_get
|
|
127
569
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body_str)
|
|
128
|
-
assert_equal "", c.header_str
|
|
129
570
|
end
|
|
130
571
|
|
|
131
572
|
def test_get_02
|
|
132
|
-
data =
|
|
573
|
+
data = String.new
|
|
133
574
|
c = Curl::Easy.new($TEST_URL) do |curl|
|
|
134
575
|
curl.on_body { |d| data << d; d.length }
|
|
135
576
|
end
|
|
@@ -137,7 +578,6 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
137
578
|
assert_equal true, c.http_get
|
|
138
579
|
|
|
139
580
|
assert_nil c.body_str
|
|
140
|
-
assert_equal "", c.header_str
|
|
141
581
|
assert_match(/^# DO NOT REMOVE THIS COMMENT/, data)
|
|
142
582
|
end
|
|
143
583
|
|
|
@@ -150,6 +590,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
150
590
|
|
|
151
591
|
|
|
152
592
|
def test_last_effective_url_01
|
|
593
|
+
omit('Windows file URL semantics differ') if WINDOWS
|
|
153
594
|
c = Curl::Easy.new($TEST_URL)
|
|
154
595
|
|
|
155
596
|
assert_equal $TEST_URL, c.url
|
|
@@ -320,19 +761,71 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
320
761
|
c.max_redirects = nil
|
|
321
762
|
assert_nil c.max_redirects
|
|
322
763
|
end
|
|
323
|
-
|
|
764
|
+
|
|
765
|
+
def test_timeout_with_floats
|
|
766
|
+
c = Curl::Easy.new($TEST_URL)
|
|
767
|
+
|
|
768
|
+
c.timeout = 1.5
|
|
769
|
+
assert_equal 1500, c.timeout_ms
|
|
770
|
+
assert_equal 1.5, c.timeout
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
def test_timeout_with_negative
|
|
774
|
+
c = Curl::Easy.new($TEST_URL)
|
|
775
|
+
|
|
776
|
+
c.timeout = -1.5
|
|
777
|
+
assert_equal 0, c.timeout
|
|
778
|
+
assert_equal 0, c.timeout_ms
|
|
779
|
+
|
|
780
|
+
c.timeout = -4.8
|
|
781
|
+
assert_equal 0, c.timeout
|
|
782
|
+
assert_equal 0, c.timeout_ms
|
|
783
|
+
end
|
|
784
|
+
|
|
324
785
|
def test_timeout_01
|
|
325
786
|
c = Curl::Easy.new($TEST_URL)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
787
|
+
|
|
788
|
+
assert_equal 0, c.timeout
|
|
789
|
+
|
|
329
790
|
c.timeout = 3
|
|
330
791
|
assert_equal 3, c.timeout
|
|
331
|
-
|
|
332
|
-
c.timeout =
|
|
333
|
-
|
|
792
|
+
|
|
793
|
+
c.timeout = 0
|
|
794
|
+
assert_equal 0, c.timeout
|
|
334
795
|
end
|
|
335
|
-
|
|
796
|
+
|
|
797
|
+
def test_timeout_ms_01
|
|
798
|
+
c = Curl::Easy.new($TEST_URL)
|
|
799
|
+
|
|
800
|
+
assert_equal 0, c.timeout_ms
|
|
801
|
+
|
|
802
|
+
c.timeout_ms = 100
|
|
803
|
+
assert_equal 100, c.timeout_ms
|
|
804
|
+
|
|
805
|
+
c.timeout_ms = nil
|
|
806
|
+
assert_equal 0, c.timeout_ms
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
def test_timeout_ms_with_floats
|
|
810
|
+
c = Curl::Easy.new($TEST_URL)
|
|
811
|
+
|
|
812
|
+
c.timeout_ms = 55.5
|
|
813
|
+
assert_equal 55, c.timeout_ms
|
|
814
|
+
assert_equal 0.055, c.timeout
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def test_timeout_ms_with_negative
|
|
818
|
+
c = Curl::Easy.new($TEST_URL)
|
|
819
|
+
|
|
820
|
+
c.timeout_ms = -1.5
|
|
821
|
+
assert_equal 0, c.timeout
|
|
822
|
+
assert_equal 0, c.timeout_ms
|
|
823
|
+
|
|
824
|
+
c.timeout_ms = -4.8
|
|
825
|
+
assert_equal 0, c.timeout
|
|
826
|
+
assert_equal 0, c.timeout_ms
|
|
827
|
+
end
|
|
828
|
+
|
|
336
829
|
def test_connect_timeout_01
|
|
337
830
|
c = Curl::Easy.new($TEST_URL)
|
|
338
831
|
|
|
@@ -344,7 +837,19 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
344
837
|
c.connect_timeout = nil
|
|
345
838
|
assert_nil c.connect_timeout
|
|
346
839
|
end
|
|
347
|
-
|
|
840
|
+
|
|
841
|
+
def test_connect_timeout_ms_01
|
|
842
|
+
c = Curl::Easy.new($TEST_URL)
|
|
843
|
+
|
|
844
|
+
assert_nil c.connect_timeout_ms
|
|
845
|
+
|
|
846
|
+
c.connect_timeout_ms = 100
|
|
847
|
+
assert_equal 100, c.connect_timeout_ms
|
|
848
|
+
|
|
849
|
+
c.connect_timeout_ms = nil
|
|
850
|
+
assert_nil c.connect_timeout_ms
|
|
851
|
+
end
|
|
852
|
+
|
|
348
853
|
def test_ftp_response_timeout_01
|
|
349
854
|
c = Curl::Easy.new($TEST_URL)
|
|
350
855
|
|
|
@@ -534,6 +1039,27 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
534
1039
|
assert_raises(ArgumentError) { c.resolve_mode = :bad }
|
|
535
1040
|
end
|
|
536
1041
|
|
|
1042
|
+
def test_http_version_accessors
|
|
1043
|
+
c = Curl::Easy.new
|
|
1044
|
+
assert_equal Curl::HTTP_NONE, c.http_version
|
|
1045
|
+
|
|
1046
|
+
c.http_version = Curl::HTTP_1_1
|
|
1047
|
+
assert_equal Curl::HTTP_1_1, c.http_version
|
|
1048
|
+
|
|
1049
|
+
if Curl.const_defined?(:HTTP_2TLS)
|
|
1050
|
+
c.http_version = Curl::HTTP_2TLS
|
|
1051
|
+
assert_equal Curl::HTTP_2TLS, c.http_version
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
if Curl.const_defined?(:HTTP_2_PRIOR_KNOWLEDGE)
|
|
1055
|
+
c.http_version = Curl::HTTP_2_PRIOR_KNOWLEDGE
|
|
1056
|
+
assert_equal Curl::HTTP_2_PRIOR_KNOWLEDGE, c.http_version
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
c.http_version = nil
|
|
1060
|
+
assert_equal Curl::HTTP_NONE, c.http_version
|
|
1061
|
+
end
|
|
1062
|
+
|
|
537
1063
|
def test_enable_cookies
|
|
538
1064
|
c = Curl::Easy.new
|
|
539
1065
|
assert !c.enable_cookies?
|
|
@@ -562,14 +1088,23 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
562
1088
|
assert_equal "some.file", c.cookiejar
|
|
563
1089
|
end
|
|
564
1090
|
|
|
1091
|
+
def test_cookielist
|
|
1092
|
+
c = Curl::Easy.new TestServlet.url
|
|
1093
|
+
c.enable_cookies = true
|
|
1094
|
+
c.post_body = URI.encode_www_form('c' => 'somename=somevalue')
|
|
1095
|
+
assert_nil c.cookielist
|
|
1096
|
+
c.perform
|
|
1097
|
+
assert_match(/somevalue/, c.cookielist.join(''))
|
|
1098
|
+
end
|
|
1099
|
+
|
|
565
1100
|
def test_on_success
|
|
566
1101
|
curl = Curl::Easy.new($TEST_URL)
|
|
567
1102
|
on_success_called = false
|
|
568
1103
|
curl.on_success {|c|
|
|
569
1104
|
on_success_called = true
|
|
570
|
-
assert_not_nil c.
|
|
571
|
-
|
|
572
|
-
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.
|
|
1105
|
+
assert_not_nil c.body
|
|
1106
|
+
assert_match(/Content-Length: /, c.head)
|
|
1107
|
+
assert_match(/^# DO NOT REMOVE THIS COMMENT/, c.body)
|
|
573
1108
|
}
|
|
574
1109
|
curl.perform
|
|
575
1110
|
assert on_success_called, "Success handler not called"
|
|
@@ -611,20 +1146,27 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
611
1146
|
assert_equal 'GET', curl.body_str
|
|
612
1147
|
end
|
|
613
1148
|
|
|
614
|
-
def
|
|
1149
|
+
def test_remote
|
|
615
1150
|
curl = Curl::Easy.new(TestServlet.url)
|
|
616
1151
|
curl.http_post([Curl::PostField.content('document_id', 5)])
|
|
617
1152
|
assert_equal "POST\ndocument_id=5", curl.unescape(curl.body_str)
|
|
1153
|
+
|
|
1154
|
+
curl.http_put([Curl::PostField.content('document_id', 5)])
|
|
1155
|
+
assert_equal "PUT\ndocument_id=5", curl.unescape(curl.body_str)
|
|
1156
|
+
|
|
1157
|
+
curl.http_patch([Curl::PostField.content('document_id', 5)])
|
|
1158
|
+
assert_equal "PATCH\ndocument_id=5", curl.unescape(curl.body_str)
|
|
618
1159
|
end
|
|
619
1160
|
|
|
620
|
-
def
|
|
1161
|
+
def test_remote_is_easy_handle
|
|
621
1162
|
# see: http://pastie.org/560852 and
|
|
622
1163
|
# http://groups.google.com/group/curb---ruby-libcurl-bindings/browse_thread/thread/216bb2d9b037f347?hl=en
|
|
623
|
-
[:post, :get, :head, :delete].each do |method|
|
|
1164
|
+
[:post, :patch, :put, :get, :head, :delete].each do |method|
|
|
624
1165
|
retries = 0
|
|
625
1166
|
begin
|
|
626
1167
|
count = 0
|
|
627
|
-
|
|
1168
|
+
m = "http_#{method}".to_sym
|
|
1169
|
+
Curl::Easy.send(m, TestServlet.url) do|c|
|
|
628
1170
|
count += 1
|
|
629
1171
|
assert_equal Curl::Easy, c.class
|
|
630
1172
|
end
|
|
@@ -633,24 +1175,40 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
633
1175
|
retries+=1
|
|
634
1176
|
retry if retries < 3
|
|
635
1177
|
raise e
|
|
1178
|
+
rescue ArgumentError => e
|
|
1179
|
+
assert false, "#{m} - #{e.message}"
|
|
636
1180
|
end
|
|
637
1181
|
end
|
|
638
1182
|
end
|
|
639
1183
|
|
|
640
1184
|
# see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445
|
|
641
|
-
def
|
|
1185
|
+
def test_multipart_array_remote
|
|
642
1186
|
curl = Curl::Easy.new(TestServlet.url)
|
|
643
1187
|
curl.multipart_form_post = true
|
|
644
1188
|
fields = [
|
|
645
|
-
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README'))),
|
|
646
|
-
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README')))
|
|
1189
|
+
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))),
|
|
1190
|
+
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
|
647
1191
|
]
|
|
648
1192
|
curl.http_post(fields)
|
|
649
|
-
assert_match
|
|
650
|
-
assert_match
|
|
1193
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
|
1194
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
|
1195
|
+
|
|
1196
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1197
|
+
curl.multipart_form_post = true
|
|
1198
|
+
fields = [
|
|
1199
|
+
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))),
|
|
1200
|
+
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
|
1201
|
+
]
|
|
1202
|
+
curl.http_put(fields)
|
|
1203
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
|
1204
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
|
1205
|
+
|
|
1206
|
+
curl.http_patch(fields)
|
|
1207
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
|
1208
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
|
651
1209
|
end
|
|
652
1210
|
|
|
653
|
-
def
|
|
1211
|
+
def test_with_body_remote
|
|
654
1212
|
curl = Curl::Easy.new(TestServlet.url)
|
|
655
1213
|
curl.post_body = 'foo=bar&encoded%20string=val'
|
|
656
1214
|
|
|
@@ -658,23 +1216,151 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
658
1216
|
|
|
659
1217
|
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
|
660
1218
|
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
|
1219
|
+
|
|
1220
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1221
|
+
curl.put_data = 'foo=bar&encoded%20string=val'
|
|
1222
|
+
|
|
1223
|
+
curl.perform
|
|
1224
|
+
|
|
1225
|
+
assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
|
|
661
1226
|
end
|
|
662
|
-
|
|
663
|
-
def
|
|
1227
|
+
|
|
1228
|
+
def test_post_body_from_to_s_keeps_string_buffer_alive
|
|
1229
|
+
post_body = Object.new
|
|
1230
|
+
def post_body.to_s
|
|
1231
|
+
'foo=bar&encoded%20string=val'
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1235
|
+
curl.post_body = post_body
|
|
1236
|
+
|
|
1237
|
+
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
|
1238
|
+
|
|
1239
|
+
GC.start
|
|
1240
|
+
GC.compact if GC.respond_to?(:compact)
|
|
1241
|
+
|
|
1242
|
+
curl.perform
|
|
1243
|
+
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1246
|
+
def test_post_body_preserves_assigned_snapshot_when_original_string_is_mutated
|
|
1247
|
+
omit('CURLOPT_COPYPOSTFIELDS is not available in this build') unless Curl.const_defined?(:CURLOPT_COPYPOSTFIELDS)
|
|
1248
|
+
|
|
1249
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1250
|
+
post_body = String.new('alpha=1')
|
|
1251
|
+
curl.post_body = post_body
|
|
1252
|
+
|
|
1253
|
+
post_body.replace('beta=2')
|
|
1254
|
+
|
|
1255
|
+
assert_equal 'beta=2', post_body
|
|
1256
|
+
assert_equal 'alpha=1', curl.post_body
|
|
1257
|
+
|
|
1258
|
+
curl.perform
|
|
1259
|
+
|
|
1260
|
+
assert_equal "POST\nalpha=1", curl.body_str
|
|
1261
|
+
assert_equal 'alpha=1', curl.post_body
|
|
1262
|
+
end
|
|
1263
|
+
|
|
1264
|
+
def test_setopt_postfields_keeps_string_buffer_alive
|
|
1265
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1266
|
+
curl.set(Curl::CURLOPT_POSTFIELDS, 'foo=bar&encoded%20string=val')
|
|
1267
|
+
|
|
1268
|
+
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
|
1269
|
+
|
|
1270
|
+
GC.start
|
|
1271
|
+
GC.compact if GC.respond_to?(:compact)
|
|
1272
|
+
|
|
1273
|
+
curl.perform
|
|
1274
|
+
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
def test_setopt_postfields_preserves_assigned_snapshot_when_original_string_is_mutated
|
|
1278
|
+
omit('CURLOPT_COPYPOSTFIELDS is not available in this build') unless Curl.const_defined?(:CURLOPT_COPYPOSTFIELDS)
|
|
1279
|
+
|
|
1280
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1281
|
+
post_body = String.new('alpha=1')
|
|
1282
|
+
curl.set(Curl::CURLOPT_POSTFIELDS, post_body)
|
|
1283
|
+
|
|
1284
|
+
post_body.replace('beta=2')
|
|
1285
|
+
|
|
1286
|
+
assert_equal 'beta=2', post_body
|
|
1287
|
+
assert_equal 'alpha=1', curl.post_body
|
|
1288
|
+
|
|
1289
|
+
curl.perform
|
|
1290
|
+
|
|
1291
|
+
assert_equal "POST\nalpha=1", curl.body_str
|
|
1292
|
+
assert_equal 'alpha=1', curl.post_body
|
|
1293
|
+
end
|
|
1294
|
+
|
|
1295
|
+
def test_setopt_postfields_nil_preserves_post_request
|
|
1296
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1297
|
+
curl.set(Curl::CURLOPT_POST, true)
|
|
1298
|
+
curl.set(Curl::CURLOPT_POSTFIELDS, nil)
|
|
1299
|
+
|
|
1300
|
+
curl.perform
|
|
1301
|
+
|
|
1302
|
+
assert_nil curl.post_body
|
|
1303
|
+
assert_equal "POST\n", curl.body_str
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
def test_form_body_remote
|
|
664
1307
|
curl = Curl::Easy.new(TestServlet.url)
|
|
665
1308
|
curl.http_post('foo=bar', 'encoded%20string=val')
|
|
666
1309
|
|
|
667
1310
|
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
|
668
1311
|
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
|
1312
|
+
|
|
1313
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1314
|
+
curl.http_put('foo=bar', 'encoded%20string=val')
|
|
1315
|
+
|
|
1316
|
+
assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
|
|
1317
|
+
|
|
1318
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1319
|
+
curl.http_patch('foo=bar', 'encoded%20string=val')
|
|
1320
|
+
|
|
1321
|
+
assert_equal "PATCH\nfoo=bar&encoded%20string=val", curl.body_str
|
|
669
1322
|
end
|
|
670
1323
|
|
|
671
|
-
def
|
|
1324
|
+
def test_multipart_file_remote
|
|
1325
|
+
[:put, :post, :patch].each {|method|
|
|
1326
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1327
|
+
curl.multipart_form_post = true
|
|
1328
|
+
pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.md')))
|
|
1329
|
+
curl.send("http_#{method}", pf)
|
|
1330
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
|
1331
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
|
1332
|
+
}
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
def test_multipart_form_reuse_clears_previous_form
|
|
672
1336
|
curl = Curl::Easy.new(TestServlet.url)
|
|
673
1337
|
curl.multipart_form_post = true
|
|
674
|
-
|
|
675
|
-
curl.
|
|
676
|
-
|
|
677
|
-
|
|
1338
|
+
curl.http_post(Curl::PostField.content('document_id', '5'))
|
|
1339
|
+
assert_match(/document_id/, curl.body_str)
|
|
1340
|
+
|
|
1341
|
+
curl.multipart_form_post = false
|
|
1342
|
+
curl.post_body = 'foo=bar'
|
|
1343
|
+
curl.http_post
|
|
1344
|
+
|
|
1345
|
+
assert_equal "POST\nfoo=bar", curl.body_str
|
|
1346
|
+
end
|
|
1347
|
+
|
|
1348
|
+
def test_multipart_patch_build_failure_restores_easy_request_state
|
|
1349
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1350
|
+
curl.multipart_form_post = true
|
|
1351
|
+
field = Curl::PostField.content('document_id', '5')
|
|
1352
|
+
|
|
1353
|
+
field.name = nil
|
|
1354
|
+
error = assert_raise(Curl::Err::InvalidPostFieldError) do
|
|
1355
|
+
# The first field allocates native multipart form state before the second field raises.
|
|
1356
|
+
curl.http_patch(Curl::PostField.content('keep', 'allocated'), field)
|
|
1357
|
+
end
|
|
1358
|
+
assert_match(/Cannot post unnamed field/, error.message)
|
|
1359
|
+
|
|
1360
|
+
curl.multipart_form_post = false
|
|
1361
|
+
curl.perform
|
|
1362
|
+
|
|
1363
|
+
assert_equal 'GET', curl.body_str
|
|
678
1364
|
end
|
|
679
1365
|
|
|
680
1366
|
def test_delete_remote
|
|
@@ -696,7 +1382,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
696
1382
|
redirect = curl.header_str.match(/Location: (.*)/)
|
|
697
1383
|
|
|
698
1384
|
assert_equal '', curl.body_str
|
|
699
|
-
assert_match
|
|
1385
|
+
assert_match('/nonexistent', redirect[1])
|
|
700
1386
|
end
|
|
701
1387
|
|
|
702
1388
|
def test_head_accessor
|
|
@@ -707,7 +1393,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
707
1393
|
redirect = curl.header_str.match(/Location: (.*)/)
|
|
708
1394
|
|
|
709
1395
|
assert_equal '', curl.body_str
|
|
710
|
-
assert_match
|
|
1396
|
+
assert_match('/nonexistent', redirect[1])
|
|
711
1397
|
curl.head = false
|
|
712
1398
|
curl.perform
|
|
713
1399
|
assert_equal 'GET', curl.body_str
|
|
@@ -717,9 +1403,11 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
717
1403
|
curl = Curl::Easy.new(TestServlet.url)
|
|
718
1404
|
curl.headers['Content-Type'] = 'application/json'
|
|
719
1405
|
assert curl.http_put("message")
|
|
720
|
-
assert_match
|
|
721
|
-
assert_match
|
|
722
|
-
assert_match
|
|
1406
|
+
assert_match(/^PUT/, curl.body_str)
|
|
1407
|
+
assert_match(/message$/, curl.body_str)
|
|
1408
|
+
assert_match(/message$/, curl.body)
|
|
1409
|
+
assert_match(/application\/json/, curl.header_str)
|
|
1410
|
+
assert_match(/application\/json/, curl.head)
|
|
723
1411
|
end
|
|
724
1412
|
|
|
725
1413
|
def test_put_data
|
|
@@ -728,8 +1416,46 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
728
1416
|
|
|
729
1417
|
curl.perform
|
|
730
1418
|
|
|
731
|
-
assert_match
|
|
732
|
-
assert_match
|
|
1419
|
+
assert_match(/^PUT/, curl.body_str)
|
|
1420
|
+
assert_match(/message$/, curl.body_str)
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
def test_put_data_from_to_s_object
|
|
1424
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1425
|
+
curl.put_data = :message
|
|
1426
|
+
|
|
1427
|
+
curl.perform
|
|
1428
|
+
|
|
1429
|
+
assert_equal "PUT\nmessage", curl.body_str
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1432
|
+
def test_put_data_read_exception_sets_result_and_releases_callback_state
|
|
1433
|
+
error_class = Class.new(StandardError)
|
|
1434
|
+
reader_class = Class.new do
|
|
1435
|
+
define_method(:read) do |_len|
|
|
1436
|
+
raise error_class, 'upload read failed'
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
def seek(_offset, _whence = SEEK_SET)
|
|
1440
|
+
0
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
def stat
|
|
1444
|
+
Struct.new(:size).new(1)
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1449
|
+
curl.put_data = reader_class.new
|
|
1450
|
+
|
|
1451
|
+
error = assert_raise(error_class) { curl.perform }
|
|
1452
|
+
assert_equal 'upload read failed', error.message
|
|
1453
|
+
assert_not_equal 0, curl.last_result
|
|
1454
|
+
|
|
1455
|
+
curl.url = TestServlet.url
|
|
1456
|
+
curl.http_get
|
|
1457
|
+
|
|
1458
|
+
assert_equal 'GET', curl.body_str
|
|
733
1459
|
end
|
|
734
1460
|
|
|
735
1461
|
# https://github.com/taf2/curb/issues/101
|
|
@@ -739,8 +1465,8 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
739
1465
|
|
|
740
1466
|
curl.perform
|
|
741
1467
|
|
|
742
|
-
assert_match
|
|
743
|
-
assert_match
|
|
1468
|
+
assert_match(/^PUT/, curl.body_str)
|
|
1469
|
+
assert_match("a\0b", curl.body_str)
|
|
744
1470
|
end
|
|
745
1471
|
|
|
746
1472
|
def test_put_nil_data_no_crash
|
|
@@ -755,7 +1481,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
755
1481
|
File.open(__FILE__,'rb') do|f|
|
|
756
1482
|
assert curl.http_put(f)
|
|
757
1483
|
end
|
|
758
|
-
assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str
|
|
1484
|
+
assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str.tr("\r", '')
|
|
759
1485
|
end
|
|
760
1486
|
|
|
761
1487
|
def test_put_class_method
|
|
@@ -765,7 +1491,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
765
1491
|
assert_equal Curl::Easy, c.class
|
|
766
1492
|
end
|
|
767
1493
|
assert_equal 1, count
|
|
768
|
-
assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str
|
|
1494
|
+
assert_equal "PUT\n#{File.read(__FILE__)}", curl.body_str.tr("\r", '')
|
|
769
1495
|
end
|
|
770
1496
|
|
|
771
1497
|
# Generate a self-signed cert with
|
|
@@ -774,7 +1500,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
774
1500
|
def test_cert
|
|
775
1501
|
curl = Curl::Easy.new(TestServlet.url)
|
|
776
1502
|
curl.cert= File.join(File.dirname(__FILE__),"cert.pem")
|
|
777
|
-
assert_match
|
|
1503
|
+
assert_match(/cert.pem$/,curl.cert)
|
|
778
1504
|
end
|
|
779
1505
|
|
|
780
1506
|
def test_cert_with_password
|
|
@@ -782,7 +1508,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
782
1508
|
path = File.join(File.dirname(__FILE__),"cert.pem")
|
|
783
1509
|
curl.certpassword = 'password'
|
|
784
1510
|
curl.cert = path
|
|
785
|
-
assert_match
|
|
1511
|
+
assert_match(/cert.pem$/,curl.cert)
|
|
786
1512
|
end
|
|
787
1513
|
|
|
788
1514
|
def test_cert_type
|
|
@@ -803,7 +1529,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
803
1529
|
def test_ca_cert
|
|
804
1530
|
curl = Curl::Easy.new(TestServlet.url)
|
|
805
1531
|
curl.cacert= File.join(File.dirname(__FILE__),"cacert.pem")
|
|
806
|
-
assert_match
|
|
1532
|
+
assert_match(/cacert.pem$/, curl.cacert)
|
|
807
1533
|
end
|
|
808
1534
|
|
|
809
1535
|
def test_user_agent
|
|
@@ -829,7 +1555,15 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
829
1555
|
$auth_header = nil
|
|
830
1556
|
# curl checks the auth type supported by the server, so we have to create a
|
|
831
1557
|
# new easy handle if we're going to change the auth type...
|
|
832
|
-
|
|
1558
|
+
if WINDOWS
|
|
1559
|
+
# On Windows, libcurl often uses SSPI for NTLM which yields a different
|
|
1560
|
+
# header value and encoding; skip the NTLM-specific assertion.
|
|
1561
|
+
return
|
|
1562
|
+
end
|
|
1563
|
+
# curl 8.20.0 disables NTLM by default and plans to remove it in
|
|
1564
|
+
# September 2026; libcurl silently downgrades to Basic when NTLM is
|
|
1565
|
+
# unavailable, so skip the NTLM-specific assertion in that case.
|
|
1566
|
+
return unless Curl.ntlm?
|
|
833
1567
|
curl = Curl::Easy.new(TestServlet.url)
|
|
834
1568
|
curl.username = "foo"
|
|
835
1569
|
curl.password = "bar"
|
|
@@ -850,7 +1584,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
850
1584
|
end
|
|
851
1585
|
|
|
852
1586
|
def test_post_streaming
|
|
853
|
-
readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README'))
|
|
1587
|
+
readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README.md'))
|
|
854
1588
|
|
|
855
1589
|
pf = Curl::PostField.file("filename", readme)
|
|
856
1590
|
|
|
@@ -860,11 +1594,10 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
860
1594
|
easy.multipart_form_post = true
|
|
861
1595
|
easy.http_post(pf)
|
|
862
1596
|
|
|
863
|
-
assert_not_equal(0,easy.
|
|
864
|
-
assert_equal(easy.
|
|
1597
|
+
assert_not_equal(0,easy.body.size)
|
|
1598
|
+
assert_equal(Digest::MD5.hexdigest(easy.body), Digest::MD5.hexdigest(File.binread(readme)))
|
|
865
1599
|
end
|
|
866
1600
|
|
|
867
|
-
|
|
868
1601
|
def test_easy_close
|
|
869
1602
|
easy = Curl::Easy.new
|
|
870
1603
|
easy.close
|
|
@@ -872,6 +1605,18 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
872
1605
|
easy.http_get
|
|
873
1606
|
end
|
|
874
1607
|
|
|
1608
|
+
def test_easy_close_restores_error_buffer
|
|
1609
|
+
easy = Curl::Easy.new('http://127.0.0.1:1')
|
|
1610
|
+
assert_raise(Curl::Err::ConnectionFailedError) { easy.perform }
|
|
1611
|
+
assert_not_nil easy.last_error
|
|
1612
|
+
|
|
1613
|
+
easy.close
|
|
1614
|
+
easy.url = 'http://127.0.0.1:1'
|
|
1615
|
+
assert_raise(Curl::Err::ConnectionFailedError) { easy.perform }
|
|
1616
|
+
|
|
1617
|
+
assert_not_nil easy.last_error
|
|
1618
|
+
end
|
|
1619
|
+
|
|
875
1620
|
def test_easy_reset
|
|
876
1621
|
easy = Curl::Easy.new
|
|
877
1622
|
easy.url = TestServlet.url + "?query=foo"
|
|
@@ -884,6 +1629,18 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
884
1629
|
easy.http_get
|
|
885
1630
|
end
|
|
886
1631
|
|
|
1632
|
+
def test_last_result_initialization
|
|
1633
|
+
# Test for issue #463 - ensure last_result is properly initialized to 0
|
|
1634
|
+
easy = Curl::Easy.new
|
|
1635
|
+
assert_equal 0, easy.last_result, "last_result should be initialized to 0 for new instance"
|
|
1636
|
+
|
|
1637
|
+
# Test that reset also sets last_result to 0
|
|
1638
|
+
easy.url = TestServlet.url
|
|
1639
|
+
easy.http_get
|
|
1640
|
+
easy.reset
|
|
1641
|
+
assert_equal 0, easy.last_result, "last_result should be reset to 0 after calling reset"
|
|
1642
|
+
end
|
|
1643
|
+
|
|
887
1644
|
def test_easy_use_http_versions
|
|
888
1645
|
easy = Curl::Easy.new
|
|
889
1646
|
easy.url = TestServlet.url + "?query=foo"
|
|
@@ -909,16 +1666,22 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
909
1666
|
assert_equal "PUT\nhello", curl.body_str
|
|
910
1667
|
curl.http('COPY')
|
|
911
1668
|
assert_equal 'COPY', curl.body_str
|
|
1669
|
+
|
|
1670
|
+
curl.http_patch
|
|
1671
|
+
assert_equal "PATCH\n", curl.body_str
|
|
1672
|
+
|
|
1673
|
+
curl.http_put
|
|
1674
|
+
assert_equal "PUT\n", curl.body_str
|
|
912
1675
|
end
|
|
913
1676
|
|
|
914
1677
|
def test_easy_http_verbs_must_respond_to_str
|
|
915
|
-
# issue http://github.com/taf2/curb/issues
|
|
1678
|
+
# issue http://github.com/taf2/curb/issues/45
|
|
916
1679
|
assert_nothing_raised do
|
|
917
|
-
c = Curl::Easy.new ; c.url =
|
|
1680
|
+
c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(:get)
|
|
918
1681
|
end
|
|
919
1682
|
|
|
920
1683
|
assert_raise RuntimeError do
|
|
921
|
-
c = Curl::Easy.new ; c.url =
|
|
1684
|
+
c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(FooNoToS.new)
|
|
922
1685
|
end
|
|
923
1686
|
|
|
924
1687
|
end
|
|
@@ -957,8 +1720,8 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
957
1720
|
curl.headers['Accept'] = '*/*'
|
|
958
1721
|
curl.headers['Authorization'] = 'Foo Bar Biz Baz'
|
|
959
1722
|
curl.http_put(rd)
|
|
960
|
-
assert_match
|
|
961
|
-
assert_match
|
|
1723
|
+
assert_match(/^PUT/, curl.body_str)
|
|
1724
|
+
assert_match(/hello$/, curl.body_str)
|
|
962
1725
|
curl.header_str
|
|
963
1726
|
curl.body_str
|
|
964
1727
|
end
|
|
@@ -991,7 +1754,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
991
1754
|
c = Curl::Easy.new($TEST_URL)
|
|
992
1755
|
c.on_success {|x| raise "error" }
|
|
993
1756
|
c.perform
|
|
994
|
-
rescue => e
|
|
1757
|
+
rescue Curl::Err::AbortedByCallbackError => e
|
|
995
1758
|
assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
|
|
996
1759
|
c.close
|
|
997
1760
|
end
|
|
@@ -1017,6 +1780,91 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
1017
1780
|
end
|
|
1018
1781
|
end
|
|
1019
1782
|
|
|
1783
|
+
def test_close_in_on_progress_is_blocked
|
|
1784
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1785
|
+
did_raise = false
|
|
1786
|
+
|
|
1787
|
+
curl.on_progress do |_dltotal, _dlnow, _ultotal, _ulnow|
|
|
1788
|
+
unless did_raise
|
|
1789
|
+
begin
|
|
1790
|
+
curl.close
|
|
1791
|
+
rescue RuntimeError
|
|
1792
|
+
did_raise = true
|
|
1793
|
+
end
|
|
1794
|
+
end
|
|
1795
|
+
true
|
|
1796
|
+
end
|
|
1797
|
+
|
|
1798
|
+
curl.perform
|
|
1799
|
+
assert did_raise
|
|
1800
|
+
end
|
|
1801
|
+
|
|
1802
|
+
def test_close_in_on_debug_is_blocked
|
|
1803
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1804
|
+
did_raise = false
|
|
1805
|
+
|
|
1806
|
+
curl.on_debug do |_type, _data|
|
|
1807
|
+
unless did_raise
|
|
1808
|
+
begin
|
|
1809
|
+
curl.close
|
|
1810
|
+
rescue RuntimeError
|
|
1811
|
+
did_raise = true
|
|
1812
|
+
end
|
|
1813
|
+
end
|
|
1814
|
+
end
|
|
1815
|
+
|
|
1816
|
+
curl.perform
|
|
1817
|
+
assert did_raise
|
|
1818
|
+
end
|
|
1819
|
+
|
|
1820
|
+
def test_close_in_upload_read_is_blocked
|
|
1821
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1822
|
+
|
|
1823
|
+
reader_class = Class.new do
|
|
1824
|
+
attr_reader :close_blocked
|
|
1825
|
+
|
|
1826
|
+
def initialize(curl)
|
|
1827
|
+
@curl = curl
|
|
1828
|
+
@done = false
|
|
1829
|
+
@close_blocked = false
|
|
1830
|
+
end
|
|
1831
|
+
|
|
1832
|
+
def read(_len)
|
|
1833
|
+
return nil if @done
|
|
1834
|
+
|
|
1835
|
+
@done = true
|
|
1836
|
+
begin
|
|
1837
|
+
@curl.close
|
|
1838
|
+
rescue RuntimeError
|
|
1839
|
+
@close_blocked = true
|
|
1840
|
+
end
|
|
1841
|
+
|
|
1842
|
+
'hello'
|
|
1843
|
+
end
|
|
1844
|
+
|
|
1845
|
+
def seek(_offset, _whence = SEEK_SET)
|
|
1846
|
+
0
|
|
1847
|
+
end
|
|
1848
|
+
|
|
1849
|
+
def stat
|
|
1850
|
+
Struct.new(:size).new(5)
|
|
1851
|
+
end
|
|
1852
|
+
end
|
|
1853
|
+
|
|
1854
|
+
reader = reader_class.new(curl)
|
|
1855
|
+
curl.http_put(reader)
|
|
1856
|
+
|
|
1857
|
+
assert reader.close_blocked
|
|
1858
|
+
assert_equal "PUT\nhello", curl.body_str
|
|
1859
|
+
end
|
|
1860
|
+
|
|
1861
|
+
def test_set_unsupported_options
|
|
1862
|
+
curl = Curl::Easy.new
|
|
1863
|
+
assert_raises TypeError do
|
|
1864
|
+
curl.set(99999, 1)
|
|
1865
|
+
end
|
|
1866
|
+
end
|
|
1867
|
+
|
|
1020
1868
|
include TestServerMethods
|
|
1021
1869
|
|
|
1022
1870
|
def setup
|