httpclient 2.1.5 → 2.8.3

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 (71) 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.rb +6 -4
  7. data/lib/httpclient/auth.rb +575 -173
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +6 -2
  11. data/lib/httpclient/cookie.rb +162 -504
  12. data/lib/httpclient/http.rb +334 -119
  13. data/lib/httpclient/include_client.rb +85 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +385 -288
  16. data/lib/httpclient/ssl_config.rb +195 -155
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +14 -10
  19. data/lib/httpclient/util.rb +142 -6
  20. data/lib/httpclient/version.rb +3 -0
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/httpclient.rb +509 -202
  23. data/lib/jsonclient.rb +63 -0
  24. data/lib/oauthclient.rb +111 -0
  25. data/sample/async.rb +8 -0
  26. data/sample/auth.rb +11 -0
  27. data/sample/cookie.rb +18 -0
  28. data/sample/dav.rb +103 -0
  29. data/sample/howto.rb +49 -0
  30. data/sample/jsonclient.rb +67 -0
  31. data/sample/oauth_buzz.rb +57 -0
  32. data/sample/oauth_friendfeed.rb +59 -0
  33. data/sample/oauth_twitter.rb +61 -0
  34. data/sample/ssl/0cert.pem +22 -0
  35. data/sample/ssl/0key.pem +30 -0
  36. data/sample/ssl/1000cert.pem +19 -0
  37. data/sample/ssl/1000key.pem +18 -0
  38. data/sample/ssl/htdocs/index.html +10 -0
  39. data/sample/ssl/ssl_client.rb +22 -0
  40. data/sample/ssl/webrick_httpsd.rb +29 -0
  41. data/sample/stream.rb +21 -0
  42. data/sample/thread.rb +27 -0
  43. data/sample/wcat.rb +21 -0
  44. data/test/ca-chain.pem +44 -0
  45. data/test/ca.cert +23 -0
  46. data/test/client-pass.key +18 -0
  47. data/test/client.cert +19 -0
  48. data/test/client.key +15 -0
  49. data/test/helper.rb +131 -0
  50. data/test/htdigest +1 -0
  51. data/test/htpasswd +2 -0
  52. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  53. data/test/runner.rb +2 -0
  54. data/test/server.cert +19 -0
  55. data/test/server.key +15 -0
  56. data/test/sslsvr.rb +65 -0
  57. data/test/subca.cert +21 -0
  58. data/test/test_auth.rb +492 -0
  59. data/test/test_cookie.rb +309 -0
  60. data/test/test_hexdump.rb +14 -0
  61. data/test/test_http-access2.rb +508 -0
  62. data/test/test_httpclient.rb +2145 -0
  63. data/test/test_include_client.rb +52 -0
  64. data/test/test_jsonclient.rb +80 -0
  65. data/test/test_ssl.rb +559 -0
  66. data/test/test_webagent-cookie.rb +465 -0
  67. metadata +85 -44
  68. data/lib/httpclient/auth.rb.orig +0 -513
  69. data/lib/httpclient/cacert.p7s +0 -1579
  70. data/lib/httpclient.rb.orig +0 -1020
  71. data/lib/tags +0 -908
