curb 0.8.5 → 1.3.6

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +579 -0
  3. data/Rakefile +85 -27
  4. data/doc.rb +48 -8
  5. data/ext/banned.h +32 -0
  6. data/ext/curb.c +563 -233
  7. data/ext/curb.h +19 -10
  8. data/ext/curb_easy.c +3229 -368
  9. data/ext/curb_easy.h +42 -0
  10. data/ext/curb_errors.c +117 -18
  11. data/ext/curb_errors.h +9 -5
  12. data/ext/curb_macros.h +33 -21
  13. data/ext/curb_multi.c +1904 -272
  14. data/ext/curb_multi.h +11 -3
  15. data/ext/curb_postfield.c +149 -77
  16. data/ext/curb_postfield.h +1 -0
  17. data/ext/curb_upload.c +38 -11
  18. data/ext/curb_upload.h +2 -0
  19. data/ext/extconf.rb +355 -35
  20. data/lib/curb.rb +1 -0
  21. data/lib/curl/download.rb +160 -0
  22. data/lib/curl/easy.rb +422 -88
  23. data/lib/curl/multi.rb +295 -56
  24. data/lib/curl.rb +676 -11
  25. data/tests/bug_crash_on_debug.rb +14 -28
  26. data/tests/bug_crash_on_progress.rb +32 -16
  27. data/tests/bug_curb_easy_blocks_ruby_threads.rb +10 -15
  28. data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +6 -30
  29. data/tests/bug_follow_redirect_288.rb +83 -0
  30. data/tests/bug_instance_post_differs_from_class_post.rb +3 -5
  31. data/tests/bug_issue102.rb +17 -0
  32. data/tests/bug_issue_noproxy.rb +56 -0
  33. data/tests/bug_issue_post_redirect.rb +93 -0
  34. data/tests/bug_issue_spnego.rb +41 -0
  35. data/tests/bug_multi_segfault.rb +1 -0
  36. data/tests/bug_poison.rb +29 -0
  37. data/tests/bug_raise_on_callback.rb +30 -0
  38. data/tests/helper.rb +400 -44
  39. data/tests/leak_trace.rb +237 -0
  40. data/tests/mem_check.rb +3 -0
  41. data/tests/tc_curl.rb +31 -1
  42. data/tests/tc_curl_download.rb +98 -7
  43. data/tests/tc_curl_easy.rb +985 -66
  44. data/tests/tc_curl_easy_cookielist.rb +277 -0
  45. data/tests/tc_curl_easy_request_target.rb +41 -0
  46. data/tests/tc_curl_easy_resolve.rb +48 -0
  47. data/tests/tc_curl_maxfilesize.rb +212 -0
  48. data/tests/tc_curl_multi.rb +1111 -51
  49. data/tests/tc_curl_native_coverage.rb +145 -0
  50. data/tests/tc_curl_network_policy.rb +1475 -0
  51. data/tests/tc_curl_postfield.rb +207 -30
  52. data/tests/tc_curl_protocols.rb +388 -0
  53. data/tests/tc_fiber_scheduler.rb +584 -0
  54. data/tests/tc_ftp_options.rb +39 -0
  55. data/tests/tc_gc_compact.rb +223 -0
  56. data/tests/tc_test_server_methods.rb +110 -0
  57. data/tests/timeout.rb +30 -6
  58. metadata +66 -31
  59. data/README +0 -194
@@ -2,109 +2,110 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
2
 
3
3
  class TestCurbCurlPostfield < Test::Unit::TestCase
4
4
  def test_private_new
5
- assert_raise(NoMethodError) { Curl::PostField.new }
5
+ assert_raise(NoMethodError) { Curl::PostField.new }
6
6
  end
7
-
7
+
8
8
  def test_new_content_01
9
9
  pf = Curl::PostField.content('foo', 'bar')
10
-
10
+
11
11
  assert_equal 'foo', pf.name
12
12
  assert_equal 'bar', pf.content
13
13
  assert_nil pf.content_type
14
14
  assert_nil pf.local_file
15
- assert_nil pf.remote_file
15
+ assert_nil pf.remote_file
16
16
  assert_nil pf.set_content_proc
17
17
  end
18
-
18
+
19
19
  def test_new_content_02
20
20
  pf = Curl::PostField.content('foo', 'bar', 'text/html')
