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