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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +522 -0
  3. data/Rakefile +79 -26
  4. data/ext/banned.h +32 -0
  5. data/ext/curb.c +549 -230
  6. data/ext/curb.h +19 -10
  7. data/ext/curb_easy.c +1935 -366
  8. data/ext/curb_easy.h +16 -0
  9. data/ext/curb_errors.c +115 -18
  10. data/ext/curb_errors.h +8 -5
  11. data/ext/curb_macros.h +33 -21
  12. data/ext/curb_multi.c +1858 -272
  13. data/ext/curb_multi.h +10 -3
  14. data/ext/curb_postfield.c +149 -77
  15. data/ext/curb_postfield.h +1 -0
  16. data/ext/curb_upload.c +38 -11
  17. data/ext/curb_upload.h +2 -0
  18. data/ext/extconf.rb +353 -35
  19. data/lib/curb.rb +1 -0
  20. data/lib/curl/easy.rb +314 -78
  21. data/lib/curl/multi.rb +134 -28
  22. data/lib/curl.rb +217 -11
  23. data/tests/bug_crash_on_debug.rb +14 -28
  24. data/tests/bug_crash_on_progress.rb +32 -16
  25. data/tests/bug_curb_easy_blocks_ruby_threads.rb +10 -15
  26. data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +6 -30
  27. data/tests/bug_follow_redirect_288.rb +83 -0
  28. data/tests/bug_instance_post_differs_from_class_post.rb +3 -5
  29. data/tests/bug_issue102.rb +1 -1
  30. data/tests/bug_issue_noproxy.rb +56 -0
  31. data/tests/bug_issue_post_redirect.rb +93 -0
  32. data/tests/bug_issue_spnego.rb +41 -0
  33. data/tests/bug_multi_segfault.rb +1 -0
  34. data/tests/bug_raise_on_callback.rb +30 -0
  35. data/tests/helper.rb +400 -44
  36. data/tests/leak_trace.rb +237 -0
  37. data/tests/mem_check.rb +3 -0
  38. data/tests/tc_curl.rb +31 -1
  39. data/tests/tc_curl_download.rb +12 -7
  40. data/tests/tc_curl_easy.rb +911 -63
  41. data/tests/tc_curl_easy_cookielist.rb +277 -0
  42. data/tests/tc_curl_easy_request_target.rb +41 -0
  43. data/tests/tc_curl_easy_resolve.rb +48 -0
  44. data/tests/tc_curl_maxfilesize.rb +12 -0
  45. data/tests/tc_curl_multi.rb +855 -53
  46. data/tests/tc_curl_native_coverage.rb +145 -0
  47. data/tests/tc_curl_postfield.rb +207 -30
  48. data/tests/tc_curl_protocols.rb +37 -0
  49. data/tests/tc_fiber_scheduler.rb +543 -0
  50. data/tests/tc_ftp_options.rb +39 -0
  51. data/tests/tc_gc_compact.rb +223 -0
  52. data/tests/tc_test_server_methods.rb +110 -0
  53. data/tests/timeout.rb +30 -6
  54. metadata +59 -36
  55. data/README +0 -194
@@ -22,26 +22,15 @@ end
22
22
  Any insight you care to share would be helpful. Thanks.
23
23
  =end
24
24
  require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
25
- require 'webrick'
26
- class ::WEBrick::HTTPServer ; def access_log(config, req, res) ; end ; end
27
- class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
28
25
 
29
26
  class BugCurbEasyPostWithStringNoContentLengthHeader < Test::Unit::TestCase
30
- def test_bug_workaround
31
- server = WEBrick::HTTPServer.new( :Port => 9999 )
32
- server.mount_proc("/test") do|req,res|
33
- assert_equal '15', req['Content-Length']
34
- res.body = "hi"
35
- res['Content-Type'] = "text/html"
36
- end
27
+ include BugTestServerSetupTeardown
37
28
 
38
- thread = Thread.new(server) do|srv|
39
- srv.start
40
- end
29
+ def test_bug_workaround
41
30
  params = {:cat => "hat", :foo => "bar"}
42
31
 
43
32
  post_body = params.map{|f,k| "#{Curl::Easy.new.escape(f)}=#{Curl::Easy.new.escape(k)}"}.join('&')
44
- c = Curl::Easy.http_post("http://127.0.0.1:9999/test",post_body) do |curl|
33
+ c = Curl::Easy.http_post("http://127.0.0.1:#{@port}/test",post_body) do |curl|
45
34
  curl.headers["User-Agent"] = "Curl/Ruby"
46
35
  curl.headers["X-Tender-Auth"] = "A Token"
47
36
  curl.headers["Accept"] = "application/vnd.tender-v1+json"
