mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* test/openssl/test_ssl_session.rb: Split out SSL::Session related
tests from test_ssl.rb git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32195 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
40508ede73
commit
02ab6d95be
4 changed files with 277 additions and 268 deletions
|
@ -1,3 +1,8 @@
|
|||
Wed Jun 22 12:38:52 2011 Hiroshi Nakamura <nahi@ruby-lang.org>
|
||||
|
||||
* test/openssl/test_ssl_session.rb: Split out SSL::Session related
|
||||
tests from test_ssl.rb
|
||||
|
||||
Wed Jun 22 03:20:52 2011 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||
|
||||
* ext/psych/lib/psych/visitors/to_ruby.rb: Fix cyclic references of
|
||||
|
|
|
@ -2,152 +2,7 @@ require_relative "utils"
|
|||
|
||||
if defined?(OpenSSL)
|
||||
|
||||
class OpenSSL::TestSSL < Test::Unit::TestCase
|
||||
RUBY = EnvUtil.rubybin
|
||||
SSL_SERVER = File.join(File.dirname(__FILE__), "ssl_server.rb")
|
||||
PORT = 20443
|
||||
ITERATIONS = ($0 == __FILE__) ? 100 : 10
|
||||
|
||||
def setup
|
||||
@ca_key = OpenSSL::TestUtils::TEST_KEY_RSA2048
|
||||
@svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
|
||||
@cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256
|
||||
@ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
|
||||
@svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
||||
@cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
||||
|
||||
now = Time.at(Time.now.to_i)
|
||||
ca_exts = [
|
||||
["basicConstraints","CA:TRUE",true],
|
||||
["keyUsage","cRLSign,keyCertSign",true],
|
||||
]
|
||||
ee_exts = [
|
||||
["keyUsage","keyEncipherment,digitalSignature",true],
|
||||
]
|
||||
@ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts,
|
||||
nil, nil, OpenSSL::Digest::SHA1.new)
|
||||
@svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts,
|
||||
@ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
|
||||
@cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts,
|
||||
@ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
|
||||
@server = nil
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
def issue_cert(*arg)
|
||||
OpenSSL::TestUtils.issue_cert(*arg)
|
||||
end
|
||||
|
||||
def issue_crl(*arg)
|
||||
OpenSSL::TestUtils.issue_crl(*arg)
|
||||
end
|
||||
|
||||
def readwrite_loop(ctx, ssl)
|
||||
while line = ssl.gets
|
||||
if line =~ /^STARTTLS$/
|
||||
ssl.accept
|
||||
next
|
||||
end
|
||||
ssl.write(line)
|
||||
end
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
rescue IOError
|
||||
ensure
|
||||
ssl.close rescue nil
|
||||
end
|
||||
|
||||
def server_loop(ctx, ssls, server_proc)
|
||||
loop do
|
||||
ssl = nil
|
||||
begin
|
||||
ssl = ssls.accept
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
retry
|
||||
end
|
||||
|
||||
Thread.start do
|
||||
Thread.current.abort_on_exception = true
|
||||
server_proc.call(ctx, ssl)
|
||||
end
|
||||
end
|
||||
rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK
|
||||
end
|
||||
|
||||
DHParam = OpenSSL::PKey::DH.new(128)
|
||||
def start_server(port0, verify_mode, start_immediately, args = {}, &block)
|
||||
ctx_proc = args[:ctx_proc]
|
||||
server_proc = args[:server_proc]
|
||||
server_proc ||= method(:readwrite_loop)
|
||||
|
||||
store = OpenSSL::X509::Store.new
|
||||
store.add_cert(@ca_cert)
|
||||
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.cert_store = store
|
||||
#ctx.extra_chain_cert = [ ca_cert ]
|
||||
ctx.cert = @svr_cert
|
||||
ctx.key = @svr_key
|
||||
ctx.tmp_dh_callback = proc { DHParam }
|
||||
ctx.verify_mode = verify_mode
|
||||
ctx_proc.call(ctx) if ctx_proc
|
||||
|
||||
Socket.do_not_reverse_lookup = true
|
||||
tcps = nil
|
||||
port = port0
|
||||
begin
|
||||
tcps = TCPServer.new("127.0.0.1", port)
|
||||
rescue Errno::EADDRINUSE
|
||||
port += 1
|
||||
retry
|
||||
end
|
||||
|
||||
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
|
||||
ssls.start_immediately = start_immediately
|
||||
|
||||
begin
|
||||
server = Thread.new do
|
||||
Thread.current.abort_on_exception = true
|
||||
server_loop(ctx, ssls, server_proc)
|
||||
end
|
||||
|
||||
$stderr.printf("%s started: pid=%d port=%d\n", SSL_SERVER, pid, port) if $DEBUG
|
||||
|
||||
block.call(server, port.to_i)
|
||||
ensure
|
||||
begin
|
||||
begin
|
||||
tcps.shutdown
|
||||
rescue Errno::ENOTCONN
|
||||
# when `Errno::ENOTCONN: Socket is not connected' on some platforms,
|
||||
# call #close instead of #shutdown.
|
||||
tcps.close
|
||||
tcps = nil
|
||||
end if (tcps)
|
||||
if (server)
|
||||
server.join(5)
|
||||
if server.alive?
|
||||
server.kill
|
||||
server.join
|
||||
flunk("TCPServer was closed and SSLServer is still alive") unless $!
|
||||
end
|
||||
end
|
||||
ensure
|
||||
tcps.close if (tcps)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def starttls(ssl)
|
||||
ssl.puts("STARTTLS")
|
||||
|
||||
sleep 1 # When this line is eliminated, process on Cygwin blocks
|
||||
# forever at ssl.connect. But I don't know why it does.
|
||||
|
||||
ssl.connect
|
||||
end
|
||||
|
||||
class OpenSSL::TestSSL < OpenSSL::SSLTestCase
|
||||
def test_ctx_setup
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
assert_equal(ctx.setup, true)
|
||||
|
@ -445,128 +300,6 @@ class OpenSSL::TestSSL < Test::Unit::TestCase
|
|||
}
|
||||
end
|
||||
|
||||
def test_client_session
|
||||
last_session = nil
|
||||
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
|
||||
2.times do
|
||||
sock = TCPSocket.new("127.0.0.1", port)
|
||||
# Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
|
||||
# when use default SSLContext. [ruby-dev:36167]
|
||||
ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.session = last_session if last_session
|
||||
ssl.connect
|
||||
|
||||
session = ssl.session
|
||||
if last_session
|
||||
assert(ssl.session_reused?)
|
||||
|
||||
if session.respond_to?(:id)
|
||||
assert_equal(session.id, last_session.id)
|
||||
end
|
||||
assert_equal(session.to_pem, last_session.to_pem)
|
||||
assert_equal(session.to_der, last_session.to_der)
|
||||
# Older version of OpenSSL may not be consistent. Look up which versions later.
|
||||
assert_equal(session.to_text, last_session.to_text)
|
||||
else
|
||||
assert(!ssl.session_reused?)
|
||||
end
|
||||
last_session = session
|
||||
|
||||
str = "x" * 100 + "\n"
|
||||
ssl.puts(str)
|
||||
assert_equal(str, ssl.gets)
|
||||
|
||||
ssl.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_server_session
|
||||
connections = 0
|
||||
saved_session = nil
|
||||
|
||||
ctx_proc = Proc.new do |ctx, ssl|
|
||||
# add test for session callbacks here
|
||||
end
|
||||
|
||||
server_proc = Proc.new do |ctx, ssl|
|
||||
session = ssl.session
|
||||
stats = ctx.session_cache_stats
|
||||
|
||||
case connections
|
||||
when 0
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 0)
|
||||
assert_equal(stats[:cache_misses], 0)
|
||||
assert(!ssl.session_reused?)
|
||||
when 1
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 1)
|
||||
assert_equal(stats[:cache_misses], 0)
|
||||
assert(ssl.session_reused?)
|
||||
ctx.session_remove(session)
|
||||
saved_session = session
|
||||
when 2
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 1)
|
||||
assert_equal(stats[:cache_misses], 1)
|
||||
assert(!ssl.session_reused?)
|
||||
ctx.session_add(saved_session)
|
||||
when 3
|
||||
assert_equal(stats[:cache_num], 2)
|
||||
assert_equal(stats[:cache_hits], 2)
|
||||
assert_equal(stats[:cache_misses], 1)
|
||||
assert(ssl.session_reused?)
|
||||
ctx.flush_sessions(Time.now + 5000)
|
||||
when 4
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 2)
|
||||
assert_equal(stats[:cache_misses], 2)
|
||||
assert(!ssl.session_reused?)
|
||||
ctx.session_add(saved_session)
|
||||
end
|
||||
connections += 1
|
||||
|
||||
readwrite_loop(ctx, ssl)
|
||||
end
|
||||
|
||||
first_session = nil
|
||||
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
|
||||
10.times do |i|
|
||||
sock = TCPSocket.new("127.0.0.1", port)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
if defined?(OpenSSL::SSL::OP_NO_TICKET)
|
||||
# disable RFC4507 support
|
||||
ctx.options = OpenSSL::SSL::OP_NO_TICKET
|
||||
end
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.session = first_session if first_session
|
||||
ssl.connect
|
||||
|
||||
session = ssl.session
|
||||
if first_session
|
||||
case i
|
||||
when 1; assert(ssl.session_reused?)
|
||||
when 2; assert(!ssl.session_reused?)
|
||||
when 3; assert(ssl.session_reused?)
|
||||
when 4; assert(!ssl.session_reused?)
|
||||
when 5..10; assert(ssl.session_reused?)
|
||||
end
|
||||
end
|
||||
first_session ||= session
|
||||
|
||||
str = "x" * 100 + "\n"
|
||||
ssl.puts(str)
|
||||
assert_equal(str, ssl.gets)
|
||||
|
||||
ssl.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_tlsext_hostname
|
||||
return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname)
|
||||
|
||||
|
|
129
test/openssl/test_ssl_session.rb
Normal file
129
test/openssl/test_ssl_session.rb
Normal file
|
@ -0,0 +1,129 @@
|
|||
require_relative "utils"
|
||||
|
||||
if defined?(OpenSSL)
|
||||
|
||||
class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
|
||||
def test_client_session
|
||||
last_session = nil
|
||||
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
|
||||
2.times do
|
||||
sock = TCPSocket.new("127.0.0.1", port)
|
||||
# Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
|
||||
# when use default SSLContext. [ruby-dev:36167]
|
||||
ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.session = last_session if last_session
|
||||
ssl.connect
|
||||
|
||||
session = ssl.session
|
||||
if last_session
|
||||
assert(ssl.session_reused?)
|
||||
|
||||
if session.respond_to?(:id)
|
||||
assert_equal(session.id, last_session.id)
|
||||
end
|
||||
assert_equal(session.to_pem, last_session.to_pem)
|
||||
assert_equal(session.to_der, last_session.to_der)
|
||||
# Older version of OpenSSL may not be consistent. Look up which versions later.
|
||||
assert_equal(session.to_text, last_session.to_text)
|
||||
else
|
||||
assert(!ssl.session_reused?)
|
||||
end
|
||||
last_session = session
|
||||
|
||||
str = "x" * 100 + "\n"
|
||||
ssl.puts(str)
|
||||
assert_equal(str, ssl.gets)
|
||||
|
||||
ssl.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_server_session
|
||||
connections = 0
|
||||
saved_session = nil
|
||||
|
||||
ctx_proc = Proc.new do |ctx, ssl|
|
||||
# add test for session callbacks here
|
||||
end
|
||||
|
||||
server_proc = Proc.new do |ctx, ssl|
|
||||
session = ssl.session
|
||||
stats = ctx.session_cache_stats
|
||||
|
||||
case connections
|
||||
when 0
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 0)
|
||||
assert_equal(stats[:cache_misses], 0)
|
||||
assert(!ssl.session_reused?)
|
||||
when 1
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 1)
|
||||
assert_equal(stats[:cache_misses], 0)
|
||||
assert(ssl.session_reused?)
|
||||
ctx.session_remove(session)
|
||||
saved_session = session
|
||||
when 2
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 1)
|
||||
assert_equal(stats[:cache_misses], 1)
|
||||
assert(!ssl.session_reused?)
|
||||
ctx.session_add(saved_session)
|
||||
when 3
|
||||
assert_equal(stats[:cache_num], 2)
|
||||
assert_equal(stats[:cache_hits], 2)
|
||||
assert_equal(stats[:cache_misses], 1)
|
||||
assert(ssl.session_reused?)
|
||||
ctx.flush_sessions(Time.now + 5000)
|
||||
when 4
|
||||
assert_equal(stats[:cache_num], 1)
|
||||
assert_equal(stats[:cache_hits], 2)
|
||||
assert_equal(stats[:cache_misses], 2)
|
||||
assert(!ssl.session_reused?)
|
||||
ctx.session_add(saved_session)
|
||||
end
|
||||
connections += 1
|
||||
|
||||
readwrite_loop(ctx, ssl)
|
||||
end
|
||||
|
||||
first_session = nil
|
||||
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
|
||||
10.times do |i|
|
||||
sock = TCPSocket.new("127.0.0.1", port)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
if defined?(OpenSSL::SSL::OP_NO_TICKET)
|
||||
# disable RFC4507 support
|
||||
ctx.options = OpenSSL::SSL::OP_NO_TICKET
|
||||
end
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.sync_close = true
|
||||
ssl.session = first_session if first_session
|
||||
ssl.connect
|
||||
|
||||
session = ssl.session
|
||||
if first_session
|
||||
case i
|
||||
when 1; assert(ssl.session_reused?)
|
||||
when 2; assert(!ssl.session_reused?)
|
||||
when 3; assert(ssl.session_reused?)
|
||||
when 4; assert(!ssl.session_reused?)
|
||||
when 5..10; assert(ssl.session_reused?)
|
||||
end
|
||||
end
|
||||
first_session ||= session
|
||||
|
||||
str = "x" * 100 + "\n"
|
||||
ssl.puts(str)
|
||||
assert_equal(str, ssl.gets)
|
||||
|
||||
ssl.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -168,4 +168,146 @@ aPgwHyJBiK1/ebK3tYcrSKrOoRyrAgEC
|
|||
$VERBOSE = back
|
||||
end
|
||||
end
|
||||
|
||||
class OpenSSL::SSLTestCase < Test::Unit::TestCase
|
||||
RUBY = EnvUtil.rubybin
|
||||
SSL_SERVER = File.join(File.dirname(__FILE__), "ssl_server.rb")
|
||||
PORT = 20443
|
||||
ITERATIONS = ($0 == __FILE__) ? 100 : 10
|
||||
|
||||
def setup
|
||||
@ca_key = OpenSSL::TestUtils::TEST_KEY_RSA2048
|
||||
@svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
|
||||
@cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256
|
||||
@ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
|
||||
@svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
||||
@cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
||||
now = Time.at(Time.now.to_i)
|
||||
ca_exts = [
|
||||
["basicConstraints","CA:TRUE",true],
|
||||
["keyUsage","cRLSign,keyCertSign",true],
|
||||
]
|
||||
ee_exts = [
|
||||
["keyUsage","keyEncipherment,digitalSignature",true],
|
||||
]
|
||||
@ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new)
|
||||
@svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
|
||||
@cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
|
||||
@server = nil
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
def issue_cert(*arg)
|
||||
OpenSSL::TestUtils.issue_cert(*arg)
|
||||
end
|
||||
|
||||
def issue_crl(*arg)
|
||||
OpenSSL::TestUtils.issue_crl(*arg)
|
||||
end
|
||||
|
||||
def readwrite_loop(ctx, ssl)
|
||||
while line = ssl.gets
|
||||
if line =~ /^STARTTLS$/
|
||||
ssl.accept
|
||||
next
|
||||
end
|
||||
ssl.write(line)
|
||||
end
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
rescue IOError
|
||||
ensure
|
||||
ssl.close rescue nil
|
||||
end
|
||||
|
||||
def server_loop(ctx, ssls, server_proc)
|
||||
loop do
|
||||
ssl = nil
|
||||
begin
|
||||
ssl = ssls.accept
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
retry
|
||||
end
|
||||
|
||||
Thread.start do
|
||||
Thread.current.abort_on_exception = true
|
||||
server_proc.call(ctx, ssl)
|
||||
end
|
||||
end
|
||||
rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK
|
||||
end
|
||||
|
||||
DHParam = OpenSSL::PKey::DH.new(128)
|
||||
def start_server(port0, verify_mode, start_immediately, args = {}, &block)
|
||||
ctx_proc = args[:ctx_proc]
|
||||
server_proc = args[:server_proc]
|
||||
server_proc ||= method(:readwrite_loop)
|
||||
|
||||
store = OpenSSL::X509::Store.new
|
||||
store.add_cert(@ca_cert)
|
||||
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.cert_store = store
|
||||
#ctx.extra_chain_cert = [ ca_cert ]
|
||||
ctx.cert = @svr_cert
|
||||
ctx.key = @svr_key
|
||||
ctx.tmp_dh_callback = proc { DHParam }
|
||||
ctx.verify_mode = verify_mode
|
||||
ctx_proc.call(ctx) if ctx_proc
|
||||
|
||||
Socket.do_not_reverse_lookup = true
|
||||
tcps = nil
|
||||
port = port0
|
||||
begin
|
||||
tcps = TCPServer.new("127.0.0.1", port)
|
||||
rescue Errno::EADDRINUSE
|
||||
port += 1
|
||||
retry
|
||||
end
|
||||
|
||||
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
|
||||
ssls.start_immediately = start_immediately
|
||||
|
||||
begin
|
||||
server = Thread.new do
|
||||
Thread.current.abort_on_exception = true
|
||||
server_loop(ctx, ssls, server_proc)
|
||||
end
|
||||
|
||||
$stderr.printf("%s started: pid=%d port=%d\n", SSL_SERVER, pid, port) if $DEBUG
|
||||
|
||||
block.call(server, port.to_i)
|
||||
ensure
|
||||
begin
|
||||
begin
|
||||
tcps.shutdown
|
||||
rescue Errno::ENOTCONN
|
||||
# when `Errno::ENOTCONN: Socket is not connected' on some platforms,
|
||||
# call #close instead of #shutdown.
|
||||
tcps.close
|
||||
tcps = nil
|
||||
end if (tcps)
|
||||
if (server)
|
||||
server.join(5)
|
||||
if server.alive?
|
||||
server.kill
|
||||
server.join
|
||||
flunk("TCPServer was closed and SSLServer is still alive") unless $!
|
||||
end
|
||||
end
|
||||
ensure
|
||||
tcps.close if (tcps)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def starttls(ssl)
|
||||
ssl.puts("STARTTLS")
|
||||
sleep 1 # When this line is eliminated, process on Cygwin blocks
|
||||
# forever at ssl.connect. But I don't know why it does.
|
||||
ssl.connect
|
||||
end
|
||||
end
|
||||
|
||||
end if defined?(OpenSSL)
|
||||
|
|
Loading…
Reference in a new issue