21
-
21
+
22
22
  assert_equal 'foo', pf.name
23
23
  assert_equal 'bar', pf.content
24
24
  assert_equal 'text/html', pf.content_type
25
25
  assert_nil pf.local_file
26
26
  assert_nil pf.remote_file
27
- assert_nil pf.set_content_proc
28
- end
29
-
27
+ assert_nil pf.set_content_proc
28
+ end
29
+
30
30
  def test_new_content_03
31
31
  l = lambda { |field| "never gets run" }
32
32
  pf = Curl::PostField.content('foo', &l)
33
-
33
+
34
34
  assert_equal 'foo', pf.name
35
35
  assert_nil pf.content
36
36
  assert_nil pf.content_type
37
37
  assert_nil pf.local_file
38
38
  assert_nil pf.remote_file
39
-
39
+
40
40
  # N.B. This doesn't just get the proc, but also removes it.
41
41
  assert_equal l, pf.set_content_proc
42
- end
42
+ end
43
43
 
44
44
  def test_new_content_04
45
45
  l = lambda { |field| "never gets run" }
46
46
  pf = Curl::PostField.content('foo', 'text/html', &l)
47
-
47
+
48
48
  assert_equal 'foo', pf.name
49
49
  assert_nil pf.content
50
50
  assert_equal 'text/html', pf.content_type
51
51
  assert_nil pf.local_file
52
52
  assert_nil pf.remote_file
53
-
53
+
54
54
  # N.B. This doesn't just get the proc, but also removes it.
55
55
  assert_equal l, pf.set_content_proc
56
- end
56
+ end
57
57
 
58
58
 
59
59
  def test_new_file_01
60
60
  pf = Curl::PostField.file('foo', 'localname')
61
-
61
+ pf.content_type = 'text/super'
62
+
62
63
  assert_equal 'foo', pf.name
63
64
  assert_equal 'localname', pf.local_file
64
65
  assert_equal 'localname', pf.remote_file
65
66
  assert_nothing_raised { pf.to_s }
66
- assert_nil pf.content_type
67
+ assert_equal 'text/super', pf.content_type
67
68
  assert_nil pf.content
68
69
  assert_nil pf.set_content_proc
69
70
  end
70
-
71
+
71
72
  def test_new_file_02
72
73
  pf = Curl::PostField.file('foo', 'localname', 'remotename')
73
-
74
+
74
75
  assert_equal 'foo', pf.name
75
76
  assert_equal 'localname', pf.local_file
76
77
  assert_equal 'remotename', pf.remote_file
77
78
  assert_nil pf.content_type
78
79
  assert_nil pf.content
79
80
  assert_nil pf.set_content_proc
80
- end
81
-
81
+ end
82
+
82
83
  def test_new_file_03
83
84
  l = lambda { |field| "never gets run" }
84
85
  pf = Curl::PostField.file('foo', 'remotename', &l)
85
-
86
+
86
87
  assert_equal 'foo', pf.name
87
88
  assert_equal 'remotename', pf.remote_file
88
89
  assert_nil pf.local_file
89
90
  assert_nil pf.content_type
90
91
  assert_nil pf.content
91
-
92
+
92
93
  # N.B. This doesn't just get the proc, but also removes it.
93
94
  assert_equal l, pf.set_content_proc
94
- end
95
+ end
95
96
 
96
97
  def test_new_file_04
97
98
  assert_raise(ArgumentError) do
98
99
  # no local name, no block
99
100
  Curl::PostField.file('foo')
100
101
  end
101
-
102
+
102
103
  assert_raise(ArgumentError) do
103
104
  # no remote name with block
104
105
  Curl::PostField.file('foo') { |field| "never runs" }
105
106
  end
106
107
  end
107
-
108
+
108
109
  def test_new_file_05
109
110
  # local gets ignored when supplying a block, but remote
110
111
  # is still set up properly.
@@ -118,15 +119,15 @@ class TestCurbCurlPostfield < Test::Unit::TestCase
118
119
  assert_nil pf.content
119
120
 
120
121
  assert_equal l, pf.set_content_proc
121
- end
122
-
122
+ end
123
+
123
124
  def test_to_s_01
124
- pf = Curl::PostField.content('foo', 'bar')
125
+ pf = Curl::PostField.content('foo', 'bar')
125
126
  assert_equal "foo=bar", pf.to_s
