httpclient 2.6.0.1 → 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.
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -3,16 +3,19 @@ unless defined?(HTTPClient::CookieManager)
3
3
  begin # for catching LoadError and load webagent-cookie instead
4
4
 
5
5
  require 'http-cookie'
6
+ require 'httpclient/util'
6
7
 
7
8
  class HTTPClient
8
9
  class CookieManager
9
- attr_reader :format
10
+ include HTTPClient::Util
11
+
12
+ attr_reader :format, :jar
10
13
  attr_accessor :cookies_file
11
14
 
12
- def initialize(cookies_file = nil, format = WebAgentSaver)
15
+ def initialize(cookies_file = nil, format = WebAgentSaver, jar = HTTP::CookieJar.new)
13
16
  @cookies_file = cookies_file
14
17
  @format = format
15
- @jar = HTTP::CookieJar.new
18
+ @jar = jar
16
19
  load_cookies if @cookies_file
17
20
  end
18
21
 
@@ -68,7 +71,7 @@ class HTTPClient
68
71
  end
69
72
 
70
73
  def find(uri)
71
- warn('CookieManager#find is deprecated and will be removed in near future. Use HTTP::Cookie.cookie_value(CookieManager#cookies) instead')
74
+ warning('CookieManager#find is deprecated and will be removed in near future. Use HTTP::Cookie.cookie_value(CookieManager#cookies) instead')
72
75
  if cookie = cookies(uri)
73
76
  HTTP::Cookie.cookie_value(cookie)
74
77
  end
@@ -174,7 +177,7 @@ class WebAgent
174
177
  CookieManager = ::HTTPClient::CookieManager
175
178
 
176
179
  class Cookie < HTTP::Cookie
177
- @@warned = false
180
+ include HTTPClient::Util
178
181
 
179
182
  def url
180
183
  deprecated('url', 'origin')
@@ -194,7 +197,7 @@ class WebAgent
194
197
  alias original_domain domain
195
198
 
196
199
  def domain
197
- warn('Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.')
200
+ warning('Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.')
198
201
  self.original_domain
199
202
  end
200
203
 
@@ -206,10 +209,7 @@ class WebAgent
206
209
  private
207
210
 
208
211
  def deprecated(old, new)
209
- unless @@warned
210
- warn("WebAgent::Cookie is deprecated and will be replaced with HTTP::Cookie in the near future. Please use Cookie##{new} instead of Cookie##{old} for the replacement.")
211
- @@warned = true
212
- end
212
+ warning("WebAgent::Cookie is deprecated and will be replaced with HTTP::Cookie in the near future. Please use Cookie##{new} instead of Cookie##{old} for the replacement.")
213
213
  end
214
214
  end
215
215
  end
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  # HTTPClient - HTTP client library.
4
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
4
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
5
5
  #
6
6
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
7
7
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -12,6 +12,7 @@ require 'time'
12
12
  if defined?(Encoding::ASCII_8BIT)
13
13
  require 'open-uri' # for encoding
14
14
  end
15
+ require 'httpclient/util'
15
16
 
16
17
 
17
18
  # A namespace module for HTTP Message definitions used by HTTPClient.
@@ -95,6 +96,7 @@ module HTTP
95
96
  # p res.header['last-modified'].first
96
97
  #
97
98
  class Message
99
+ include HTTPClient::Util
98
100
 
99
101
  CRLF = "\r\n"
100
102
 
@@ -698,8 +700,9 @@ module HTTP
698
700
 
699
701
  def params_from_file(value)
700
702
  params = {}
703
+ original_filename = value.respond_to?(:original_filename) ? value.original_filename : nil
701
704
  path = value.respond_to?(:path) ? value.path : nil
702
- params['filename'] = File.basename(path || '')
705
+ params['filename'] = original_filename || File.basename(path || '')
703
706
  # Creation time is not available from File::Stat
704
707
  if value.respond_to?(:mtime)
705
708
  params['modification-date'] = value.mtime.rfc822
@@ -806,6 +809,8 @@ module HTTP
806
809
  case path
807
810
  when /\.txt$/i
808
811
  'text/plain'
812
+ when /\.xml$/i
813
+ 'text/xml'
809
814
  when /\.(htm|html)$/i
810
815
  'text/html'
811
816
  when /\.doc$/i
@@ -980,12 +985,12 @@ module HTTP
980
985
 
981
986
  VERSION_WARNING = 'Message#version (Float) is deprecated. Use Message#http_version (String) instead.'
982
987
  def version
983
- warn(VERSION_WARNING)
988
+ warning(VERSION_WARNING)
984
989
  @http_header.http_version.to_f
985
990
  end
986
991
 
987
992
  def version=(version)
988
- warn(VERSION_WARNING)
993
+ warning(VERSION_WARNING)
989
994
  @http_header.http_version = version
990
995
  end
991
996
 
@@ -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