diff options
-rw-r--r-- | cspell.json | 6 | ||||
-rw-r--r-- | lib/chef/application/base.rb | 2 | ||||
-rw-r--r-- | lib/chef/monkey_patches/net-http.rb | 123 |
3 files changed, 131 insertions, 0 deletions
diff --git a/cspell.json b/cspell.json index ce2e83937e..13e5946b21 100644 --- a/cspell.json +++ b/cspell.json @@ -436,6 +436,7 @@ "getsebool", "getspnam", "gettext", + "gettime", "GETTHUMBPRINTCODE", "gettimezone", "gettype", @@ -572,11 +573,14 @@ "IPADDR", "ipaddr", "ipaddress", + "IPPROTO", "ireton", "isalnum", "isalpha", "isatty", "isdigit", + "IVNAMES", + "ivname", "Jagtap", "jailmode", "janky", @@ -829,6 +833,7 @@ "nobrowse", "NOCHANGES", "NOCOMPACT", + "NODELAY", "noconfirm", "Nodearray", "Nodename", @@ -1194,6 +1199,7 @@ "setools", "setsebool", "setsid", + "setsockopt", "settimezone", "SETTINGCHANGE", "setuid", diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb index f4c365d390..fd1d3db023 100644 --- a/lib/chef/application/base.rb +++ b/lib/chef/application/base.rb @@ -24,6 +24,8 @@ require "chef-utils/dist" unless defined?(ChefUtils::Dist) require_relative "../daemon" require "chef-config/mixin/dot_d" require "license_acceptance/cli_flags/mixlib_cli" +require "chef/monkey_patches/net-http" + module Mixlib autoload :Archive, "mixlib/archive" end diff --git a/lib/chef/monkey_patches/net-http.rb b/lib/chef/monkey_patches/net-http.rb new file mode 100644 index 0000000000..fc192faf71 --- /dev/null +++ b/lib/chef/monkey_patches/net-http.rb @@ -0,0 +1,123 @@ +if RUBY_VERSION.split(".")[0..1].join(".") == "3.1" + require "net/http" + # This is monkey-patch for ruby 3.1.x + # Due to change https://github.com/ruby/net-http/pull/10, when making net/http requests to a url which supports only IPv6 and not IPv4, + # ruby waits for IPv4 request to timeout first, then makes IPv6 request. This increased response time. + # NOTE 1: This is already reverted https://github.com/ruby/ruby/commit/f88bff770578583a708093f4a0d8b1483a1d2039 but under ruby 3.2.2 + # NOTE 2: We are patching action `connect` from here https://github.com/ruby/ruby/blob/f88bff770578583a708093f4a0d8b1483a1d2039/lib/net/http.rb#L1000 + + module Net + class HTTP < Protocol + def connect + if use_ssl? + # reference early to load OpenSSL before connecting, + # as OpenSSL may take time to load. + @ssl_context = OpenSSL::SSL::SSLContext.new + end + + if proxy? + conn_addr = proxy_address + conn_port = proxy_port + else + conn_addr = conn_address + conn_port = port + end + + puts "opening connection to #{conn_addr}:#{conn_port}..." + s = Timeout.timeout(@open_timeout, Net::OpenTimeout) { + begin + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + rescue => e + raise e, "Failed to open TCP connection to " + + "#{conn_addr}:#{conn_port} (#{e.message})" + end + } + s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + puts "opened" + if use_ssl? + if proxy? + plain_sock = BufferedIO.new(s, read_timeout: @read_timeout, + write_timeout: @write_timeout, + continue_timeout: @continue_timeout, + debug_output: @debug_output) + buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n" + buf << "Host: #{@address}:#{@port}\r\n" + if proxy_user + credential = ["#{proxy_user}:#{proxy_pass}"].pack("m0") + buf << "Proxy-Authorization: Basic #{credential}\r\n" + end + buf << "\r\n" + plain_sock.write(buf) + HTTPResponse.read_new(plain_sock).value + # assuming nothing left in buffers after successful CONNECT response + end + + ssl_parameters = {} + iv_list = instance_variables + SSL_IVNAMES.each_with_index do |ivname, i| + if iv_list.include?(ivname) + value = instance_variable_get(ivname) + unless value.nil? + ssl_parameters[SSL_ATTRIBUTES[i]] = value + end + end + end + @ssl_context.set_params(ssl_parameters) + unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby + @ssl_context.session_cache_mode = + OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT | + OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE + end + if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby + @ssl_context.session_new_cb = proc { |sock, sess| @ssl_session = sess } + end + + # Still do the post_connection_check below even if connecting + # to IP address + verify_hostname = @ssl_context.verify_hostname + + # Server Name Indication (SNI) RFC 3546/6066 + case @address + when Resolv::IPv4::Regex, Resolv::IPv6::Regex + # don't set SNI, as IP addresses in SNI is not valid + # per RFC 6066, section 3. + + # Avoid openssl warning + @ssl_context.verify_hostname = false + else + ssl_host_address = @address + end + + puts "starting SSL for #{conn_addr}:#{conn_port}..." + s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) + s.sync_close = true + s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address + + if @ssl_session && + (Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout) + s.session = @ssl_session + end + ssl_socket_connect(s, @open_timeout) + if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname + s.post_connection_check(@address) + end + puts "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}" + end + @socket = BufferedIO.new(s, read_timeout: @read_timeout, + write_timeout: @write_timeout, + continue_timeout: @continue_timeout, + debug_output: @debug_output) + @last_communicated = nil + on_connect + rescue => exception + if s + puts "Conn close because of connect error #{exception}" + s.close + end + raise + end + end + end +else + warn "Not applying net/http monkey patch needed for ruby 3.1" +end
\ No newline at end of file |