From 3328ad114597373d7f360f0fc8515f1d852bad18 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 27 Aug 2026 22:26:25 -0700 Subject: [PATCH] Autoload Resolv instead of requiring it eagerly net/http requires resolv at the top of the file, but the only thing it uses from that library is two regexp constants, on one line: case @address when Resolv::IPv4::Regex, Resolv::IPv6::Regex That line lives in the TLS branch of #connect, so resolv is not needed until an HTTPS connection is opened, and not at all for plain HTTP or for the many libraries that require net/http without connecting. Switching to autoload matches what the very next line of the file already does for OpenSSL, and it keeps Resolv resolvable for anything downstream that relied on net/http defining it -- the constant still works, the load just happens on first reference. Measured on Ruby 4.0.6 (arm64-darwin), best of seven runs: require 'net/http' files loaded before 76.94 ms 33 after 70.54 ms 30 -6.40 ms -3 (8% faster) Verified that requiring net/http no longer loads resolv, that Resolv::IPv4::Regex still resolves afterwards and pulls the library in on demand, and that a live HTTPS GET still succeeds. Test suite: 201 tests, 0 failures, unchanged. The three added tests cover all three properties; the first fails against the previous code. Signed-off-by: Tim Smith --- lib/net/http.rb | 2 +- test/net/http/test_require.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/net/http/test_require.rb diff --git a/lib/net/http.rb b/lib/net/http.rb index d1e9b2b..22f163b 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -22,8 +22,8 @@ require 'net/protocol' require 'uri' -require 'resolv' autoload :OpenSSL, 'openssl' +autoload :Resolv, 'resolv' module Net #:nodoc: diff --git a/test/net/http/test_require.rb b/test/net/http/test_require.rb new file mode 100644 index 0000000..2cfa3c4 --- /dev/null +++ b/test/net/http/test_require.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: false +require 'net/http' +require 'test/unit' + +# Guards the autoload of Resolv in net/http. The checks run in a subprocess +# because this one has resolv loaded already. +class HTTPRequireTest < Test::Unit::TestCase + RESOLV_LOADED = '$LOADED_FEATURES.any? { |f| File.basename(f) == "resolv.rb" }' + + def subprocess(script) + lib = File.expand_path('../../../lib', __dir__) + IO.popen([RbConfig.ruby, '-I', lib, '-e', script], &:read) + end + + def test_requiring_net_http_does_not_load_resolv + assert_equal 'false', subprocess("require 'net/http'; print #{RESOLV_LOADED}") + end + + def test_resolv_is_still_reachable_after_requiring_net_http + script = "require 'net/http'; print Resolv::IPv4::Regex.is_a?(Regexp)" + assert_equal 'true', subprocess(script) + end + + def test_referencing_resolv_loads_it + script = "require 'net/http'; Resolv::IPv4::Regex; print #{RESOLV_LOADED}" + assert_equal 'true', subprocess(script) + end +end