@@ -50,23 +39,12 @@ class BugCurbEasyPostWithStringNoContentLengthHeader < Test::Unit::TestCase
50
39
  curl.enable_cookies = true
51
40
  end
52
41
 
53
- server.shutdown
54
- thread.join
55
42
  end
56
- def test_bug
57
- server = WEBrick::HTTPServer.new( :Port => 9999 )
58
- server.mount_proc("/test") do|req,res|
59
- assert_equal '15', req['Content-Length']
60
- res.body = "hi"
61
- res['Content-Type'] = "text/html"
62
- end
63
43
 
64
- thread = Thread.new(server) do|srv|
65
- srv.start
66
- end
44
+ def test_bug
67
45
  params = {:cat => "hat", :foo => "bar"}
68
46
 
69
- c = Curl::Easy.http_post("http://127.0.0.1:9999/test") do |curl|
47
+ c = Curl::Easy.http_post("http://127.0.0.1:#{@port}/test") do |curl|
70
48
  curl.headers["User-Agent"] = "Curl/Ruby"
71
49
  curl.headers["X-Tender-Auth"] = "A Token"
72
50
  curl.headers["Accept"] = "application/vnd.tender-v1+json"
@@ -76,8 +54,6 @@ class BugCurbEasyPostWithStringNoContentLengthHeader < Test::Unit::TestCase
76
54
  curl.follow_location = true
77
55
  curl.enable_cookies = true
78
56
  end
79
-
80
- server.shutdown
81
- thread.join
82
57
  end
58
+
83
59
  end
@@ -0,0 +1,83 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class BugFollowRedirect288 < Test::Unit::TestCase
4
+ include BugTestServerSetupTeardown
5
+
6
+ def setup
7
+ @port = unused_local_port
8
+ super
9
+ @server.mount_proc("/redirect_to_test") do|req,res|
10
+ res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, "/test")
11
+ end
12
+ end
13
+
14
+ def test_follow_redirect_with_no_redirect
15
+
16
+ c = Curl::Easy.new("http://127.0.0.1:#{@port}/test")
17
+ did_call_redirect = false
18
+ c.on_redirect do|x|
19
+ did_call_redirect = true
20
+ end
21
+ c.perform
22
+
23
+ assert !did_call_redirect, "should reach this point redirect should not have been called"
24
+
25
+ c = Curl::Easy.new("http://127.0.0.1:#{@port}/test")
26
+ did_call_redirect = false
27
+ c.on_redirect do|x|
28
+ did_call_redirect = true
29
+ end
30
+ c.follow_location = true
31
+ c.perform
32
+
33
+ assert_equal 0, c.redirect_count
34
+ assert !did_call_redirect, "should reach this point redirect should not have been called"
35
+
36
+ c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
37
+ did_call_redirect = false
38
+ c.on_redirect do|x|
39
+ did_call_redirect = true
40
+ end
41
+ c.perform
42
+ assert_equal 307, c.response_code
43
+
44
+ assert did_call_redirect, "we should have called on_redirect"
45
+
46
+ c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
47
+ did_call_redirect = false
48
+ c.follow_location = true
49
+ # NOTE: while this API is not supported by libcurl e.g. there is no redirect function callback in libcurl we could
50
+ # add support in ruby for this by executing this callback if redirect_count is greater than 0 at the end of a request in curb_multi.c
51
+ c.on_redirect do|x|
52
+ did_call_redirect = true
53
+ end
54
+ c.perform
55
+ assert_equal 1, c.redirect_count
56
+ assert_equal 200, c.response_code
57
+
58
+ assert did_call_redirect, "we should have called on_redirect"
59
+
60
+ c.url = "http://127.0.0.1:#{@port}/test"
61
+ c.perform
62
+ assert_equal 0, c.redirect_count
63
+ assert_equal 200, c.response_code
64
+
65
+ puts "checking for raise support"
66
+ did_raise = false
67
+ begin
68
+ c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
69
+ did_call_redirect = false
70
+ c.on_redirect do|x|
71
+ raise "raise"
72
+ did_call_redirect = true
73
+ end
74
+ c.perform
75
+ rescue => e
76
+ did_raise = true
77
+ end
78
+ assert_equal 307, c.response_code
79
+ assert did_raise
80
+
81
+ end
82
+
83
+ end
@@ -35,15 +35,13 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
35
35
  end
36
36
 
37
37
  def do_test
38
- c = Curl::Easy.http_post('https://www.google.com/accounts/ServiceLoginAuth',
39
- Curl::PostField.content('ltmpl','m_blanco'))
38
+ c = Curl::Easy.http_post('https://www.google.com/accounts/ServiceLoginAuth', Curl::PostField.content('ltmpl','m_blanco'))
40
39
  body_c, header_c = c.body_str, c.header_str
41
40
 
42
41
  sleep 2
43
42
 