@@ -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,80 @@
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_header
28
+ res = @client.post(serverurl + 'json', :header => {'X-foo' => 'bar'}, :body => {'a' => 1, 'b' => {'c' => 2}})
29
+ assert_equal(2, res.content['b']['c'])
30
+ assert_equal('application/json; charset=utf-8', res.content_type)
31
+ end
32
+
33
+ def test_post_with_array_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_non_json_body
40
+ res = @client.post(serverurl + 'json', 'a=b&c=d')
41
+ assert_equal('a=b&c=d', res.content)
42
+ assert_equal('application/x-www-form-urlencoded', res.content_type)
43
+ end
44
+
45
+ def test_put
46
+ res = @client.put(serverurl + 'json', {'a' => 1, 'b' => {'c' => 2}})
47
+ assert_equal(2, res.content['b']['c'])
48
+ assert_equal('application/json; charset=utf-8', res.content_type)
49
+ end
50
+
51
+ def test_get_not_affected
52
+ res = @client.get(serverurl + 'json', {'a' => 1, 'b' => {'c' => 2}})
53
+ assert_equal('', res.content)
54
+ assert_equal('', res.content_type)
55
+ end
56
+
57
+ class JSONServlet < WEBrick::HTTPServlet::AbstractServlet
58
+ def get_instance(*arg)
59
+ self
60
+ end
61
+
62
+ def service(req, res)
63
+ res['content-type'] = req['content-type']
64
+ res.body = req.body
65
+ end
66
+ end
67
+
68
+ def setup_server
69
+ @server = WEBrick::HTTPServer.new(
70
+ :BindAddress => "localhost",
71
+ :Logger => @logger,
72
+ :Port => 0,
73
+ :AccessLog => [],
74
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
75
+ )
76
+ @serverport = @server.config[:Port]
77
+ @server.mount('/json', JSONServlet.new(@server))
78
+ @server_thread = start_server_thread(@server)
79
+ end
80
+ end
data/test/test_ssl.rb ADDED
@@ -0,0 +1,559 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+ require 'webrick/https'
3
+
4
+
5
+ class TestSSL < Test::Unit::TestCase
6
+ include Helper
7
+
8
+ DIR = File.dirname(File.expand_path(__FILE__))
9
+
10
+ def setup
11
+ super
12
+ @serverpid = @client = nil
13
+ @verify_callback_called = false
14
+ setup_server
15
+ setup_client
16
+ @url = "https://localhost:#{serverport}/hello"
17
+ end
18
+
19
+ def teardown
20
+ super
21
+ end
22
+
23
+ def path(filename)
24
+ File.expand_path(filename, DIR)
25
+ end
26
+
27
+ def test_proxy_ssl
28
+ setup_proxyserver
29
+ escape_noproxy do
30
+ @client.proxy = proxyurl
31
+ @client.ssl_config.set_client_cert_file(path('client.cert'), path('client.key'))
32
+ @client.ssl_config.add_trust_ca(path('ca.cert'))
33
+ @client.ssl_config.add_trust_ca(path('subca.cert'))
34
+ @client.debug_dev = str = ""
35
+ assert_equal(200, @client.get(@url).status)
36
+ assert(/accept/ =~ @proxyio.string, 'proxy is not used')
37
+ assert(/Host: localhost:#{serverport}/ =~ str)
38
+ end
39
+ end
40
+
41
+ def test_options
42
+ cfg = @client.ssl_config
43
+ assert_nil(cfg.client_cert)
44
+ assert_nil(cfg.client_key)
45
+ assert_nil(cfg.client_ca)
46
+ assert_equal(OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT, cfg.verify_mode)
47
+ assert_nil(cfg.verify_callback)
48
+ assert_nil(cfg.timeout)
49
+ expected_options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
50
+ expected_options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
51
+ expected_options |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
52
+ assert_equal(expected_options, cfg.options)
53
+ assert_equal("ALL:!aNULL:!eNULL:!SSLv2", cfg.ciphers)
54
+ assert_instance_of(OpenSSL::X509::Store, cfg.cert_store)
55
+ end
56
+
57
+ unless defined?(HTTPClient::JRubySSLSocket)
58
+ # JRubySSLSocket does not support sync mode.
59
+ def test_sync
60
+ cfg = @client.ssl_config
61
+ cfg.set_client_cert_file(path('client.cert'), path('client.key'))
62
+ cfg.add_trust_ca(path('ca.cert'))
63
+ cfg.add_trust_ca(path('subca.cert'))
64
+ assert_equal("hello", @client.get_content(@url))
65
+
66
+ @client.socket_sync = false
67
+ @client.reset_all
68
+ assert_equal("hello", @client.get_content(@url))
69
+ end
70
+ end
71
+
72
+ def test_debug_dev
73
+ str = @client.debug_dev = ''
74
+ cfg = @client.ssl_config
75
+ cfg.client_cert = path("client.cert")
76
+ cfg.client_key = path("client.key")
77
+ cfg.add_trust_ca(path('ca.cert'))
78
+ cfg.add_trust_ca(path('subca.cert'))
79
+ assert_equal("hello", @client.get_content(@url))
80
+ assert(str.scan(/^hello$/)[0])
81
+ end
82
+
83
+ def test_verification_without_httpclient
84
+ raw_cert = "-----BEGIN CERTIFICATE-----\nMIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBCMRMwEQYKCZImiZPyLGQB\nGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMRAwDgYDVQQDDAdSdWJ5\nIENBMB4XDTE2MDgxMDE3MjEzNFoXDTE3MDgxMDE3MjEzNFowSzETMBEGCgmSJomT\n8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1YnktbGFuZzEZMBcGA1UEAwwQ\nUnVieSBjZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAJCfsSXpSMpmZCVa+ZCM+QDgomnhDlvnrGDq6pasTaIspGTXgws+7r8Dt/cNe6EH\nHJpRH2cGRiO4yPcfcT9eS4X7k8OC4f33wHfACOmLu6LeoNE8ujmSk6L6WzLUI+sE\nnLZbFrXxoAo4XHsm8vEG9C+jEoXZ1p+47wrAGaDwDQTnzlMy4dT9pRQEJP2G/Rry\nUkuZn8SUWmh3/YS78iaSzsNF1cgE1ealHOrPPFDjiCGDaH/LHyUPYlbFSLZ/B7Qx\nLxi5sePLcywWq/EJrmWpgeVTDjtNijsdKv/A3qkY+fm/oD0pzt7XsfJaP9YKNyJO\nQFdxWZeiPcDF+Hwf+IwSr+kCAwEAAaMxMC8wDgYDVR0PAQH/BAQDAgeAMB0GA1Ud\nDgQWBBQNvzYzJyXemGhxbA8NMXLolDnPyjANBgkqhkiG9w0BAQsFAAOCAQEARIJV\noKejGlOTn71QutnNnu07UtTu0IHs6YqjYzzND+m4JXLN+wvYm72AFUG0b1L7dRg0\niK8XjQrlNQNVqP1Mc6tffchy20neOPOHeiO6qTdRU8P2S8D3Uwe+1qhgxjfE+cWc\nwZmWxYK4HA8c58PxWMqrkr2QqXDplG9KWLvOgrtPGiLLZcQSKhvvB63QzItHBDU6\nRayiJY3oPkK/HrIvFlySqFqzWmuyknkciOFywEHQMz/tcSFJ2QFpPj/tBz9VXohH\nZ8KscmfhZrTPBjo+ky1lz/WraWoz4LMiLnkC2ABczWLRSawu+v3Irx1NFJngt05e\npqwtqIUeg7j+JLiTaA==\n-----END CERTIFICATE-----"
85
+ raw_ca_cert = "-----BEGIN CERTIFICATE-----\nMIIDYjCCAkqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBCMRMwEQYKCZImiZPyLGQB\nGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMRAwDgYDVQQDDAdSdWJ5\nIENBMB4XDTE2MDgxMDE3MjA1NFoXDTE4MDgxMDE3MjA1NFowQjETMBEGCgmSJomT\n8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1YnktbGFuZzEQMA4GA1UEAwwH\nUnVieSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKGwyM3Ejtl\npo7CqaDlS71gDZn3gm6IwWpmRMLJofSI9LCwAbjijSC2HvO0xUWoYW40FbzjnnEi\ngszsWyPwuQIx9t0bhuAyllNIfImmkaQkrikXKBKzia4jPnbc4iXPnfjuThjESFWl\ntfbN6y1B5TjKhD1KelfakUO+iMu8WlIA9NKQZYfJ/F3QSpP5Iqb3KN/jVifFbDV8\nbAl3Ln4rT2kTCKrZZcl1jmWsJv8jBw6+P7hk0/Mu0JeHAITsjbNbpHd8UXpCfbVs\nsNGZrBU4uJdZ2YTG+Y27/t25jFNQwb+TWbvig7rfdX2sjssuxa00BBxarC08tIVj\nZprM37KcNn8CAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAQYwHQYDVR0OBBYEFA2/NjMnJd6YaHFsDw0xcuiUOc/KMB8GA1UdIwQYMBYEFA2/\nNjMnJd6YaHFsDw0xcuiUOc/KMA0GCSqGSIb3DQEBCwUAA4IBAQAJSOw49XqvUll0\n3vU9EAO6yUdeZSsQENIfYbRMQgapbnN1vTyrUjPZkGC5hIE1pVdoHtEoUEICxIwy\nr6BKxiSLBDLp+rvIuDdzMkXIWdUVvTZguVRyKtM2gfnpsPLpVnv+stBmAW2SMyxm\nkymhOpkjdv3He+45uorB3tdfBS9VVomDEUJdg38UE1b5eXRQ3D6gG0iCPFzKszXg\nLoAYhGxtjCJaKlbzduMK0YO6aelgW1+XnVIKcA7DJ9egk5d/dFZBPFfwumwr9hTH\nh7/fp3Fr87weI+CkfmFyJZrsEBlXJBVuvPesMVHTh3Whm5kmCdWcBJU0QmSq42ZL\n72U0PXLR\n-----END CERTIFICATE-----"
86
+ ca_cert = ::OpenSSL::X509::Certificate.new(raw_ca_cert)
87
+ cert = ::OpenSSL::X509::Certificate.new(raw_cert)
88
+ store = ::OpenSSL::X509::Store.new
89
+ store.add_cert(ca_cert)
90
+ assert(store.verify(cert))
91
+ end
92
+
93
+ def test_verification
94
+ cfg = @client.ssl_config
95
+ cfg.verify_callback = method(:verify_callback).to_proc
96
+ begin
97
+ @verify_callback_called = false
98
+ @client.get(@url)
99
+ assert(false)
100
+ rescue OpenSSL::SSL::SSLError => ssle
101
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
102
+ assert(@verify_callback_called)
103
+ end
104
+ #
105
+ cfg.client_cert = path("client.cert")
106
+ cfg.client_key = path("client.key")
107
+ @verify_callback_called = false
108
+ begin
109
+ @client.get(@url)
110
+ assert(false)
111
+ rescue OpenSSL::SSL::SSLError => ssle
112
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
113
+ assert(@verify_callback_called)
114
+ end
115
+ #
116
+ cfg.add_trust_ca(path('ca.cert'))
117
+ @verify_callback_called = false
118
+ begin
119
+ @client.get(@url)
120
+ assert(false)
121
+ rescue OpenSSL::SSL::SSLError => ssle
122
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
123
+ assert(@verify_callback_called)
124
+ end
125
+ #
126
+ cfg.add_trust_ca(path('subca.cert'))
127
+ @verify_callback_called = false
128
+ assert_equal("hello", @client.get_content(@url))
129
+ assert(@verify_callback_called)
130
+ #
131
+ if false
132
+ # JRubySSLSocket does not support depth.
133
+ # Also on travis environment, verify_depth seems to not work properly.
134
+ cfg.verify_depth = 1 # 2 required: root-sub
135
+ @verify_callback_called = false
136
+ begin
137
+ @client.get(@url)
138
+ assert(false, "verify_depth is not supported? #{OpenSSL::OPENSSL_VERSION}")
139
+ rescue OpenSSL::SSL::SSLError => ssle
140
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
141
+ assert(@verify_callback_called)
142
+ end
143
+ #
144
+ cfg.verify_depth = 2 # 2 required: root-sub
145
+ @verify_callback_called = false
146
+ @client.get(@url)
147
+ assert(@verify_callback_called)
148
+ #
149
+ end
150
+ cfg.verify_depth = nil
151
+ cfg.cert_store = OpenSSL::X509::Store.new
152
+ cfg.verify_mode = OpenSSL::SSL::VERIFY_PEER
153
+ begin
154
+ @client.get_content(@url)
155
+ assert(false)
156
+ rescue OpenSSL::SSL::SSLError => ssle
157
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
158
+ end
159
+ #
160
+ cfg.verify_mode = nil
161
+ assert_equal("hello", @client.get_content(@url))
162
+ cfg.verify_mode = OpenSSL::SSL::VERIFY_NONE
163
+ assert_equal("hello", @client.get_content(@url))
164
+ end
165
+
166
+ def test_cert_store
167
+ cfg = @client.ssl_config
168
+ cfg.cert_store.add_cert(cert('ca.cert'))
169
+ begin
170
+ @client.get(@url)
171
+ assert(false)
172
+ rescue OpenSSL::SSL::SSLError => ssle
173
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
174
+ end
175
+ #
176
+ cfg.cert_store.add_cert(cert('subca.cert'))
177
+ assert_equal("hello", @client.get_content(@url))
178
+ cfg.clear_cert_store
179
+ begin
180
+ @client.get(@url)
181
+ assert(false)
182
+ rescue OpenSSL::SSL::SSLError => ssle
183
+ assert_match(/(certificate verify failed|unable to find valid certification path to requested target)/, ssle.message)
184
+ end
185
+ end
186
+
187
+ if defined?(HTTPClient::JRubySSLSocket)
188
+ def test_ciphers
189
+ cfg = @client.ssl_config
190
+ cfg.set_client_cert_file(path('client.cert'), path('client-pass.key'), 'pass4key')
191
+ cfg.add_trust_ca(path('ca.cert'))
192
+ cfg.add_trust_ca(path('subca.cert'))
193
+ cfg.timeout = 123
194
+ assert_equal("hello", @client.get_content(@url))
195
+ #
196
+ cfg.ciphers = []
197
+ begin
198
+ @client.get(@url)
199
+ assert(false)
200
+ rescue OpenSSL::SSL::SSLError => ssle
201
+ assert_match(/No appropriate protocol/, ssle.message)
202
+ end
203
+ #
204
+ cfg.ciphers = %w(TLS_RSA_WITH_AES_128_CBC_SHA)
205
+ assert_equal("hello", @client.get_content(@url))
206
+ #
207
+ cfg.ciphers = HTTPClient::SSLConfig::CIPHERS_DEFAULT
208
+ assert_equal("hello", @client.get_content(@url))
209
+ end
210
+
211
+ else
212
+
213
+ def test_ciphers
214
+ cfg = @client.ssl_config
215
+ cfg.set_client_cert_file(path('client.cert'), path('client-pass.key'), 'pass4key')
216
+ cfg.add_trust_ca(path('ca.cert'))
217
+ cfg.add_trust_ca(path('subca.cert'))
218
+ cfg.timeout = 123
219
+ assert_equal("hello", @client.get_content(@url))
220
+ #
221
+ cfg.ciphers = "!ALL"
222
+ begin
223
+ @client.get(@url)
224
+ assert(false)
225
+ rescue OpenSSL::SSL::SSLError => ssle
226
+ assert_match(/no cipher match/, ssle.message)
227
+ end
228
+ #
229
+ cfg.ciphers = "ALL"
230
+ assert_equal("hello", @client.get_content(@url))
231
+ #
232
+ cfg.ciphers = "DEFAULT"
233
+ assert_equal("hello", @client.get_content(@url))
234
+ end
235
+ end
236
+
237
+ def test_set_default_paths
238
+ assert_raise(OpenSSL::SSL::SSLError) do
239
+ @client.get(@url)
240
+ end
241
+ escape_env do
242
+ ENV['SSL_CERT_FILE'] = File.join(DIR, 'ca-chain.pem')
243
+ @client.ssl_config.set_default_paths
244
+ @client.get(@url)
245
+ end
246
+ end
247
+
248
+ def test_no_sslv3
249
+ teardown_server
250
+ setup_server_with_ssl_version(:SSLv3)
251
+ assert_raise(OpenSSL::SSL::SSLError) do
252
+ @client.ssl_config.verify_mode = nil
253
+ @client.get("https://localhost:#{serverport}/hello")
254
+ end
255
+ end
256
+
257
+ def test_allow_tlsv1
258
+ teardown_server
259
+ setup_server_with_ssl_version(:TLSv1)
260
+ assert_nothing_raised do
261
+ @client.ssl_config.verify_mode = nil
262
+ @client.get("https://localhost:#{serverport}/hello")
263
+ end
264
+ end
265
+
266
+ def test_use_higher_TLS
267
+ omit('TODO: it does not pass with Java 7 or old openssl ')
268
+ teardown_server
269
+ setup_server_with_ssl_version('TLSv1_2')
270
+ assert_nothing_raised do
271
+ @client.ssl_config.verify_mode = nil
272
+ @client.get("https://localhost:#{serverport}/hello")
273
+ # TODO: should check JRubySSLSocket.ssl_socket.getSession.getProtocol
274
+ # but it's not thread safe. How can I return protocol version to the caller?
275
+ end
276
+ end
277
+
278
+ VERIFY_TEST_CERT_LOCALHOST = OpenSSL::X509::Certificate.new(<<-EOS)
279
+ -----BEGIN CERTIFICATE-----
280
+ MIIB9jCCAV+gAwIBAgIJAIH8Gsm4PcNKMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
281
+ BAMMCWxvY2FsaG9zdDAeFw0xNjA4MTgxMDI2MDVaFw00NDAxMDMxMDI2MDVaMBQx
282
+ EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
283
+ p7D8q0lcx5EZEV5+zPnQsxrbft5xyhH/MCStbH46DRATGPNSOaLRCG5r8gTKQzpD
284
+ 4swGrQFYe2ienQ+7o4aEHErsXp4O/EmDKeiXWWrMqPr23r3HOBDebuynC/sCwy7N
285
+ epnX9u1VLB03eo+suj4d86OoOF+o11t9ZP+GA29Rsf8CAwEAAaNQME4wHQYDVR0O
286
+ BBYEFIxsJuPVvd5KKFcAvHGSeKSsWiUJMB8GA1UdIwQYMBaAFIxsJuPVvd5KKFcA
287
+ vHGSeKSsWiUJMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAMJaVCrrM
288
+ SM2I06Vr4BL+jtDFhZh3HmJFEDpwEFQ5Y9hduwdUGRBGCpkuea3fE2FKwWW9gLM1
289
+ w7rFMzYFtCEqm78dJWIU79MRy0wjO4LgtYfoikgBh6JKWuV5ed/+L3sLyLG0ZTtv
290
+ lrD7lzDtXgwvj007PxDoYRp3JwYzKRmTbH8=
291
+ -----END CERTIFICATE-----
292
+ EOS
293
+
294
+ VERIFY_TEST_CERT_FOO_DOMAIN = OpenSSL::X509::Certificate.new(<<-EOS)
295
+ -----BEGIN CERTIFICATE-----
296
+ MIIB8jCCAVugAwIBAgIJAL/od7Whx7VTMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
297
+ BAMMB2Zvby5jb20wHhcNMTYwODE4MTAyMzUyWhcNNDQwMTAzMTAyMzUyWjASMRAw
298
+ DgYDVQQDDAdmb28uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnsPyr
299
+ SVzHkRkRXn7M+dCzGtt+3nHKEf8wJK1sfjoNEBMY81I5otEIbmvyBMpDOkPizAat
300
+ AVh7aJ6dD7ujhoQcSuxeng78SYMp6JdZasyo+vbevcc4EN5u7KcL+wLDLs16mdf2
301
+ 7VUsHTd6j6y6Ph3zo6g4X6jXW31k/4YDb1Gx/wIDAQABo1AwTjAdBgNVHQ4EFgQU
302
+ jGwm49W93kooVwC8cZJ4pKxaJQkwHwYDVR0jBBgwFoAUjGwm49W93kooVwC8cZJ4
303
+ pKxaJQkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQCVKTvfxx+yezuR
304
+ 5WpVKw1E9qabKOYFB5TqdHMHreRubMJTaoZC+YzhcCwtyLlAA9+axKINAiMM8T+z
305
+ jjfOHQSa2GS2TaaVDJWmXIgsAlEbjd2BEiQF0LZYGJRG9pyq0WbTV+CyFdrghjcO
306
+ xX/t7OG7NfOG9dhv3J+5SX10S5V5Dg==
307
+ -----END CERTIFICATE-----
308
+ EOS
309
+
310
+ VERIFY_TEST_CERT_ALT_NAME = OpenSSL::X509::Certificate.new(<<-EOS)
311
+ -----BEGIN CERTIFICATE-----
312
+ MIICDDCCAXWgAwIBAgIJAOxXY4nOwxhGMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
313
+ BAMMCWxvY2FsaG9zdDAeFw0xNjA4MTgxMDM0NTJaFw00NDAxMDMxMDM0NTJaMBQx
314
+ EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
315
+ p7D8q0lcx5EZEV5+zPnQsxrbft5xyhH/MCStbH46DRATGPNSOaLRCG5r8gTKQzpD
316
+ 4swGrQFYe2ienQ+7o4aEHErsXp4O/EmDKeiXWWrMqPr23r3HOBDebuynC/sCwy7N
317
+ epnX9u1VLB03eo+suj4d86OoOF+o11t9ZP+GA29Rsf8CAwEAAaNmMGQwFAYDVR0R
318
+ BA0wC4IJKi5mb28uY29tMB0GA1UdDgQWBBSMbCbj1b3eSihXALxxknikrFolCTAf
319
+ BgNVHSMEGDAWgBSMbCbj1b3eSihXALxxknikrFolCTAMBgNVHRMEBTADAQH/MA0G
320
+ CSqGSIb3DQEBCwUAA4GBADJlKNFuOnsDIhHGW72HuQw4naN6lM3eZE9JJ+UF/XIF
321
+ ghGtgqw+00Yy5wMFc1K2Wm4p5NymmDfC/P1FOe34bpxt9/IWm6mEoIWoodC3N4Cm
322
+ PtnSS1/CRWzVIPGMglTGGDcUc70tfeAWgyTxgcNQd4vTFtnN0f0RDdaXa8kfKMTw
323
+ -----END CERTIFICATE-----
324
+ EOS
325
+
326
+ VERIFY_TEST_PKEY = OpenSSL::PKey::RSA.new(<<-EOS)
327
+ -----BEGIN RSA PRIVATE KEY-----
328
+ MIICXQIBAAKBgQCnsPyrSVzHkRkRXn7M+dCzGtt+3nHKEf8wJK1sfjoNEBMY81I5
329
+ otEIbmvyBMpDOkPizAatAVh7aJ6dD7ujhoQcSuxeng78SYMp6JdZasyo+vbevcc4
330
+ EN5u7KcL+wLDLs16mdf27VUsHTd6j6y6Ph3zo6g4X6jXW31k/4YDb1Gx/wIDAQAB
331
+ AoGAe0RHx+WKtQx8/96VmTl951qzxMPho2etTYd4kAsNwzJwx2N9qu57eBYrdWF+
332
+ CQMYievucFhP4Y+bINtC1Eb6btz9TCUwjCfeIxfGRoFf3cxVmxlsRJJmN1kSZlu1
333
+ yYlcMVuP4noeFIMQBRrt5pyLCx2Z9A01NCQT4Y6VoREBIeECQQDWeNhsL6xkrmdB
334
+ M9+zl+SqHdNKhgKwMdp74+UNnAV9I8GB7bGlOWhc83aqMLgS+JBDFXcmNF/KawTR
335
+ zcnkod5xAkEAyClFgr3lZQSnwUwoA/AOcyW0+H63taaaXS/g8n3H8ENK6kL4ldUx
336
+ IgCk2ekbQ5Y3S2WScIGXNxMOza9MlsOvbwJAPUtoPvMZB+U4KVBT/JXKijvf6QqH
337
+ tidpU8L78XnHr84KPcHa5WeUxgvmvBkUYoebYzC9TrPlNIqFZBi2PJtuYQJBAMda
338
+ E5j7eJT75fhm2RPS6xFT5MH5sw6AOA3HucrJ63AoFVzsBpl0E9NBwO4ndLgDzF6T
339
+ cx4Kc4iuunewuB8QFpECQQCfvsHCjIJ/X4kiqeBzxDq2GR/oDgQkOzY+4H9U7Lwl
340
+ e61RBaxk5OHOA0bLtvJblV6NL72ZEZhX60wAWbrOPhpT
341
+ -----END RSA PRIVATE KEY-----
342
+ EOS
343
+
344
+ def test_post_connection_check
345
+ teardown_server
346
+ setup_server_with_server_cert(nil, VERIFY_TEST_CERT_LOCALHOST, VERIFY_TEST_PKEY)
347
+ file = Tempfile.new('cert')
348
+ File.write(file.path, VERIFY_TEST_CERT_LOCALHOST.to_pem)
349
+ @client.ssl_config.add_trust_ca(file.path)
350
+ assert_nothing_raised do
351
+ @client.get("https://localhost:#{serverport}/hello")
352
+ end
353
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
354
+ assert_nothing_raised do
355
+ @client.get("https://localhost:#{serverport}/hello")
356
+ end
357
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
358
+
359
+ teardown_server
360
+ setup_server_with_server_cert(nil, VERIFY_TEST_CERT_FOO_DOMAIN, VERIFY_TEST_PKEY)
361
+ File.write(file.path, VERIFY_TEST_CERT_FOO_DOMAIN.to_pem)
362
+ @client.ssl_config.add_trust_ca(file.path)
363
+ assert_raises(OpenSSL::SSL::SSLError) do
364
+ @client.get("https://localhost:#{serverport}/hello")
365
+ end
366
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
367
+ assert_nothing_raised do
368
+ @client.get("https://localhost:#{serverport}/hello")
369
+ end
370
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
371
+
372
+ teardown_server
373
+ setup_server_with_server_cert(nil, VERIFY_TEST_CERT_ALT_NAME, VERIFY_TEST_PKEY)
374
+ File.write(file.path, VERIFY_TEST_CERT_ALT_NAME.to_pem)
375
+ @client.ssl_config.add_trust_ca(file.path)
376
+ assert_raises(OpenSSL::SSL::SSLError) do
377
+ @client.get("https://localhost:#{serverport}/hello")
378
+ end
379
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
380
+ assert_nothing_raised do
381
+ @client.get("https://localhost:#{serverport}/hello")
382
+ end
383
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
384
+ end
385
+
386
+ def test_x509_store_add_cert_prepend
387
+ store = OpenSSL::X509::Store.new
388
+ assert_equal(store, store.add_cert(OpenSSL::X509::Certificate.new(VERIFY_TEST_CERT_LOCALHOST)))
389
+ end
390
+
391
+ def test_tcp_keepalive
392
+ @client.tcp_keepalive = true
393
+ @client.ssl_config.add_trust_ca(path('ca-chain.pem'))
394
+ @client.get_content(@url)
395
+
396
+ # expecting HTTP keepalive caches the socket
397
+ session = @client.instance_variable_get(:@session_manager).send(:get_cached_session, HTTPClient::Site.new(URI.parse(@url)))
398
+ socket = session.instance_variable_get(:@socket).instance_variable_get(:@socket)
399
+
400
+ assert_true(session.tcp_keepalive)
401
+ if RUBY_ENGINE == 'jruby'
402
+ assert_true(socket.getKeepAlive())
403
+ else
404
+ assert_equal(Socket::SO_KEEPALIVE, socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).optname)
405
+ end
406
+ end
407
+
408
+ def test_timeout
409
+ url = "https://localhost:#{serverport}/"
410
+ @client.ssl_config.add_trust_ca(path('ca-chain.pem'))
411
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=2'))
412
+ @client.receive_timeout = 1
413
+ @client.reset_all
414
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=0'))
415
+
416
+ start = Time.now
417
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
418
+ @client.get_content(url + 'sleep?sec=5')
419
+ end
420
+ if Time.now - start > 3
421
+ # before #342 it detected timeout when IO was freed
422
+ fail 'timeout does not work'
423
+ end
424
+
425
+ @client.receive_timeout = 3
426
+ @client.reset_all
427
+ assert_equal('sleep', @client.get_content(url + 'sleep?sec=2'))
428
+ end
429
+
430
+ private
431
+
432
+ def cert(filename)
433
+ OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename)))
434
+ end
435
+
436
+ def key(filename)
437
+ OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename)))
438
+ end
439
+
440
+ def q(str)
441
+ %Q["#{str}"]
442
+ end
443
+
444
+ def setup_server
445
+ logger = Logger.new(STDERR)
446
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
447
+ @server = WEBrick::HTTPServer.new(
448
+ :BindAddress => "localhost",
449
+ :Logger => logger,
450
+ :Port => 0,
451
+ :AccessLog => [],
452
+ :DocumentRoot => DIR,
453
+ :SSLEnable => true,
454
+ :SSLCACertificateFile => File.join(DIR, 'ca.cert'),
455
+ :SSLCertificate => cert('server.cert'),
456
+ :SSLPrivateKey => key('server.key'),
457
+ :SSLVerifyClient => nil, #OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT|OpenSSL::SSL::VERIFY_PEER,
458
+ :SSLClientCA => cert('ca.cert'),
459
+ :SSLCertName => nil
460
+ )
461
+ @serverport = @server.config[:Port]
462
+ [:hello, :sleep].each do |sym|
463
+ @server.mount(
464
+ "/#{sym}",
465
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
466
+ )
467
+ end
468
+ @server_thread = start_server_thread(@server)
469
+ end
470
+
471
+ def setup_server_with_ssl_version(ssl_version)
472
+ # JRubyOpenSSL does not support "TLSv1_2" as an known version, and some JCE provides TLS v1.2 as "TLSv1.2" not "TLSv1_2"
473
+ if RUBY_ENGINE == 'jruby' && ['TLSv1_1', 'TLSv1_2'].include?(ssl_version)
474
+ ssl_version = ssl_version.tr('_', '.')
475
+ end
476
+ logger = Logger.new(STDERR)
477
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
478
+ @server = WEBrick::HTTPServer.new(
479
+ :BindAddress => "localhost",
480
+ :Logger => logger,
481
+ :Port => 0,
482
+ :AccessLog => [],
483
+ :DocumentRoot => DIR,
484
+ :SSLEnable => true,
485
+ :SSLCACertificateFile => File.join(DIR, 'ca.cert'),
486
+ :SSLCertificate => cert('server.cert'),
487
+ :SSLPrivateKey => key('server.key')
488
+ )
489
+ @server.ssl_context.ssl_version = ssl_version
490
+ @serverport = @server.config[:Port]
491
+ [:hello].each do |sym|
492
+ @server.mount(
493
+ "/#{sym}",
494
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
495
+ )
496
+ end
497
+ @server_thread = start_server_thread(@server)
498
+ end
499
+
500
+ def setup_server_with_server_cert(ca_cert, server_cert, server_key)
501
+ logger = Logger.new(STDERR)
502
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
503
+ @server = WEBrick::HTTPServer.new(
504
+ :BindAddress => "localhost",
505
+ :Logger => logger,
506
+ :Port => 0,
507
+ :AccessLog => [],
508
+ :DocumentRoot => DIR,
509
+ :SSLEnable => true,
510
+ :SSLCACertificateFile => ca_cert,
511
+ :SSLCertificate => server_cert,
512
+ :SSLPrivateKey => server_key,
513
+ :SSLVerifyClient => nil,
514
+ :SSLClientCA => nil,
515
+ :SSLCertName => nil
516
+ )
517
+ @serverport = @server.config[:Port]
518
+ [:hello].each do |sym|
519
+ @server.mount(
520
+ "/#{sym}",
521
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
522
+ )
523
+ end
524
+ @server_thread = start_server_thread(@server)
525
+ end
526
+
527
+ def do_hello(req, res)
528
+ res['content-type'] = 'text/html'
529
+ res.body = "hello"
530
+ end
531
+
532
+ def do_sleep(req, res)
533
+ sec = req.query['sec'].to_i
534
+ sleep sec
535
+ res['content-type'] = 'text/html'
536
+ res.body = "sleep"
537
+ end
538
+
539
+ def start_server_thread(server)
540
+ t = Thread.new {
541
+ Thread.current.abort_on_exception = true
542
+ server.start
543
+ }
544
+ while server.status != :Running
545
+ sleep 0.1
546
+ unless t.alive?
547
+ t.join
548
+ raise
549
+ end
550
+ end
551
+ t
552
+ end
553
+
554
+ def verify_callback(ok, cert)
555
+ @verify_callback_called = true
556
+ p ["client", ok, cert] if $DEBUG
557
+ ok
558
+ end
559
+ end