diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb index dccc11a55..9b1539840 100644 --- a/lib/openssl/ssl.rb +++ b/lib/openssl/ssl.rb @@ -15,7 +15,11 @@ if defined?(OpenSSL::SSL) require "io/nonblock" -require "ipaddr" +# Only OpenSSL::SSL.verify_certificate_identity uses IPAddr, and only for +# certificates carrying an iPAddress SAN. Autoloading keeps ipaddr (and the +# socket library it pulls in) off the require path for everyone else, while +# leaving the IPAddr constant resolvable as before. +autoload :IPAddr, "ipaddr" require "socket" module OpenSSL diff --git a/test/openssl/test_require.rb b/test/openssl/test_require.rb new file mode 100644 index 000000000..06e407867 --- /dev/null +++ b/test/openssl/test_require.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true +require_relative "utils" + +class OpenSSL::TestRequire < OpenSSL::TestCase + IPADDR_LOADED = '$LOADED_FEATURES.any? { |f| File.basename(f) == "ipaddr.rb" }' + + def subprocess(script) + lib = File.expand_path("../../lib", __dir__) + IO.popen([RbConfig.ruby, "-I", lib, "-e", script], &:read) + end + + def test_requiring_openssl_does_not_load_ipaddr + assert_equal "false", subprocess("require 'openssl'; print #{IPADDR_LOADED}") + end + + def test_ipaddr_is_still_reachable_after_requiring_openssl + assert_equal "127.0.0.1", subprocess("require 'openssl'; print IPAddr.new('127.0.0.1').to_s") + end + + def test_referencing_ipaddr_loads_it + script = "require 'openssl'; IPAddr.new('127.0.0.1'); print #{IPADDR_LOADED}" + assert_equal "true", subprocess(script) + end +end