httpclient 2.7.2 → 2.9.0

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.
data/test/test_ssl.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require File.expand_path('helper', File.dirname(__FILE__))
2
2
  require 'webrick/https'
3
+ require 'time'
3
4
 
4
5
 
5
6
  class TestSSL < Test::Unit::TestCase
@@ -7,6 +8,8 @@ class TestSSL < Test::Unit::TestCase
7
8
 
8
9
  DIR = File.dirname(File.expand_path(__FILE__))
9
10
 
11
+ OPENSSL_VERSION = Integer(OpenSSL::OPENSSL_LIBRARY_VERSION.match(/OpenSSL (\d+)\./)[1])
12
+
10
13
  def setup
11
14
  super
12
15
  @serverpid = @client = nil
@@ -24,6 +27,10 @@ class TestSSL < Test::Unit::TestCase
24
27
  File.expand_path(filename, DIR)
25
28
  end
26
29
 
30
+ def read_path(filename)
31
+ File.read(path(filename))
32
+ end
33
+
27
34
  def test_proxy_ssl
28
35
  setup_proxyserver
29
36
  escape_noproxy do
@@ -31,7 +38,7 @@ class TestSSL < Test::Unit::TestCase
31
38
  @client.ssl_config.set_client_cert_file(path('client.cert'), path('client.key'))
32
39
  @client.ssl_config.add_trust_ca(path('ca.cert'))
33
40
  @client.ssl_config.add_trust_ca(path('subca.cert'))
34
- @client.debug_dev = str = ""
41
+ @client.debug_dev = str = "".dup
35
42
  assert_equal(200, @client.get(@url).status)
36
43
  assert(/accept/ =~ @proxyio.string, 'proxy is not used')
37
44
  assert(/Host: localhost:#{serverport}/ =~ str)
@@ -70,7 +77,7 @@ unless defined?(HTTPClient::JRubySSLSocket)
70
77
  end
71
78
 
72
79
  def test_debug_dev
73
- str = @client.debug_dev = ''
80
+ str = @client.debug_dev = ''.dup
74
81
  cfg = @client.ssl_config
75
82
  cfg.client_cert = path("client.cert")
76
83
  cfg.client_key = path("client.key")
@@ -80,6 +87,17 @@ end
80
87
  assert(str.scan(/^hello$/)[0])
81
88
  end
82
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
+
83
101
  def test_verification
84
102
  cfg = @client.ssl_config
85
103
  cfg.verify_callback = method(:verify_callback).to_proc
@@ -149,6 +167,29 @@ end
149
167
  #
150
168
  cfg.verify_mode = nil
151
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
152
193
  end
153
194
 
154
195
  if defined?(HTTPClient::JRubySSLSocket)
@@ -201,22 +242,19 @@ else
201
242
  end
202
243
  end
203
244
 
204
- # SSL_CERT_FILE does not work with recent jruby-openssl.
205
- # You should not depend on SSL_CERT_FILE on JRuby
206
- if !defined?(JRUBY_VERSION)
207
- def test_set_default_paths
208
- assert_raise(OpenSSL::SSL::SSLError) do
209
- @client.get(@url)
210
- end
211
- escape_env do
212
- ENV['SSL_CERT_FILE'] = File.join(DIR, 'ca-chain.pem')
213
- @client.ssl_config.set_default_paths
214
- @client.get(@url)
215
- end
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)
216
253
  end
217
254
  end
218
255
 
219
256
  def test_no_sslv3
257
+ omit('TODO: SSLv3 is not supported in many environments. re-enable when disable TLSv1')
220
258
  teardown_server
221
259
  setup_server_with_ssl_version(:SSLv3)
222
260
  assert_raise(OpenSSL::SSL::SSLError) do
@@ -225,19 +263,31 @@ end
225
263
  end
226
264
  end
227
265
 
228
- def test_allow_tlsv1
229
- teardown_server
230
- setup_server_with_ssl_version(:TLSv1)
231
- assert_nothing_raised do
232
- @client.ssl_config.verify_mode = nil
233
- @client.get("https://localhost:#{serverport}/hello")
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)
234
284
  end
