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,85 @@
1
+ # It is useful to re-use a HTTPClient instance for multiple requests, to
2
+ # re-use HTTP 1.1 persistent connections.
3
+ #
4
+ # To do that, you sometimes want to store an HTTPClient instance in a global/
5
+ # class variable location, so it can be accessed and re-used.
6
+ #
7
+ # This mix-in makes it easy to create class-level access to one or more
8
+ # HTTPClient instances. The HTTPClient instances are lazily initialized
9
+ # on first use (to, for instance, avoid interfering with WebMock/VCR),
10
+ # and are initialized in a thread-safe manner. Note that a
11
+ # HTTPClient, once initialized, is safe for use in multiple threads.
12
+ #
13
+ # Note that you `extend` HTTPClient::IncludeClient, not `include.
14
+ #
15
+ # require 'httpclient/include_client'
16
+ # class Widget
17
+ # extend HTTPClient::IncludeClient
18
+ #
19
+ # include_http_client
20
+ # # and/or, specify more stuff
21
+ # include_http_client('http://myproxy:8080', :method_name => :my_client) do |client|
22
+ # # any init you want
23
+ # client.set_cookie_store nil
24
+ # client.
25
+ # end
26
+ # end
27
+ #
28
+ # That creates two HTTPClient instances available at the class level.
29
+ # The first will be available from Widget.http_client (default method
30
+ # name for `include_http_client`), with default initialization.
31
+ #
32
+ # The second will be available at Widget.my_client, with the init arguments
33
+ # provided, further initialized by the block provided.
34
+ #
35
+ # In addition to a class-level method, for convenience instance-level methods
36
+ # are also provided. Widget.http_client is identical to Widget.new.http_client
37
+ #
38
+ #
39
+ require 'httpclient'
40
+
41
+ class HTTPClient
42
+ module IncludeClient
43
+
44
+
45
+ def include_http_client(*args, &block)
46
+ # We're going to dynamically define a class
47
+ # to hold our state, namespaced, as well as possibly dynamic
48
+ # name of cover method.
49
+ method_name = (args.last.delete(:method_name) if args.last.kind_of? Hash) || :http_client
50
+ args.pop if args.last == {} # if last arg was named methods now empty, remove it.
51
+
52
+ # By the amazingness of closures, we can create these things
53
+ # in local vars here and use em in our method, we don't even
54
+ # need iVars for state.
55
+ client_instance = nil
56
+ client_mutex = Mutex.new
57
+ client_args = args
58
+ client_block = block
59
+
60
+ # to define a _class method_ on the specific class that's currently
61
+ # `self`, we have to use this bit of metaprogramming, sorry.
62
+ (class << self; self ; end).instance_eval do
63
+ define_method(method_name) do
64
+ # implementation copied from ruby stdlib singleton
65
+ # to create this global obj thread-safely.
66
+ return client_instance if client_instance
67
+ client_mutex.synchronize do
68
+ return client_instance if client_instance
69
+ # init HTTPClient with specified args/block
70
+ client_instance = HTTPClient.new(*client_args)
71
+ client_block.call(client_instance) if client_block
72
+ end
73
+ return client_instance
74
+ end
75
+ end
76
+
77
+ # And for convenience, an _instance method_ on the class that just
78
+ # delegates to the class method.
79
+ define_method(method_name) do
80
+ self.class.send(method_name)
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,588 @@
1
+ # HTTPClient - HTTP client library.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
+ #
4
+ # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
+ # redistribute it and/or modify it under the same terms of Ruby's license;
6
+ # either the dual license version in 2003, or any later version.
7
+
8
+
9
+ require 'java'
10
+ require 'httpclient/ssl_config'
11
+
12
+
13
+ class HTTPClient
14
+
15
+ unless defined?(SSLSocket)
16
+
17
+ class JavaSocketWrap
18
+ java_import 'java.net.InetSocketAddress'
19
+ java_import 'java.io.BufferedInputStream'
20
+
21
+ BUF_SIZE = 1024 * 16
22
+
23
+ def self.connect(socket, site, opts = {})
24
+ socket_addr = InetSocketAddress.new(site.host, site.port)
25
+ if opts[:connect_timeout]
26
+ socket.connect(socket_addr, opts[:connect_timeout])
27
+ else
28
+ socket.connect(socket_addr)
29
+ end
30
+ socket.setSoTimeout(opts[:so_timeout]) if opts[:so_timeout]
31
+ socket.setKeepAlive(true) if opts[:tcp_keepalive]
32
+ socket
33
+ end
34
+
35
+ def initialize(socket, debug_dev = nil)
36
+ @socket = socket
37
+ @debug_dev = debug_dev
38
+ @outstr = @socket.getOutputStream
39
+ @instr = BufferedInputStream.new(@socket.getInputStream)
40
+ @buf = (' ' * BUF_SIZE).to_java_bytes
41
+ @bufstr = ''
42
+ end
43
+
44
+ def close
45
+ @socket.close
46
+ end
47
+
48
+ def closed?
49
+ @socket.isClosed
50
+ end
51
+
52
+ def eof?
53
+ @socket.isClosed
54
+ end
55
+
56
+ def gets(rs)
57
+ while (size = @bufstr.index(rs)).nil?
58
+ if fill() == -1
59
+ size = @bufstr.size
60
+ break
61
+ end
62
+ end
63
+ str = @bufstr.slice!(0, size + rs.size)
64
+ debug(str)
65
+ str
66
+ end
67
+
68
+ def read(size, buf = nil)
69
+ while @bufstr.size < size
70
+ if fill() == -1
71
+ break
72
+ end
73
+ end
74
+ str = @bufstr.slice!(0, size)
75
+ debug(str)
76
+ if buf
77
+ buf.replace(str)
78
+ else
79
+ str
80
+ end
81
+ end
82
+
83
+ def readpartial(size, buf = nil)
84
+ while @bufstr.size == 0
85
+ if fill() == -1
86
+ raise EOFError.new('end of file reached')
87
+ end
88
+ end
89
+ str = @bufstr.slice!(0, size)
90
+ debug(str)
91
+ if buf
92
+ buf.replace(str)
93
+ else
94
+ str
95
+ end
96
+ end
97
+
98
+ def <<(str)
99
+ rv = @outstr.write(str.to_java_bytes)
100
+ debug(str)
101
+ rv
102
+ end
103
+
104
+ def flush
105
+ @socket.flush
106
+ end
107
+
108
+ def sync
109
+ true
110
+ end
111
+
112
+ def sync=(sync)
113
+ unless sync
114
+ raise "sync = false is not supported. This option was introduced for backward compatibility just in case."
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def fill
121
+ begin
122
+ size = @instr.read(@buf)
123
+ if size > 0
124
+ @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
125
+ end
126
+ size
127
+ rescue java.io.IOException => e
128
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
129
+ end
130
+ end
131
+
132
+ def debug(str)
133
+ @debug_dev << str if @debug_dev && str
134
+ end
135
+ end
136
+
137
+ class JRubySSLSocket < JavaSocketWrap
138
+ java_import 'java.io.ByteArrayInputStream'
139
+ java_import 'java.io.InputStreamReader'
140
+ java_import 'java.net.Socket'
141
+ java_import 'java.security.KeyStore'
142
+ java_import 'java.security.cert.Certificate'
143
+ java_import 'java.security.cert.CertificateFactory'
144
+ java_import 'javax.net.ssl.KeyManagerFactory'
145
+ java_import 'javax.net.ssl.SSLContext'
146
+ java_import 'javax.net.ssl.SSLSocketFactory'
147
+ java_import 'javax.net.ssl.TrustManager'
148
+ java_import 'javax.net.ssl.TrustManagerFactory'
149
+ java_import 'javax.net.ssl.X509TrustManager'
150
+ java_import 'org.jruby.ext.openssl.x509store.PEMInputOutput'
151
+
152
+ class JavaCertificate
153
+ attr_reader :cert
154
+
155
+ def initialize(cert)
156
+ @cert = cert
157
+ end
158
+
159
+ def subject
160
+ @cert.getSubjectDN
161
+ end
162
+
163
+ def to_text
164
+ @cert.toString
165
+ end
166
+
167
+ def to_pem
168
+ '(not in PEM format)'
169
+ end
170
+ end
171
+
172
+ class SSLStoreContext
173
+ attr_reader :current_cert, :chain, :error_depth, :error, :error_string
174
+
175
+ def initialize(current_cert, chain, error_depth, error, error_string)
176
+ @current_cert, @chain, @error_depth, @error, @error_string =
177
+ current_cert, chain, error_depth, error, error_string
178
+ end
179
+ end
180
+
181
+ class JSSEVerifyCallback
182
+ def initialize(verify_callback)
183
+ @verify_callback = verify_callback
184
+ end
185
+
186
+ def call(is_ok, chain, error_depth = -1, error = -1, error_string = '(unknown)')
187
+ if @verify_callback
188
+ ruby_chain = chain.map { |cert|
189
+ JavaCertificate.new(cert)
190
+ }.reverse
191
+ # NOTE: The order depends on provider implementation
192
+ ruby_chain.each do |cert|
193
+ is_ok = @verify_callback.call(
194
+ is_ok,
195
+ SSLStoreContext.new(cert, ruby_chain, error_depth, error, error_string)
196
+ )
197
+ end
198
+ end
199
+ is_ok
200
+ end
201
+ end
202
+
203
+ class VerifyNoneTrustManagerFactory
204
+ class VerifyNoneTrustManager
205
+ include X509TrustManager
206
+
207
+ def initialize(verify_callback)
208
+ @verify_callback = JSSEVerifyCallback.new(verify_callback)
209
+ end
210
+
211
+ def checkServerTrusted(chain, authType)
212
+ @verify_callback.call(true, chain)
213
+ end
214
+
215
+ def checkClientTrusted(chain, authType); end
216
+ def getAcceptedIssuers; end
217
+ end
218
+
219
+ def initialize(verify_callback = nil)
220
+ @verify_callback = verify_callback
221
+ end
222
+
223
+ def init(trustStore)
224
+ @managers = [VerifyNoneTrustManager.new(@verify_callback)].to_java(X509TrustManager)
225
+ end
226
+
227
+ def getTrustManagers
228
+ @managers
229
+ end
230
+ end
231
+
232
+ class SystemTrustManagerFactory
233
+ class SystemTrustManager
234
+ include X509TrustManager
235
+
236
+ def initialize(original, verify_callback)
237
+ @original = original
238
+ @verify_callback = JSSEVerifyCallback.new(verify_callback)
239
+ end
240
+
241
+ def checkServerTrusted(chain, authType)
242
+ is_ok = false
243
+ excn = nil
244
+ # TODO can we detect the depth from excn?
245
+ error_depth = -1
246
+ error = nil
247
+ error_message = nil
248
+ begin
249
+ @original.checkServerTrusted(chain, authType)
250
+ is_ok = true
251
+ rescue java.security.cert.CertificateException => excn
252
+ is_ok = false
253
+ error = excn.class.name
254
+ error_message = excn.getMessage
255
+ end
256
+ is_ok = @verify_callback.call(is_ok, chain, error_depth, error, error_message)
257
+ unless is_ok
258
+ excn ||= OpenSSL::SSL::SSLError.new('verifycallback failed')
259
+ raise excn
260
+ end
261
+ end
262
+
263
+ def checkClientTrusted(chain, authType); end
264
+ def getAcceptedIssuers; end
265
+ end
266
+
267
+ def initialize(verify_callback = nil)
268
+ @verify_callback = verify_callback
269
+ end
270
+
271
+ def init(trust_store)
272
+ tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
273
+ tmf.java_method(:init, [KeyStore]).call(trust_store)
274
+ @original = tmf.getTrustManagers.find { |tm|
275
+ tm.is_a?(X509TrustManager)
276
+ }
277
+ @managers = [SystemTrustManager.new(@original, @verify_callback)].to_java(X509TrustManager)
278
+ end
279
+
280
+ def getTrustManagers
281
+ @managers
282
+ end
283
+ end
284
+
285
+ module PEMUtils
286
+ def self.read_certificate(pem)
287
+ cert = pem.sub(/.*?-----BEGIN CERTIFICATE-----/m, '').sub(/-----END CERTIFICATE-----.*?/m, '')
288
+ der = cert.unpack('m*').first
289
+ cf = CertificateFactory.getInstance('X.509')
290
+ cf.generateCertificate(ByteArrayInputStream.new(der.to_java_bytes))
291
+ end
292
+
293
+ def self.read_private_key(pem, password)
294
+ if password
295
+ password = password.unpack('C*').to_java(:char)
296
+ end
297
+ PEMInputOutput.read_private_key(InputStreamReader.new(ByteArrayInputStream.new(pem.to_java_bytes)), password)
298
+ end
299
+ end
300
+
301
+ class KeyStoreLoader
302
+ PASSWORD = 16.times.map { rand(256) }.to_java(:char)
303
+
304
+ def initialize
305
+ @keystore = KeyStore.getInstance('JKS')
306
+ @keystore.load(nil)
307
+ end
308
+
309
+ def add(cert_source, key_source, password)
310
+ cert_str = cert_source.respond_to?(:to_pem) ? cert_source.to_pem : File.read(cert_source.to_s)
311
+ cert = PEMUtils.read_certificate(cert_str)
312
+ @keystore.setCertificateEntry('client_cert', cert)
313
+ key_str = key_source.respond_to?(:to_pem) ? key_source.to_pem : File.read(key_source.to_s)
314
+ key_pair = PEMUtils.read_private_key(key_str, password)
315
+ @keystore.setKeyEntry('client_key', key_pair.getPrivate, PASSWORD, [cert].to_java(Certificate))
316
+ end
317
+
318
+ def keystore
319
+ @keystore
320
+ end
321
+ end
322
+
323
+ class TrustStoreLoader
324
+ attr_reader :size
325
+
326
+ def initialize
327
+ @trust_store = KeyStore.getInstance('JKS')
328
+ @trust_store.load(nil)
329
+ @size = 0
330
+ end
331
+
332
+ def add(cert_source)
333
+ return if cert_source == :default
334
+ if cert_source.respond_to?(:to_pem)
335
+ pem = cert_source.to_pem
336
+ load_pem(pem)
337
+ elsif File.directory?(cert_source)
338
+ warn("#{cert_source}: directory not yet supported")
339
+ return
340
+ else
341
+ pem = nil
342
+ File.read(cert_source).each_line do |line|
343
+ case line
344
+ when /-----BEGIN CERTIFICATE-----/
345
+ pem = ''
346
+ when /-----END CERTIFICATE-----/
347
+ load_pem(pem)
348
+ # keep parsing in case where multiple certificates in a file
349
+ else
350
+ if pem
351
+ pem << line
352
+ end
353
+ end
354
+ end
355
+ end
356
+ end
357
+
358
+ def trust_store
359
+ if @size == 0
360
+ nil
361
+ else
362
+ @trust_store
363
+ end
364
+ end
365
+
366
+ private
367
+
368
+ def load_pem(pem)
369
+ cert = PEMUtils.read_certificate(pem)
370
+ @size += 1
371
+ @trust_store.setCertificateEntry("cert_#{@size}", cert)
372
+ end
373
+ end
374
+
375
+ # Ported from commons-httpclient 'BrowserCompatHostnameVerifier'
376
+ class BrowserCompatHostnameVerifier
377
+ BAD_COUNTRY_2LDS = %w(ac co com ed edu go gouv gov info lg ne net or org).sort
378
+ require 'ipaddr'
379
+
380
+ def extract_sans(cert, subject_type)
381
+ sans = cert.getSubjectAlternativeNames rescue nil
382
+ if sans.nil?
383
+ return nil
384
+ end
385
+ sans.find_all { |san|
386
+ san.first.to_i == subject_type
387
+ }.map { |san|
388
+ san[1]
389
+ }
390
+ end
391
+
392
+ def extract_cn(cert)
393
+ subject = cert.getSubjectX500Principal()
394
+ if subject
395
+ subject_dn = javax.naming.ldap.LdapName.new(subject.toString)
396
+ subject_dn.getRdns.to_a.reverse.each do |rdn|
397
+ attributes = rdn.toAttributes
398
+ cn = attributes.get('cn')
399
+ if cn
400
+ if value = cn.get
401
+ return value.to_s
402
+ end
403
+ end
404
+ end
405
+ end
406
+ end
407
+
408
+ def ipaddr?(addr)
409
+ !(IPAddr.new(addr) rescue nil).nil?
410
+ end
411
+
412
+ def verify(hostname, cert)
413
+ is_ipaddr = ipaddr?(hostname)
414
+ sans = extract_sans(cert, is_ipaddr ? 7 : 2)
415
+ cn = extract_cn(cert)
416
+ if sans
417
+ sans.each do |san|
418
+ return true if match_identify(hostname, san)
419
+ end
420
+ raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match any of the subject alternative names: #{sans}")
421
+ elsif cn
422
+ return true if match_identify(hostname, cn)
423
+ raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match common name of the certificate subject: #{cn}")
424
+ end
425
+ raise OpenSSL::SSL::SSLError.new("Certificate subject for for <#{hostname}> doesn't contain a common name and does not have alternative names")
426
+ end
427
+
428
+ def match_identify(hostname, identity)
429
+ if hostname.nil?
430
+ return false
431
+ end
432
+ hostname = hostname.downcase
433
+ identity = identity.downcase
434
+ parts = identity.split('.')
435
+ if parts.length >= 3 && parts.first.end_with?('*') && valid_country_wildcard(parts)
436
+ create_wildcard_regexp(identity) =~ hostname
437
+ else
438
+ hostname == identity
439
+ end
440
+ end
441
+
442
+ def create_wildcard_regexp(value)
443
+ # Escape first then search '\*' for meta-char interpolation
444
+ labels = value.split('.').map { |e| Regexp.escape(e) }
445
+ # Handle '*'s only at the left-most label, exclude A-label and U-label
446
+ labels[0].gsub!(/\\\*/, '[^.]+') if !labels[0].start_with?('xn\-\-') and labels[0].ascii_only?
447
+ /\A#{labels.join('\.')}\z/i
448
+ end
449
+
450
+ def valid_country_wildcard(parts)
451
+ if parts.length != 3 || parts[2].length != 2
452
+ true
453
+ else
454
+ !BAD_COUNTRY_2LDS.include?(parts[1])
455
+ end
456
+ end
457
+ end
458
+
459
+ def self.create_socket(session)
460
+ opts = {
461
+ :connect_timeout => session.connect_timeout * 1000,
462
+ # send_timeout is ignored in JRuby
463
+ :so_timeout => session.receive_timeout * 1000,
464
+ :tcp_keepalive => session.tcp_keepalive,
465
+ :debug_dev => session.debug_dev
466
+ }
467
+ socket = nil
468
+ begin
469
+ if session.proxy
470
+ site = session.proxy || session.dest
471
+ socket = JavaSocketWrap.connect(Socket.new, site, opts)
472
+ session.connect_ssl_proxy(JavaSocketWrap.new(socket), Util.urify(session.dest.to_s))
473
+ end
474
+ new(socket, session.dest, session.ssl_config, opts)
475
+ rescue
476
+ socket.close if socket
477
+ raise
478
+ end
479
+ end
480
+
481
+ DEFAULT_SSL_PROTOCOL = (java.lang.System.getProperty('java.specification.version') == '1.7') ? 'TLSv1.2' : 'TLS'
482
+ def initialize(socket, dest, config, opts = {})
483
+ @config = config
484
+ begin
485
+ @ssl_socket = create_ssl_socket(socket, dest, config, opts)
486
+ ssl_version = java_ssl_version(config)
487
+ @ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
488
+ if config.ciphers != SSLConfig::CIPHERS_DEFAULT
489
+ @ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
490
+ end
491
+ ssl_connect(dest.host)
492
+ rescue java.security.GeneralSecurityException => e
493
+ raise OpenSSL::SSL::SSLError.new(e.getMessage)
494
+ rescue java.io.IOException => e
495
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
496
+ end
497
+
498
+ super(@ssl_socket, opts[:debug_dev])
499
+ end
500
+
501
+ def java_ssl_version(config)
502
+ if config.ssl_version == :auto
503
+ DEFAULT_SSL_PROTOCOL
504
+ else
505
+ config.ssl_version.to_s.tr('_', '.')
506
+ end
507
+ end
508
+
509
+ def create_ssl_context(config)
510
+ unless config.cert_store_crl_items.empty?
511
+ raise NotImplementedError.new('Manual CRL configuration is not yet supported')
512
+ end
513
+
514
+ km = nil
515
+ if config.client_cert && config.client_key
516
+ loader = KeyStoreLoader.new
517
+ loader.add(config.client_cert, config.client_key, config.client_key_pass)
518
+ kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
519
+ kmf.init(loader.keystore, KeyStoreLoader::PASSWORD)
520
+ km = kmf.getKeyManagers
521
+ end
522
+
523
+ trust_store = nil
524
+ verify_callback = config.verify_callback || config.method(:default_verify_callback)
525
+ if !config.verify?
526
+ tmf = VerifyNoneTrustManagerFactory.new(verify_callback)
527
+ else
528
+ tmf = SystemTrustManagerFactory.new(verify_callback)
529
+ loader = TrustStoreLoader.new
530
+ config.cert_store_items.each do |item|
531
+ loader.add(item)
532
+ end
533
+ trust_store = loader.trust_store
534
+ end
535
+ tmf.init(trust_store)
536
+ tm = tmf.getTrustManagers
537
+
538
+ ctx = SSLContext.getInstance(java_ssl_version(config))
539
+ ctx.init(km, tm, nil)
540
+ if config.timeout
541
+ ctx.getClientSessionContext.setSessionTimeout(config.timeout)
542
+ end
543
+ ctx
544
+ end
545
+
546
+ def create_ssl_socket(socket, dest, config, opts)
547
+ ctx = create_ssl_context(config)
548
+ factory = ctx.getSocketFactory
549
+ if socket
550
+ ssl_socket = factory.createSocket(socket, dest.host, dest.port, true)
551
+ else
552
+ ssl_socket = factory.createSocket
553
+ JavaSocketWrap.connect(ssl_socket, dest, opts)
554
+ end
555
+ ssl_socket
556
+ end
557
+
558
+ def peer_cert
559
+ @peer_cert
560
+ end
561
+
562
+ private
563
+
564
+ def ssl_connect(hostname)
565
+ @ssl_socket.startHandshake
566
+ ssl_session = @ssl_socket.getSession
567
+ @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
568
+ if $DEBUG
569
+ warn("Protocol version: #{ssl_session.getProtocol}")
570
+ warn("Cipher: #{@ssl_socket.getSession.getCipherSuite}")
571
+ end
572
+ post_connection_check(hostname)
573
+ end
574
+
575
+ def post_connection_check(hostname)
576
+ if !@config.verify?
577
+ return
578
+ else
579
+ BrowserCompatHostnameVerifier.new.verify(hostname, @peer_cert.cert)
580
+ end
581
+ end
582
+ end
583
+
584
+ SSLSocket = JRubySSLSocket
585
+
586
+ end
587
+
588
+ end