lib/httpclient/jruby_ssl_socket.rb in httpclient-2.8.2.4 vs lib/httpclient/jruby_ssl_socket.rb in httpclient-2.8.3
- old
+ new
@@ -13,13 +13,27 @@
class HTTPClient
unless defined?(SSLSocket)
class JavaSocketWrap
+ java_import 'java.net.InetSocketAddress'
java_import 'java.io.BufferedInputStream'
+
BUF_SIZE = 1024 * 16
+ def self.connect(socket, site, opts = {})
+ socket_addr = InetSocketAddress.new(site.host, site.port)
+ if opts[:connect_timeout]
+ socket.connect(socket_addr, opts[:connect_timeout])
+ else
+ socket.connect(socket_addr)
+ end
+ socket.setSoTimeout(opts[:so_timeout]) if opts[:so_timeout]
+ socket.setKeepAlive(true) if opts[:tcp_keepalive]
+ socket
+ end
+
def initialize(socket, debug_dev = nil)
@socket = socket
@debug_dev = debug_dev
@outstr = @socket.getOutputStream
@instr = BufferedInputStream.new(@socket.getInputStream)
@@ -37,11 +51,10 @@
def eof?
@socket.isClosed
end
-
def gets(rs)
while (size = @bufstr.index(rs)).nil?
if fill() == -1
size = @bufstr.size
break
@@ -103,15 +116,19 @@
end
private
def fill
- size = @instr.read(@buf)
- if size > 0
- @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
+ begin
+ size = @instr.read(@buf)
+ if size > 0
+ @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
+ end
+ size
+ rescue java.io.IOException => e
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
end
- size
end
def debug(str)
@debug_dev << str if @debug_dev && str
end
@@ -265,12 +282,12 @@
end
end
module PEMUtils
def self.read_certificate(pem)
- pem = pem.sub(/-----BEGIN CERTIFICATE-----/, '').sub(/-----END CERTIFICATE-----/, '')
- der = pem.unpack('m*').first
+ cert = pem.sub(/.*?-----BEGIN CERTIFICATE-----/m, '').sub(/-----END CERTIFICATE-----.*?/m, '')
+ der = cert.unpack('m*').first
cf = CertificateFactory.getInstance('X.509')
cf.generateCertificate(ByteArrayInputStream.new(der.to_java_bytes))
end
def self.read_private_key(pem, password)
@@ -438,31 +455,60 @@
end
end
end
def self.create_socket(session)
- site = session.proxy || session.dest
- socket = Socket.new(site.host, site.port)
+ opts = {
+ :connect_timeout => session.connect_timeout * 1000,
+ # send_timeout is ignored in JRuby
+ :so_timeout => session.receive_timeout * 1000,
+ :tcp_keepalive => session.tcp_keepalive,
+ :debug_dev => session.debug_dev
+ }
+ socket = nil
begin
if session.proxy
+ site = session.proxy || session.dest
+ socket = JavaSocketWrap.connect(Socket.new, site, opts)
session.connect_ssl_proxy(JavaSocketWrap.new(socket), Util.urify(session.dest.to_s))
end
+ new(socket, session.dest, session.ssl_config, opts)
rescue
- socket.close
+ socket.close if socket
raise
end
- new(socket, session.dest, session.ssl_config, session.debug_dev)
end
DEFAULT_SSL_PROTOCOL = (java.lang.System.getProperty('java.specification.version') == '1.7') ? 'TLSv1.2' : 'TLS'
- def initialize(socket, dest, config, debug_dev = nil)
+ def initialize(socket, dest, config, opts = {})
@config = config
+ begin
+ @ssl_socket = create_ssl_socket(socket, dest, config, opts)
+ ssl_version = java_ssl_version(config)
+ @ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
+ if config.ciphers != SSLConfig::CIPHERS_DEFAULT
+ @ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
+ end
+ ssl_connect(dest.host)
+ rescue java.security.GeneralSecurityException => e
+ raise OpenSSL::SSL::SSLError.new(e.getMessage)
+ rescue java.io.IOException => e
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
+ end
+
+ super(@ssl_socket, opts[:debug_dev])
+ end
+
+ def java_ssl_version(config)
if config.ssl_version == :auto
- ssl_version = DEFAULT_SSL_PROTOCOL
+ DEFAULT_SSL_PROTOCOL
else
- ssl_version = config.ssl_version.to_s.tr('_', '.')
+ config.ssl_version.to_s.tr('_', '.')
end
+ end
+
+ def create_ssl_context(config)
unless config.cert_store_crl_items.empty?
raise NotImplementedError.new('Manual CRL configuration is not yet supported')
end
km = nil
@@ -487,52 +533,51 @@
trust_store = loader.trust_store
end
tmf.init(trust_store)
tm = tmf.getTrustManagers
- ctx = SSLContext.getInstance(ssl_version)
+ ctx = SSLContext.getInstance(java_ssl_version(config))
ctx.init(km, tm, nil)
if config.timeout
ctx.getClientSessionContext.setSessionTimeout(config.timeout)
end
+ ctx
+ end
+ def create_ssl_socket(socket, dest, config, opts)
+ ctx = create_ssl_context(config)
factory = ctx.getSocketFactory
- begin
+ if socket
ssl_socket = factory.createSocket(socket, dest.host, dest.port, true)
- ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
- if config.ciphers != SSLConfig::CIPHERS_DEFAULT
- ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
- end
- ssl_socket.startHandshake
- ssl_session = ssl_socket.getSession
- @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
- if $DEBUG
- warn("Protocol version: #{ssl_session.getProtocol}")
- warn("Cipher: #{ssl_socket.getSession.getCipherSuite}")
- end
- post_connection_check(dest.host, @peer_cert)
- rescue java.security.GeneralSecurityException => e
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
- rescue javax.net.ssl.SSLException => e
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
- rescue java.net.SocketException => e
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
+ else
+ ssl_socket = factory.createSocket
+ JavaSocketWrap.connect(ssl_socket, dest, opts)
end
-
- super(ssl_socket, debug_dev)
+ ssl_socket
end
def peer_cert
@peer_cert
end
private
- def post_connection_check(hostname, wrap_cert)
+ def ssl_connect(hostname)
+ @ssl_socket.startHandshake
+ ssl_session = @ssl_socket.getSession
+ @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
+ if $DEBUG
+ warn("Protocol version: #{ssl_session.getProtocol}")
+ warn("Cipher: #{@ssl_socket.getSession.getCipherSuite}")
+ end
+ post_connection_check(hostname)
+ end
+
+ def post_connection_check(hostname)
if !@config.verify?
return
else
- BrowserCompatHostnameVerifier.new.verify(hostname, wrap_cert.cert)
+ BrowserCompatHostnameVerifier.new.verify(hostname, @peer_cert.cert)
end
end
end
SSLSocket = JRubySSLSocket