126
127
  end
127
128
 
128
129
  def test_to_s_02
129
- pf = Curl::PostField.content('foo', 'bar ton')
130
+ pf = Curl::PostField.content('foo', 'bar ton')
130
131
  assert_equal "foo=bar%20ton", pf.to_s
131
132
  end
132
133
 
@@ -135,9 +136,185 @@ class TestCurbCurlPostfield < Test::Unit::TestCase
135
136
  assert_equal "foo=FOOBAR", pf.to_s
136
137
  end
137
138
 
139
+ def test_content_proc_survives_gc
140
+ pf = postfield_with_only_native_proc_reference
141
+
142
+ 10.times do
143
+ GC.start(full_mark: true, immediate_sweep: true)
144
+ GC.compact if GC.respond_to?(:compact)
145
+ end
146
+
147
+ assert_equal "foo=FOOBAR", pf.to_s
148
+ end
149
+
138
150
  def test_to_s_04
139
151
  pf = Curl::PostField.file('foo.file', 'bar.file')
140
152
  assert_nothing_raised { pf.to_s }
141
153
  #assert_raise(Curl::Err::InvalidPostFieldError) { pf.to_s }
142
154
  end
155
+
156
+ def postfield_with_only_native_proc_reference
157
+ Curl::PostField.content('foo') { |field| field.name.upcase + "BAR" }
158
+ end
159
+ end
160
+
161
+ class TestCurbCurlPostfieldNativeCoverage < Test::Unit::TestCase
162
+ def test_attribute_writers_round_trip
163
+ pf = Curl::PostField.content('foo', 'bar')
164
+
165
+ assert_equal 'renamed', pf.name = 'renamed'
166
+ assert_equal 'payload', pf.content = 'payload'
167
+ assert_equal 'text/plain', pf.content_type = 'text/plain'
168
+ assert_equal 'local.txt', pf.local_file = 'local.txt'
169
+ assert_equal 'remote.txt', pf.remote_file = 'remote.txt'
170
+
171
+ assert_equal 'renamed', pf.name
172
+ assert_equal 'payload', pf.content
173
+ assert_equal 'text/plain', pf.content_type
174
+ assert_equal 'local.txt', pf.local_file
175
+ assert_equal 'remote.txt', pf.remote_file
176
+ end
177
+
178
+ def test_to_s_accepts_non_string_name_via_to_s
179
+ name_like = Object.new
180
+ def name_like.to_s
181
+ 'fancy name'
182
+ end
183
+
184
+ pf = Curl::PostField.content(name_like, 'value')
185
+ assert_equal 'fancy%20name=value', pf.to_s
186
+ end
187
+
188
+ def test_to_s_uses_remote_file_when_local_file_is_missing
189
+ pf = Curl::PostField.file('upload', 'local.txt', 'remote.txt')
190
+ pf.local_file = nil
191
+
192
+ assert_equal 'upload=remote.txt', pf.to_s
193
+ end
194
+
195
+ def test_to_s_rejects_name_without_to_s
196
+ name_like = Class.new do
197
+ undef to_s
198
+ end.new
199
+
200
+ pf = Curl::PostField.content(name_like, 'value')
201
+
202
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { pf.to_s }
203
+ assert_match(/Cannot convert unnamed field to string/, error.message)
204
+ end
205
+
206
+ def test_to_s_rejects_content_without_to_s
207
+ content_like = Class.new do
208
+ undef to_s
209
+ end.new
210
+
211
+ pf = Curl::PostField.content('name', content_like)
212
+
213
+ error = assert_raise(RuntimeError) { pf.to_s }
214
+ assert_match(/does not respond_to to_s/, error.message)
215
+ end
216
+
217
+ def test_multipart_rejects_unnamed_field
218
+ curl = Curl::Easy.new(TestServlet.url)
219
+ curl.multipart_form_post = true
220
+ pf = Curl::PostField.content('name', 'value')
221
+ pf.name = nil
222
+
223
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
224
+ assert_match(/Cannot post unnamed field/, error.message)
225
+ end
226
+
227
+ def test_multipart_rejects_content_field_without_data
228
+ curl = Curl::Easy.new(TestServlet.url)
229
+ curl.multipart_form_post = true
230
+ pf = Curl::PostField.content('name', 'value')
231
+ pf.content = nil
232
+
233
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
234
+ assert_match(/Cannot post content field with no data/, error.message)
235
+ end
236
+
237
+ def test_multipart_rejects_file_field_without_filename
238
+ curl = Curl::Easy.new(TestServlet.url)
239
+ curl.multipart_form_post = true
240
+ pf = Curl::PostField.file('upload', 'remote.txt') { 'payload' }
241
+ pf.local_file = 'dummy.txt'
242
+ pf.remote_file = nil
243
+
244
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
245
+ assert_match(/Cannot post file upload field with no filename/, error.message)
246
+ end
247
+ end
248
+
249
+ class TestCurbCurlPostfieldMultipartCoverage < Test::Unit::TestCase
250
+ include TestServerMethods
251
+
252
+ def setup
253
+ server_setup
254
+ end
255
+
256
+ def test_multipart_content_variants_include_dynamic_and_typed_fields
257
+ curl = Curl::Easy.new(TestServlet.url)
258
+ curl.multipart_form_post = true
259
+
260
+ proc_without_type = Curl::PostField.content('proc_without_type') { 'alpha' }
261
+ proc_with_type = Curl::PostField.content('proc_with_type', 'text/plain') { 'beta' }
262
+ direct_with_type = Curl::PostField.content('direct_with_type', 'gamma', 'text/plain')
263
+
264
+ curl.http_post([proc_without_type, proc_with_type, direct_with_type])
265
+ body = curl.body_str
266
+
267
+ assert_match(/name="proc_without_type"/, body)
268
+ assert_match(/alpha/, body)
269
+ assert_match(/name="proc_with_type"/, body)
270
+ assert_match(/beta/, body)
271
+ assert_match(/name="direct_with_type"/, body)
272
+ assert_match(/gamma/, body)
273
+ assert_match(/Content-Type: text\/plain/, body)
274
+ end
275
+
276
+ def test_multipart_file_variants_include_buffered_and_local_uploads
277
+ readme = File.expand_path(File.join(File.dirname(__FILE__), '..', 'README.md'))
278
+
279
+ proc_without_type = Curl::PostField.file('proc_without_type', 'proc_without_type.txt') { 'alpha' }
280
+ proc_with_type = Curl::PostField.file('proc_with_type', 'proc_with_type.txt') { 'beta' }
281
+ proc_with_type.content_type = 'text/plain'
282
+
283
+ direct_without_type = Curl::PostField.file('direct_without_type', 'ignored.txt', 'direct_without_type.txt')
284
+ direct_without_type.content = 'gamma'
285
+ direct_without_type.local_file = nil
286
+
287
+ direct_with_type = Curl::PostField.file('direct_with_type', 'ignored.txt', 'direct_with_type.txt')
288
+ direct_with_type.content = 'delta'
289
+ direct_with_type.local_file = nil
290
+ direct_with_type.content_type = 'text/plain'
291
+
292
+ local_with_type = Curl::PostField.file('local_with_type', readme)
293
+ local_with_type.content_type = 'text/plain'
294
+
295
+ curl = Curl::Easy.new(TestServlet.url)
296
+ curl.multipart_form_post = true
297
+ curl.http_post([proc_without_type, proc_with_type, direct_without_type, direct_with_type, local_with_type])
298
+ body = curl.body_str
299
+
300
+ assert_match(/name="proc_without_type"/, body)
301
+ assert_match(/filename="proc_without_type.txt"/, body)
302
+ assert_match(/alpha/, body)
303
+
304
+ assert_match(/name="proc_with_type"/, body)
305
+ assert_match(/filename="proc_with_type.txt"/, body)
306
+ assert_match(/beta/, body)
307
+
308
+ assert_match(/name="direct_without_type"/, body)
309
+ assert_match(/filename="direct_without_type.txt"/, body)
310
+ assert_match(/gamma/, body)
311
+
312
+ assert_match(/name="direct_with_type"/, body)
313
+ assert_match(/filename="direct_with_type.txt"/, body)
314
+ assert_match(/delta/, body)
315
+
316
+ assert_match(/name="local_with_type"/, body)
317
+ assert_match(/Curb - Libcurl bindings for Ruby/, body)
318
+ assert_match(/Content-Type: text\/plain/, body)
319
+ end
143
320
  end