235
285
  end
236
286
 
237
287
  def test_use_higher_TLS
238
- omit('TODO: it does not pass with Java 7 or old openssl ')
288
+ omit('TODO: it does not pass with Java 7 or old openssl')
239
289
  teardown_server
240
- setup_server_with_ssl_version(:TLSv1_2)
290
+ setup_server_with_ssl_version('TLSv1_2')
241
291
  assert_nothing_raised do
242
292
  @client.ssl_config.verify_mode = nil
243
293
  @client.get("https://localhost:#{serverport}/hello")
@@ -246,6 +296,100 @@ end
246
296
  end
247
297
  end
248
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
+
249
393
  private
250
394
 
251
395
  def cert(filename)
@@ -278,7 +422,7 @@ private
278
422
  :SSLCertName => nil
279
423
  )
280
424
  @serverport = @server.config[:Port]
281
- [:hello].each do |sym|
425
+ [:hello, :sleep].each do |sym|
282
426
  @server.mount(
283
427
  "/#{sym}",
284
428
  WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
@@ -288,6 +432,10 @@ private
288
432
  end
289
433
 
290
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
291
439
  logger = Logger.new(STDERR)
292
440
  logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
293
441
  @server = WEBrick::HTTPServer.new(
@@ -312,11 +460,45 @@ private
312
460
  @server_thread = start_server_thread(@server)
313
461
  end
314
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
+
315
490
  def do_hello(req, res)
316
491
  res['content-type'] = 'text/html'
317
492
  res.body = "hello"
318
493
  end
319
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
+
320
502
  def start_server_thread(server)
321
503
  t = Thread.new {
322
504
  Thread.current.abort_on_exception = true
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.2
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroshi Nakamura
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-22 00:00:00.000000000 Z
12
- dependencies: []
13
- description:
11
+ date: 2025-02-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mutex_m
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
14
28
  email: nahi@ruby-lang.org
15
29
  executables:
16
30
  - httpclient
@@ -46,6 +60,7 @@ files:
46
60
  - sample/auth.rb
47
61
  - sample/cookie.rb
48
62
  - sample/dav.rb
63
+ - sample/generate_test_keys.rb
49
64
  - sample/howto.rb
50
65
  - sample/jsonclient.rb
51
66
  - sample/oauth_buzz.rb
@@ -63,17 +78,26 @@ files:
63
78
  - sample/wcat.rb
64
79
  - test/ca-chain.pem
65
80
  - test/ca.cert
81
+ - test/ca.key
82
+ - test/ca.srl
66
83
  - test/client-pass.key
67
84
  - test/client.cert
68
85
  - test/client.key
86
+ - test/fixtures/verify.alt.cert
87
+ - test/fixtures/verify.foo.cert
88
+ - test/fixtures/verify.key
89
+ - test/fixtures/verify.localhost.cert
69
90
  - test/helper.rb
70
91
  - test/htdigest
71
92
  - test/htpasswd
93
+ - test/jruby_ssl_socket/test_pemutils.rb
72
94
  - test/runner.rb
73
95
  - test/server.cert
74
96
  - test/server.key
75
97
  - test/sslsvr.rb
76
98
  - test/subca.cert
99
+ - test/subca.key
100
+ - test/subca.srl
77
101
  - test/test_auth.rb
78
102
  - test/test_cookie.rb
79
103
  - test/test_hexdump.rb
@@ -87,7 +111,7 @@ homepage: https://github.com/nahi/httpclient
87
111
  licenses:
88
112
  - ruby
89
113
  metadata: {}
90
- post_install_message:
114
+ post_install_message:
91
115
  rdoc_options: []
92
116
  require_paths:
93
117
  - lib
@@ -102,9 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
126
  - !ruby/object:Gem::Version
103
127
  version: '0'
104
128
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.5.1
107
- signing_key:
129
+ rubygems_version: 3.5.3
130
+ signing_key:
108
131
  specification_version: 4
109
132
  summary: gives something like the functionality of libwww-perl (LWP) in Ruby
110
133
  test_files: []