44
- c.http_post('https://www.google.com/accounts/ServiceLoginAuth',
45
- Curl::PostField.content('ltmpl','m_blanco'))
46
- body_i, header_i = c.body_str, c.header_str
43
+ c.http_post('https://www.google.com/accounts/ServiceLoginAuth', Curl::PostField.content('ltmpl','m_blanco'))
44
+ body_i, header_i = c.body, c.head
47
45
 
48
46
  # timestamps will differ, just check first bit. We wont get here if
49
47
  # the bug bites anyway...
@@ -4,7 +4,7 @@ class BugIssue102 < Test::Unit::TestCase
4
4
 
5
5
  def test_interface
6
6
  test = "https://api.twitter.com/1/users/show.json?screen_name=TwitterAPI&include_entities=true"
7
- ip = "192.168.1.61"
7
+ ip = "0.0.0.0"
8
8
 
9
9
  c = Curl::Easy.new do |curl|
10
10
  curl.url = test
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class BugIssueNoproxy < Test::Unit::TestCase
4
+ def test_noproxy_option_support
5
+ # Test that CURLOPT_NOPROXY constant is defined
6
+ assert Curl.const_defined?(:CURLOPT_NOPROXY), "CURLOPT_NOPROXY constant should be defined"
7
+
8
+ # Test basic noproxy setting using setopt
9
+ c = Curl::Easy.new('https://google.com')
10
+
11
+ # This should not raise an error
12
+ assert_nothing_raised do
13
+ c.setopt(Curl::CURLOPT_NOPROXY, "localhost,127.0.0.1")
14
+ end
15
+
16
+ # Test using the convenience method if it exists
17
+ if c.respond_to?(:noproxy=)
18
+ assert_nothing_raised do
19
+ c.noproxy = "localhost,127.0.0.1"
20
+ end
21
+
22
+ # Test getter if it exists
23
+ if c.respond_to?(:noproxy)
24
+ assert_equal "localhost,127.0.0.1", c.noproxy
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_noproxy_with_set_method
30
+ c = Curl::Easy.new('https://google.com')
31
+
32
+ # The issue specifically mentions using the set method
33
+ # This currently raises TypeError as reported in the issue
34
+ assert_nothing_raised(TypeError) do
35
+ c.set(:noproxy, "localhost,127.0.0.1")
36
+ end
37
+ end
38
+
39
+ def test_noproxy_empty_string
40
+ c = Curl::Easy.new('https://google.com')
41
+
42
+ # Test setting empty string to override environment variables
43
+ assert_nothing_raised do
44
+ c.setopt(Curl::CURLOPT_NOPROXY, "")
45
+ end
46
+ end
47
+
48
+ def test_noproxy_nil_value
49
+ c = Curl::Easy.new('https://google.com')
50
+
51
+ # Test setting nil to reset
52
+ assert_nothing_raised do
53
+ c.setopt(Curl::CURLOPT_NOPROXY, nil)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,93 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+ require 'json'
3
+
4
+ class BugIssuePostRedirect < Test::Unit::TestCase
5
+ include BugTestServerSetupTeardown
6
+
7
+ def setup
8
+ @port = 9998
9
+ super
10
+ # Mount a POST endpoint that returns a redirect
11
+ @server.mount_proc("/post_redirect") do |req, res|
12
+ if req.request_method == "POST"
13
+ res.status = 302
14
+ res['Location'] = "http://127.0.0.1:#{@port}/redirected"
15
+ res.body = "Redirecting..."
16
+ else
17
+ res.status = 405
18
+ res.body = "Method not allowed"
19
+ end
20
+ end
21
+
22
+ # Mount the redirect target
23
+ @server.mount_proc("/redirected") do |req, res|
24
+ res.status = 200
25
+ res['Content-Type'] = "text/plain"
26
+ res.body = "You have been redirected"
27
+ end
28
+ end
29
+
30
+ def test_post_with_max_redirects_zero_should_not_follow_redirect
31
+ # Test case replicating the issue: POST with max_redirects=0 and follow_location=false
32
+ # should NOT trigger on_redirect callback or follow the redirect
33
+
34
+ redirect_called = false
35
+
36
+ handle = Curl::Easy.new("http://127.0.0.1:#{@port}/post_redirect") do |curl|
37
+ curl.max_redirects = 0
38
+ curl.follow_location = false
39
+ curl.on_redirect do |easy|
40
+ redirect_called = true
41
+ end
42
+ curl.headers['Content-Type'] = 'application/json'
43
+ curl.post_body = {test: "data"}.to_json
44
+ end
45
+
46
+ handle.http(:POST)
47
+
48
+ # The response should be the redirect response (302)
49
+ assert_equal 302, handle.response_code
50
+ assert_match(/Redirecting/, handle.body_str)
51
+
52
+ # on_redirect should NOT be called when follow_location is false
53
+ assert !redirect_called, "on_redirect callback should not be called when follow_location is false"
54
+ end
55
+
56
+ def test_post_with_follow_location_true_triggers_redirect
57
+ # Test that on_redirect IS called when follow_location is true
58
+ redirect_called = false
59
+
60
+ handle = Curl::Easy.new("http://127.0.0.1:#{@port}/post_redirect") do |curl|
61
+ curl.follow_location = true
62
+ curl.on_redirect do |easy|
63
+ redirect_called = true
64
+ end
65
+ curl.headers['Content-Type'] = 'application/json'
66
+ curl.post_body = {test: "data"}.to_json
67
+ end
68
+
69
+ handle.http(:POST)
70
+
71
+ # Should follow the redirect and get the final response
72
+ assert_equal 200, handle.response_code
73
+ assert_match(/You have been redirected/, handle.body_str)
74
+
75
+ # on_redirect SHOULD be called when follow_location is true
76
+ assert redirect_called, "on_redirect callback should be called when follow_location is true"
77
+ end
78
+
79
+ def test_curl_post_class_method_respects_redirect_settings
80
+ # Test that Curl.post (class method) respects redirect settings
81
+ # According to the issue, this works correctly
82
+
83
+ response = Curl.post("http://127.0.0.1:#{@port}/post_redirect", {test: "data"}.to_json) do |curl|
84
+ curl.max_redirects = 0
85
+ curl.follow_location = false
86
+ curl.headers['Content-Type'] = 'application/json'
87
+ end
88
+
89
+ # Should get the redirect response, not follow it
90
+ assert_equal 302, response.response_code
91
+ assert_match(/Redirecting/, response.body_str)
92
+ end
93
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class BugIssueSpnego < Test::Unit::TestCase
4
+ def test_spnego_detection_works
5
+ # The fix for issue #227 ensures that Curl.spnego? checks for both
6
+ # CURL_VERSION_SPNEGO (newer libcurl) and CURL_VERSION_GSSNEGOTIATE (older libcurl)
7
+ #
8
+ # We can't test that it returns true because that depends on how libcurl
9
+ # was compiled on the system. We can only test that:
10
+ # 1. The method exists and works
11
+ # 2. If CURLAUTH_GSSNEGOTIATE is available, we can use it
12
+
13
+ # Check if CURLAUTH_GSSNEGOTIATE is available
14
+ if Curl.const_defined?(:CURLAUTH_GSSNEGOTIATE) && Curl.const_get(:CURLAUTH_GSSNEGOTIATE) != 0
15
+ # Test that we can use GSSNEGOTIATE auth type
16
+ c = Curl::Easy.new('http://example.com')
17
+ assert_nothing_raised do
18
+ c.http_auth_types = Curl::CURLAUTH_GSSNEGOTIATE
19
+ end
20
+
21
+ # The fix ensures spnego? won't incorrectly return false when
22
+ # older libcurl versions have GSSNEGOTIATE support
23
+ # (The actual return value depends on system libcurl compilation)
24
+ end
25
+
26
+ # The important fix is that the method now checks both constants
27
+ # This test passes if no exceptions are raised
28
+ assert true, "SPNEGO detection is working correctly"
29
+ end
30
+
31
+ def test_spnego_method_exists
32
+ # The method should always exist
33
+ assert Curl.respond_to?(:spnego?), "Curl should respond to spnego?"
34
+ end
35
+
36
+ def test_spnego_returns_boolean
37
+ # The method should return a boolean
38
+ result = Curl.spnego?
39
+ assert [true, false].include?(result), "Curl.spnego? should return true or false, got #{result.inspect}"
40
+ end
41
+ end
@@ -3,6 +3,7 @@
3
3
  # irb: multi = Curl::Multi.new
4
4
  # irb: exit
5
5
  # <main>:47140: [BUG] Bus Error
6
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
6
7
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','ext'))
7
8
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
8
9
  require 'curb'
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class BugRaiseOnCallback < Test::Unit::TestCase
4
+ include BugTestServerSetupTeardown
5
+
6
+ def setup
7
+ @port = unused_local_port
8
+ super
9
+ end
10
+
11
+ def test_on_complte
12
+ url = "http://127.0.0.1:#{@port}/test"
13
+ c = Curl::Easy.new(url)
14
+ did_raise = false
15
+ begin
16
+ c.on_complete do|x|
17
+ assert_equal url, x.url
18
+ raise "error complete" # this will get swallowed
19
+ end
20
+ c.perform
21
+ rescue => e
22
+ did_raise = true
23
+ end
24
+ assert did_raise, "we want to raise an exception if the ruby callbacks raise"
25
+
26
+ end
27
+
28
+ end
29
+
30
+ #test_on_debug