rs-httpclient 3.0.0.beta1

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +77 -0
  4. data/bin/jsonclient +85 -0
  5. data/lib/hexdump.rb +50 -0
  6. data/lib/http-access2/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/http-access2.rb +55 -0
  9. data/lib/httpclient/auth.rb +924 -0
  10. data/lib/httpclient/cacert.pem +3252 -0
  11. data/lib/httpclient/cacert1024.pem +3866 -0
  12. data/lib/httpclient/connection.rb +88 -0
  13. data/lib/httpclient/cookie.rb +220 -0
  14. data/lib/httpclient/http.rb +1082 -0
  15. data/lib/httpclient/include_client.rb +85 -0
  16. data/lib/httpclient/jruby_ssl_socket.rb +594 -0
  17. data/lib/httpclient/session.rb +960 -0
  18. data/lib/httpclient/ssl_config.rb +452 -0
  19. data/lib/httpclient/ssl_socket.rb +150 -0
  20. data/lib/httpclient/timeout.rb +140 -0
  21. data/lib/httpclient/util.rb +222 -0
  22. data/lib/httpclient/version.rb +3 -0
  23. data/lib/httpclient/webagent-cookie.rb +459 -0
  24. data/lib/httpclient.rb +1331 -0
  25. data/lib/jsonclient.rb +66 -0
  26. data/lib/oauthclient.rb +111 -0
  27. data/sample/async.rb +8 -0
  28. data/sample/auth.rb +11 -0
  29. data/sample/cookie.rb +18 -0
  30. data/sample/dav.rb +103 -0
  31. data/sample/generate_test_keys.rb +99 -0
  32. data/sample/howto.rb +49 -0
  33. data/sample/jsonclient.rb +67 -0
  34. data/sample/oauth_buzz.rb +57 -0
  35. data/sample/oauth_friendfeed.rb +59 -0
  36. data/sample/oauth_twitter.rb +61 -0
  37. data/sample/ssl/0cert.pem +22 -0
  38. data/sample/ssl/0key.pem +30 -0
  39. data/sample/ssl/1000cert.pem +19 -0
  40. data/sample/ssl/1000key.pem +18 -0
  41. data/sample/ssl/htdocs/index.html +10 -0
  42. data/sample/ssl/ssl_client.rb +22 -0
  43. data/sample/ssl/webrick_httpsd.rb +29 -0
  44. data/sample/stream.rb +21 -0
  45. data/sample/thread.rb +27 -0
  46. data/sample/wcat.rb +21 -0
  47. data/test/ca-chain.pem +40 -0
  48. data/test/ca.cert +20 -0
  49. data/test/ca.key +27 -0
  50. data/test/ca.srl +1 -0
  51. data/test/client-pass.key +30 -0
  52. data/test/client.cert +20 -0
  53. data/test/client.key +27 -0
  54. data/test/fixtures/verify.alt.cert +20 -0
  55. data/test/fixtures/verify.foo.cert +20 -0
  56. data/test/fixtures/verify.key +27 -0
  57. data/test/fixtures/verify.localhost.cert +20 -0
  58. data/test/helper.rb +129 -0
  59. data/test/htdigest +1 -0
  60. data/test/htpasswd +2 -0
  61. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  62. data/test/runner.rb +2 -0
  63. data/test/server.cert +20 -0
  64. data/test/server.key +27 -0
  65. data/test/sslsvr.rb +65 -0
  66. data/test/subca.cert +20 -0
  67. data/test/subca.key +27 -0
  68. data/test/subca.srl +1 -0
  69. data/test/test_auth.rb +496 -0
  70. data/test/test_cookie.rb +309 -0
  71. data/test/test_hexdump.rb +13 -0
  72. data/test/test_http-access2.rb +516 -0
  73. data/test/test_httpclient.rb +2144 -0
  74. data/test/test_include_client.rb +52 -0
  75. data/test/test_jsonclient.rb +98 -0
  76. data/test/test_ssl.rb +522 -0
  77. data/test/test_webagent-cookie.rb +465 -0
  78. metadata +130 -0
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('helper', File.dirname(__FILE__))
3
+
4
+ require 'httpclient/include_client'
5
+ class TestIncludeClient < Test::Unit::TestCase
6
+ class Widget
7
+ extend HTTPClient::IncludeClient
8
+
9
+ include_http_client("http://example.com") do |client|
10
+ client.cookie_manager = nil
11
+ client.agent_name = "iMonkey 4k"
12
+ end
13
+ end
14
+
15
+ class OtherWidget
16
+ extend HTTPClient::IncludeClient
17
+
18
+ include_http_client
19
+ include_http_client(:method_name => :other_http_client)
20
+ end
21
+
22
+ class UnrelatedBlankClass ; end
23
+
24
+ def test_client_class_level_singleton
25
+ assert_equal Widget.http_client.object_id, Widget.http_client.object_id
26
+
27
+ assert_equal Widget.http_client.object_id, Widget.new.http_client.object_id
28
+
29
+ assert_not_equal Widget.http_client.object_id, OtherWidget.http_client.object_id
30
+ end
31
+
32
+ def test_configured
33
+ assert_equal Widget.http_client.agent_name, "iMonkey 4k"
34
+ assert_nil Widget.http_client.cookie_manager
35
+ assert_equal Widget.http_client.proxy.to_s, "http://example.com"
36
+ end
37
+
38
+ def test_two_includes
39
+ assert_not_equal OtherWidget.http_client.object_id, OtherWidget.other_http_client.object_id
40
+
41
+ assert_equal OtherWidget.other_http_client.object_id, OtherWidget.new.other_http_client.object_id
42
+ end
43
+
44
+ # meta-programming gone wrong sometimes accidentally
45
+ # adds the class method to _everyone_, a mistake we've made before.
46
+ def test_not_infected_class_hieararchy
47
+ assert ! Class.respond_to?(:http_client)
48
+ assert ! UnrelatedBlankClass.respond_to?(:http_client)
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,98 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('helper', File.dirname(__FILE__))
3
+ require 'jsonclient'
4
+
5
+
6
+ class TestJSONClient < Test::Unit::TestCase
7
+ include Helper
8
+
9
+ def setup
10
+ super
11
+ setup_server
12
+ @client = JSONClient.new
13
+ end
14
+
15
+ def teardown
16
+ super
17
+ end
18
+
19
+ def test_post
20
+ res = @client.post(serverurl + 'json', {'a' => 1, 'b' => {'c' => 2}})
21
+ assert_equal(2, res.content['b']['c'])
22
+ assert_equal('application/json; charset=utf-8', res.content_type)
23
+ # #previous contains the original response
24
+ assert_equal(1, JSON.parse(res.previous.content)['a'])
25
+ end
26
+
27
+ def test_post_with_array
28
+ res = @client.post(serverurl + 'json', [{'a' => 1, 'b' => {'c' => 2}}])
29
+ assert_equal(2, res.content[0]['b']['c'])
30
+ assert_equal('application/json; charset=utf-8', res.content_type)
31
+ end
32
+
33
+ def test_post_with_header
34
+ res = @client.post(serverurl + 'json', :header => {'X-foo' => 'bar'}, :body => {'a' => 1, 'b' => {'c' => 2}})
35
+ assert_equal(2, res.content['b']['c'])
36
+ assert_equal('application/json; charset=utf-8', res.content_type)
37
+ end
38
+
39
+ def test_post_with_array_header
40
+ res = @client.post(serverurl + 'json', :header => [['X-foo', 'bar']], :body => {'a' => 1, 'b' => {'c' => 2}})
41
+ assert_equal(2, res.content['b']['c'])
42
+ assert_equal('application/json; charset=utf-8', res.content_type)
43
+ end
44
+
45
+ def test_post_non_json_body
46
+ res = @client.post(serverurl + 'json', 'a=b&c=d')
47
+ assert_equal('a=b&c=d', res.content)
48
+ assert_equal('application/x-www-form-urlencoded', res.content_type)
49
+ end
50
+
51
+ def test_put
52
+ res = @client.put(serverurl + 'json', {'a' => 1, 'b' => {'c' => 2}})
53
+ assert_equal(2, res.content['b']['c'])
54
+ assert_equal('application/json; charset=utf-8', res.content_type)
55
+ end
56
+
57
+ def test_get_not_affected
58
+ res = @client.get(serverurl + 'json', {'a' => 1, 'b' => {'c' => 2}})
59
+ assert_equal('', res.content)
60
+ assert_equal('', res.content_type)
61
+ end
62
+
63
+ def test_hash_header_not_modified
64
+ header = {'X-foo' => 'bar'}
65
+ _res = @client.post(serverurl, :header => header, :body => {'a' => 1, 'b' => {'c' => 2}})
66
+ assert_equal({'X-foo' => 'bar'}, header)
67
+ end
68
+
69
+ def test_array_header_not_modified
70
+ header = [['X-foo', 'bar']]
71
+ _res = @client.post(serverurl, :header => header, :body => {'a' => 1, 'b' => {'c' => 2}})
72
+ assert_equal([['X-foo', 'bar']], header)
73
+ end
74
+
75
+ class JSONServlet < WEBrick::HTTPServlet::AbstractServlet
76
+ def get_instance(*arg)
77
+ self
78
+ end
79
+
80
+ def service(req, res)
81
+ res['content-type'] = req['content-type']
82
+ res.body = req.body
83
+ end
84
+ end
85
+
86
+ def setup_server
87
+ @server = WEBrick::HTTPServer.new(
88
+ :BindAddress => "localhost",
89
+ :Logger => @logger,
90
+ :Port => 0,
91
+ :AccessLog => [],
92
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
93
+ )
94
+ @serverport = @server.config[:Port]
95
+ @server.mount('/json', JSONServlet.new(@server))
96
+ @server_thread = start_server_thread(@server)
97
+ end
98
+ end
data/test/test_ssl.rb ADDED
@@ -0,0 +1,522 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+ require 'webrick/https'
3
+ require 'time'
4
+
5
+
6
+ class TestSSL < Test::Unit::TestCase
7
+ include Helper
8
+
9
+ DIR = File.dirname(File.expand_path(__FILE__))
10
+
11
+ OPENSSL_VERSION = Integer(OpenSSL::OPENSSL_LIBRARY_VERSION.match(/OpenSSL (\d+)\./)[1])
12
+
13
+ def setup
14
+ super
15
+ @serverpid = @client = nil
16
+ @verify_callback_called = false
17
+ setup_server
18
+ setup_client
19
+ @url = "https://localhost:#{serverport}/hello"
20
+ end
21
+
22
+ def teardown
23
+ super
24
+ end
25
+
26
+ def path(filename)
27
+ File.expand_path(filename, DIR)
28
+ end
29
+
30
+ def read_path(filename)
31
+ File.read(path(filename))
32
+ end
33
+
34
+ def test_proxy_ssl
35
+ setup_proxyserver
36
+ escape_noproxy do
37
+ @client.proxy = proxyurl
38
+ @client.ssl_config.set_client_cert_file(path('client.cert'), path('client.key'))
39
+ @client.ssl_config.add_trust_ca(path('ca.cert'))
40
+ @client.ssl_config.add_trust_ca(path('subca.cert'))
41
+ @client.debug_dev = str = "".dup
42
+ assert_equal(200, @client.get(@url).status)
43
+ assert(/accept/ =~ @proxyio.string, 'proxy is not used')
44
+ assert(/Host: localhost:#{serverport}/ =~ str)
45
+ end
46
+ end
47
+
48
+ def test_options
49
+ cfg = @client.ssl_config
50
+ assert_nil(cfg.client_cert)
51
+ assert_nil(cfg.client_key)
52
+ assert_nil(cfg.client_ca)
53
+ assert_equal(OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT, cfg.verify_mode)
54
+ assert_nil(cfg.verify_callback)
55
+ assert_nil(cfg.timeout)
56
+ expected_options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
57
+ expected_options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
58
+ expected_options |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
59
+ assert_equal(expected_options, cfg.options)
60
+ assert_equal("ALL:!aNULL:!eNULL:!SSLv2", cfg.ciphers)
61
+ assert_instance_of(OpenSSL::X509::Store, cfg.cert_store)
62
+ end
63
+
64
+ unless defined?(HTTPClient::JRubySSLSocket)
65
+ # JRubySSLSocket does not support sync mode.
66
+ def test_sync
67
+ cfg = @client.ssl_config
68
+ cfg.set_client_cert_file(path('client.cert'), path('client.key'))
69
+ cfg.add_trust_ca(path('ca.cert'))
70
+ cfg.add_trust_ca(path('subca.cert'))
71
+ assert_equal("hello", @client.get_content(@url))
72
+
73
+ @client.socket_sync = false
74
+ @client.reset_all
75
+ assert_equal("hello", @client.get_content(@url))
76
+ end
77
+ end
78
+
79
+ def test_debug_dev
80
+ str = @client.debug_dev = ''.dup
81
+ cfg = @client.ssl_config
82
+ cfg.client_cert = path("client.cert")
83
+ cfg.client_key = path("client.key")
84
+ cfg.add_trust_ca(path('ca.cert'))
85
+ cfg.add_trust_ca(path('subca.cert'))
86
+ assert_equal("hello", @client.get_content(@url))
87
+ assert(str.scan(/^hello$/)[0])
88
+ end
89
+
90
+ def test_verification_without_httpclient
91
+ raw_cert = "-----BEGIN CERTIFICATE-----\nMIIDKDCCAhCgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES\nMBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X\nDTA0MDEzMTAzMTQ1OFoXDTM1MDEyMzAzMTQ1OFowZTELMAkGA1UEBgwCSlAxEjAQ\nBgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMRAwDgYDVQQDDAdleGFtcGxl\nMSIwIAYJKoZIhvcNAQkBDBNleGFtcGxlQGV4YW1wbGUub3JnMIGfMA0GCSqGSIb3\nDQEBAQUAA4GNADCBiQKBgQDRWssrK8Gyr+500hpLjCGR3+AHL8/hEJM5zKi/MgLW\njTkvsgOwbYwXOiNtAbR9y4/ucDq7EY+cMUMHES4uFaPTcOaAV0aZRmk8AgslN1tQ\ngNS6ew7/Luq3DcVeWkX8PYgR9VG0mD1MPfJ6+IFA5d3vKpdBkBgN4l46jjO0/2Xf\newIDAQABo4GPMIGMMAwGA1UdEwEB/wQCMAAwMQYJYIZIAYb4QgENBCQWIlJ1Ynkv\nT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOFvay0H7lr2\nxUx6waYEV2bVDYQhMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI\nKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADggEBABd2dYWqbDIWf5sWFvslezxJv8gI\nw64KCJBuyJAiDuf+oazr3016kMzAlt97KecLZDusGNagPrq02UX7YMoQFsWJBans\ncDtHrkM0al5r6/WGexNMgtYbNTYzt/IwodISGBgZ6dsOuhznwms+IBsTNDAvWeLP\nlt2tOqD8kEmjwMgn0GDRuKjs4EoboA3kMULb1p9akDV9ZESU3eOtpS5/G5J5msLI\n9WXbYBjcjvkLuJH9VsJhb+R58Vl0ViemvAHhPilSl1SPWVunGhv6FcIkdBEi1k9F\ne8BNMmsEjFiANiIRvpdLRbiGBt0KrKTndVfsmoKCvY48oCOvnzxtahFxfs8=\n-----END CERTIFICATE-----"
92
+ raw_ca_cert = "-----BEGIN CERTIFICATE-----\nMIID0DCCArigAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES\nMBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X\nDTA0MDEzMDAwNDIzMloXDTM2MDEyMjAwNDIzMlowPDELMAkGA1UEBgwCSlAxEjAQ\nBgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQswCQYDVQQDDAJDQTCCASIw\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbv0x42BTKFEQOE+KJ2XmiSdZpR\nwjzQLAkPLRnLB98tlzs4xo+y4RyY/rd5TT9UzBJTIhP8CJi5GbS1oXEerQXB3P0d\nL5oSSMwGGyuIzgZe5+vZ1kgzQxMEKMMKlzA73rbMd4Jx3u5+jdbP0EDrPYfXSvLY\nbS04n2aX7zrN3x5KdDrNBfwBio2/qeaaj4+9OxnwRvYP3WOvqdW0h329eMfHw0pi\nJI0drIVdsEqClUV4pebT/F+CPUPkEh/weySgo9wANockkYu5ujw2GbLFcO5LXxxm\ndEfcVr3r6t6zOA4bJwL0W/e6LBcrwiG/qPDFErhwtgTLYf6Er67SzLyA66UCAwEA\nAaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIBDQQkFiJSdWJ5L09w\nZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRJ7Xd380KzBV7f\nUSKIQ+O/vKbhDzAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0wW4AUSe13d/NCswVe\n31EiiEPjv7ym4Q+hQKQ+MDwxCzAJBgNVBAYMAkpQMRIwEAYDVQQKDAlKSU4uR1Iu\nSlAxDDAKBgNVBAsMA1JSUjELMAkGA1UEAwwCQ0GCAQAwDQYJKoZIhvcNAQEFBQAD\nggEBAIu/mfiez5XN5tn2jScgShPgHEFJBR0BTJBZF6xCk0jyqNx/g9HMj2ELCuK+\nr/Y7KFW5c5M3AQ+xWW0ZSc4kvzyTcV7yTVIwj2jZ9ddYMN3nupZFgBK1GB4Y05GY\nMJJFRkSu6d/Ph5ypzBVw2YMT/nsOo5VwMUGLgS7YVjU+u/HNWz80J3oO17mNZllj\nPvORJcnjwlroDnS58KoJ7GDgejv3ESWADvX1OHLE4cRkiQGeLoEU4pxdCxXRqX0U\nPbwIkZN9mXVcrmPHq8MWi4eC/V7hnbZETMHuWhUoiNdOEfsAXr3iP4KjyyRdwc7a\nd/xgcK06UVQRL/HbEYGiQL056mc=\n-----END CERTIFICATE-----"
93
+ ca_cert = ::OpenSSL::X509::Certificate.new(raw_ca_cert)
94
+ cert = ::OpenSSL::X509::Certificate.new(raw_cert)
95
+ store = ::OpenSSL::X509::Store.new
96
+ store.add_cert(ca_cert)
97
+ store.time = Time.new(2017, 01, 01)
98
+ assert(store.verify(cert), "Verify failed: #{store.error_string}, #{store.error}")
99
+ end
100
+
101
+ def test_verification
102
+ cfg = @client.ssl_config
103
+ cfg.verify_callback = method(:verify_callback).to_proc
104
+ begin
105
+ @verify_callback_called = false
106
+ @client.get(@url)
107
+ assert(false)
108
+ rescue OpenSSL::SSL::SSLError => ssle
109
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
110
+ assert(@verify_callback_called)
111
+ end
112
+ #
113
+ cfg.client_cert = path("client.cert")
114
+ cfg.client_key = path("client.key")
115
+ @verify_callback_called = false
116
+ begin
117
+ @client.get(@url)
118
+ assert(false)
119
+ rescue OpenSSL::SSL::SSLError => ssle
120
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
121
+ assert(@verify_callback_called)
122
+ end
123
+ #
124
+ cfg.add_trust_ca(path('ca.cert'))
125
+ @verify_callback_called = false
126
+ begin
127
+ @client.get(@url)
128
+ assert(false)
129
+ rescue OpenSSL::SSL::SSLError => ssle
130
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
131
+ assert(@verify_callback_called)
132
+ end
133
+ #
134
+ cfg.add_trust_ca(path('subca.cert'))
135
+ @verify_callback_called = false
136
+ assert_equal("hello", @client.get_content(@url))
137
+ assert(@verify_callback_called)
138
+ #
139
+ if false
140
+ # JRubySSLSocket does not support depth.
141
+ # Also on travis environment, verify_depth seems to not work properly.
142
+ cfg.verify_depth = 1 # 2 required: root-sub
143
+ @verify_callback_called = false
144
+ begin
145
+ @client.get(@url)
146
+ assert(false, "verify_depth is not supported? #{OpenSSL::OPENSSL_VERSION}")
147
+ rescue OpenSSL::SSL::SSLError => ssle
148
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
149
+ assert(@verify_callback_called)
150
+ end
151
+ #
152
+ cfg.verify_depth = 2 # 2 required: root-sub
153
+ @verify_callback_called = false
154
+ @client.get(@url)
155
+ assert(@verify_callback_called)
156
+ #
157
+ end
158
+ cfg.verify_depth = nil
159
+ cfg.cert_store = OpenSSL::X509::Store.new
160
+ cfg.verify_mode = OpenSSL::SSL::VERIFY_PEER
161
+ begin
162
+ @client.get_content(@url)
163
+ assert(false)
164
+ rescue OpenSSL::SSL::SSLError => ssle
165
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
166
+ end
167
+ #
168
+ cfg.verify_mode = nil
169
+ assert_equal("hello", @client.get_content(@url))
170
+ cfg.verify_mode = OpenSSL::SSL::VERIFY_NONE
171
+ assert_equal("hello", @client.get_content(@url))
172
+ end
173
+
174
+ def test_cert_store
175
+ cfg = @client.ssl_config
176
+ cfg.cert_store.add_cert(cert('ca.cert'))
177
+ begin
178
+ @client.get(@url)
179
+ assert(false)
180
+ rescue OpenSSL::SSL::SSLError => ssle
181
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
182
+ end
183
+ #
184
+ cfg.cert_store.add_cert(cert('subca.cert'))
185
+ assert_equal("hello", @client.get_content(@url))
186
+ cfg.clear_cert_store
187
+ begin
188
+ @client.get(@url)
189
+ assert(false)
190
+ rescue OpenSSL::SSL::SSLError => ssle
191
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
192
+ end
193
+ end
194
+
195
+ if defined?(HTTPClient::JRubySSLSocket)
196
+ def test_ciphers
197
+ cfg = @client.ssl_config
198
+ cfg.set_client_cert_file(path('client.cert'), path('client-pass.key'), 'pass4key')
199
+ cfg.add_trust_ca(path('ca.cert'))
200
+ cfg.add_trust_ca(path('subca.cert'))
201
+ cfg.timeout = 123
202
+ assert_equal("hello", @client.get_content(@url))
203
+ #
204
+ cfg.ciphers = []
205
+ begin
206
+ @client.get(@url)
207
+ assert(false)
208
+ rescue OpenSSL::SSL::SSLError => ssle
209
+ assert_match(/No appropriate protocol/, ssle.message)
210
+ end
211
+ #
212
+ cfg.ciphers = %w(TLS_RSA_WITH_AES_128_CBC_SHA)
213
+ assert_equal("hello", @client.get_content(@url))
214
+ #
215
+ cfg.ciphers = HTTPClient::SSLConfig::CIPHERS_DEFAULT
216
+ assert_equal("hello", @client.get_content(@url))
217
+ end
218
+
219
+ else
220
+
221
+ def test_ciphers
222
+ cfg = @client.ssl_config
223
+ cfg.set_client_cert_file(path('client.cert'), path('client-pass.key'), 'pass4key')
224
+ cfg.add_trust_ca(path('ca.cert'))
225
+ cfg.add_trust_ca(path('subca.cert'))
226
+ cfg.timeout = 123
227
+ assert_equal("hello", @client.get_content(@url))
228
+ #
229
+ cfg.ciphers = "!ALL"
230
+ begin
231
+ @client.get(@url)
232
+ assert(false)
233
+ rescue OpenSSL::SSL::SSLError => ssle
234
+ assert_match(/no cipher match/, ssle.message)
235
+ end
236
+ #
237
+ cfg.ciphers = "ALL"
238
+ assert_equal("hello", @client.get_content(@url))
239
+ #
240
+ cfg.ciphers = "DEFAULT"
241
+ assert_equal("hello", @client.get_content(@url))
242
+ end
243
+ end
244
+
245
+ def test_set_default_paths
246
+ assert_raise(OpenSSL::SSL::SSLError) do
247
+ @client.get(@url)
248
+ end
249
+ escape_env do
250
+ ENV['SSL_CERT_FILE'] = File.join(DIR, 'ca-chain.pem')
251
+ @client.ssl_config.set_default_paths
252
+ @client.get(@url)
253
+ end
254
+ end
255
+
256
+ def test_no_sslv3
257
+ omit('TODO: SSLv3 is not supported in many environments. re-enable when disable TLSv1')
258
+ teardown_server
259
+ setup_server_with_ssl_version(:SSLv3)
260
+ assert_raise(OpenSSL::SSL::SSLError) do
261
+ @client.ssl_config.verify_mode = nil
262
+ @client.get("https://localhost:#{serverport}/hello")
263
+ end
264
+ end
265
+
266
+ if OPENSSL_VERSION < 3
267
+ def test_allow_tlsv1
268
+ teardown_server
269
+ setup_server_with_ssl_version(:TLSv1)
270
+ assert_nothing_raised do
271
+ @client.ssl_config.verify_mode = nil
272
+ @client.get("https://localhost:#{serverport}/hello")
273
+ end
274
+ end
275
+ else
276
+ def test_disallow_tlsv1
277
+ teardown_server
278
+ setup_server_with_ssl_version(:TLSv1)
279
+ ssle = assert_raise(OpenSSL::SSL::SSLError) do
280
+ @client.ssl_config.verify_mode = nil
281
+ @client.get("https://localhost:#{serverport}/hello")
282
+ end
283
+ assert_match(/tlsv1 alert protocol version/, ssle.message)
284
+ end
285
+ end
286
+
287
+ def test_use_higher_TLS
288
+ omit('TODO: it does not pass with Java 7 or old openssl')
289
+ teardown_server
290
+ setup_server_with_ssl_version('TLSv1_2')
291
+ assert_nothing_raised do
292
+ @client.ssl_config.verify_mode = nil
293
+ @client.get("https://localhost:#{serverport}/hello")
294
+ # TODO: should check JRubySSLSocket.ssl_socket.getSession.getProtocol
295
+ # but it's not thread safe. How can I return protocol version to the caller?
296
+ end
297
+ end
298
+
299
+ def test_post_connection_check
300
+ teardown_server
301
+ setup_server_with_server_cert(
302
+ nil,
303
+ OpenSSL::X509::Certificate.new(read_path("fixtures/verify.localhost.cert")),
304
+ OpenSSL::PKey::RSA.new(read_path("fixtures/verify.key")),
305
+ )
306
+ @client.ssl_config.add_trust_ca(path("fixtures/verify.localhost.cert"))
307
+ assert_nothing_raised do
308
+ @client.get("https://localhost:#{serverport}/hello")
309
+ end
310
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
311
+ assert_nothing_raised do
312
+ @client.get("https://localhost:#{serverport}/hello")
313
+ end
314
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
315
+
316
+ teardown_server
317
+ setup_server_with_server_cert(
318
+ nil,
319
+ OpenSSL::X509::Certificate.new(read_path("fixtures/verify.foo.cert")),
320
+ OpenSSL::PKey::RSA.new(read_path("fixtures/verify.key")),
321
+ )
322
+ @client.ssl_config.add_trust_ca(path("fixtures/verify.foo.cert"))
323
+ assert_raises(OpenSSL::SSL::SSLError) do
324
+ @client.get("https://localhost:#{serverport}/hello")
325
+ end
326
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
327
+ assert_nothing_raised do
328
+ @client.get("https://localhost:#{serverport}/hello")
329
+ end
330
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
331
+
332
+ teardown_server
333
+ setup_server_with_server_cert(
334
+ nil,
335
+ OpenSSL::X509::Certificate.new(read_path("fixtures/verify.alt.cert")),
336
+ OpenSSL::PKey::RSA.new(read_path("fixtures/verify.key")),
337
+ )
338
+ @client.ssl_config.add_trust_ca(path("fixtures/verify.alt.cert"))
339
+ assert_raises(OpenSSL::SSL::SSLError) do
340
+ @client.get("https://localhost:#{serverport}/hello")
341
+ end
342
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
343
+ assert_nothing_raised do
344
+ @client.get("https://localhost:#{serverport}/hello")
345
+ end
346
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
347
+ end
348
+
349
+ def test_x509_store_add_cert_prepend
350
+ store = OpenSSL::X509::Store.new
351
+ assert_equal(store, store.add_cert(OpenSSL::X509::Certificate.new(read_path("fixtures/verify.localhost.cert"))))
352
+ end
353
+
354
+ def test_tcp_keepalive
355
+ @client.tcp_keepalive = true
356
+ @client.ssl_config.add_trust_ca(path('ca-chain.pem'))
357
+ @client.get_content(@url)
358
+
359
+ # expecting HTTP keepalive caches the socket
360
+ session = @client.instance_variable_get(:@session_manager).send(:get_cached_session, HTTPClient::Site.new(URI.parse(@url)))
361
+ socket = session.instance_variable_get(:@socket).instance_variable_get(:@socket)
362
+
363
+ assert_true(session.tcp_keepalive)
364
+ if RUBY_ENGINE == 'jruby'
365
+ assert_true(socket.getKeepAlive())
366
+ else
367
+ assert_equal(Socket::SO_KEEPALIVE, socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).optname)
368
+ end
369
+ end
370
+
371
+ def test_timeout
372
+ url = "https://localhost:#{serverport}/"
373
+ @client.ssl_config.add_trust_ca(path('ca-chain.pem'))
374
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=2'))
375
+ @client.receive_timeout = 1
376
+ @client.reset_all
377
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=0'))
378
+
379
+ start = Time.now
380
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
381
+ @client.get_content(url + 'sleep?sec=5')
382
+ end
383
+ if Time.now - start > 3
384
+ # before #342 it detected timeout when IO was freed
385
+ fail 'timeout does not work'
386
+ end
387
+
388
+ @client.receive_timeout = 3
389
+ @client.reset_all
390
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=2'))
391
+ end
392
+
393
+ private
394
+
395
+ def cert(filename)
396
+ OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename)))
397
+ end
398
+
399
+ def key(filename)
400
+ OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename)))
401
+ end
402
+
403
+ def q(str)
404
+ %Q["#{str}"]
405
+ end
406
+
407
+ def setup_server
408
+ logger = Logger.new(STDERR)
409
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
410
+ @server = WEBrick::HTTPServer.new(
411
+ :BindAddress => "localhost",
412
+ :Logger => logger,
413
+ :Port => 0,
414
+ :AccessLog => [],
415
+ :DocumentRoot => DIR,
416
+ :SSLEnable => true,
417
+ :SSLCACertificateFile => File.join(DIR, 'ca.cert'),
418
+ :SSLCertificate => cert('server.cert'),
419
+ :SSLPrivateKey => key('server.key'),
420
+ :SSLVerifyClient => nil, #OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT|OpenSSL::SSL::VERIFY_PEER,
421
+ :SSLClientCA => cert('ca.cert'),
422
+ :SSLCertName => nil
423
+ )
424
+ @serverport = @server.config[:Port]
425
+ [:hello, :sleep].each do |sym|
426
+ @server.mount(
427
+ "/#{sym}",
428
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
429
+ )
430
+ end
431
+ @server_thread = start_server_thread(@server)
432
+ end
433
+
434
+ def setup_server_with_ssl_version(ssl_version)
435
+ # JRubyOpenSSL does not support "TLSv1_2" as an known version, and some JCE provides TLS v1.2 as "TLSv1.2" not "TLSv1_2"
436
+ if RUBY_ENGINE == 'jruby' && ['TLSv1_1', 'TLSv1_2'].include?(ssl_version)
437
+ ssl_version = ssl_version.tr('_', '.')
438
+ end
439
+ logger = Logger.new(STDERR)
440
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
441
+ @server = WEBrick::HTTPServer.new(
442
+ :BindAddress => "localhost",
443
+ :Logger => logger,
444
+ :Port => 0,
445
+ :AccessLog => [],
446
+ :DocumentRoot => DIR,
447
+ :SSLEnable => true,
448
+ :SSLCACertificateFile => File.join(DIR, 'ca.cert'),
449
+ :SSLCertificate => cert('server.cert'),
450
+ :SSLPrivateKey => key('server.key')
451
+ )
452
+ @server.ssl_context.ssl_version = ssl_version
453
+ @serverport = @server.config[:Port]
454
+ [:hello].each do |sym|
455
+ @server.mount(
456
+ "/#{sym}",
457
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
458
+ )
459
+ end
460
+ @server_thread = start_server_thread(@server)
461
+ end
462
+
463
+ def setup_server_with_server_cert(ca_cert, server_cert, server_key)
464
+ logger = Logger.new(STDERR)
465
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
466
+ @server = WEBrick::HTTPServer.new(
467
+ :BindAddress => "localhost",
468
+ :Logger => logger,
469
+ :Port => 0,
470
+ :AccessLog => [],
471
+ :DocumentRoot => DIR,
472
+ :SSLEnable => true,
473
+ :SSLCACertificateFile => ca_cert,
474
+ :SSLCertificate => server_cert,
475
+ :SSLPrivateKey => server_key,
476
+ :SSLVerifyClient => nil,
477
+ :SSLClientCA => nil,
478
+ :SSLCertName => nil
479
+ )
480
+ @serverport = @server.config[:Port]
481
+ [:hello].each do |sym|
482
+ @server.mount(
483
+ "/#{sym}",
484
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
485
+ )
486
+ end
487
+ @server_thread = start_server_thread(@server)
488
+ end
489
+
490
+ def do_hello(req, res)
491
+ res['content-type'] = 'text/html'
492
+ res.body = "hello"
493
+ end
494
+
495
+ def do_sleep(req, res)
496
+ sec = req.query['sec'].to_i
497
+ sleep sec
498
+ res['content-type'] = 'text/html'
499
+ res.body = "sleep"
500
+ end
501
+
502
+ def start_server_thread(server)
503
+ t = Thread.new {
504
+ Thread.current.abort_on_exception = true
505
+ server.start
506
+ }
507
+ while server.status != :Running
508
+ sleep 0.1
509
+ unless t.alive?
510
+ t.join
511
+ raise
512
+ end
513
+ end
514
+ t
515
+ end
516
+
517
+ def verify_callback(ok, cert)
518
+ @verify_callback_called = true
519
+ p ["client", ok, cert] if $DEBUG
520
+ ok
521
+ end
522
+ end