diff --git a/Manifest.txt b/Manifest.txt index 5304cd96eb12..10f60a801a10 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -566,7 +566,7 @@ lib/rubygems/vendor/net-http/lib/net/http/response.rb lib/rubygems/vendor/net-http/lib/net/http/responses.rb lib/rubygems/vendor/net-http/lib/net/http/status.rb lib/rubygems/vendor/net-http/lib/net/https.rb -lib/rubygems/vendor/net-protocol/LICENSE.txt +lib/rubygems/vendor/net-protocol/COPYING lib/rubygems/vendor/net-protocol/lib/net/protocol.rb lib/rubygems/vendor/optparse/COPYING lib/rubygems/vendor/optparse/lib/optionparser.rb diff --git a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb index 8cb81be7301a..e8aaf70016bd 100644 --- a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +++ b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb @@ -104,6 +104,9 @@ def initialize(options = {}, &block) end def with(options = {}) + # We need to manage exception handling manually here in order + # to work correctly with `Gem::Timeout.timeout` and `Thread#raise`. + # Otherwise an interrupted Thread can leak connections. Thread.handle_interrupt(Exception => :never) do conn = checkout(options) begin diff --git a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb index 364662d801b4..026d2c5be27a 100644 --- a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +++ b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb @@ -54,9 +54,12 @@ def push(obj, options = {}) # immediately returned. If no connection is available within the given # timeout a Bundler::ConnectionPool::TimeoutError is raised. # - # +:timeout+ is the only checked entry in +options+ and is preferred over - # the +timeout+ argument (which will be removed in a future release). Other - # options may be used by subclasses that extend TimedStack. + # @option options [Float] :timeout (0.5) Wait this many seconds for an available entry + # @option options [Class] :exception (Bundler::ConnectionPool::TimeoutError) Exception class to raise + # if an entry was not available within the timeout period. Use `exception: false` to return nil. + # + # The +timeout+ argument will be removed in 3.0. + # Other options may be used by subclasses that extend TimedStack. def pop(timeout = 0.5, options = {}) options, timeout = timeout, 0.5 if Hash === timeout timeout = options.fetch :timeout, timeout @@ -73,7 +76,14 @@ def pop(timeout = 0.5, options = {}) return connection if connection to_wait = deadline - current_time - raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0 + if to_wait <= 0 + exc = options.fetch(:exception, Bundler::ConnectionPool::TimeoutError) + if exc + raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" + else + return nil + end + end @resource.wait(@mutex, to_wait) end end diff --git a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb index cbac68b3dc83..2e9eebdbb6d2 100644 --- a/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +++ b/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb @@ -1,3 +1,3 @@ class Bundler::ConnectionPool - VERSION = "2.5.4" + VERSION = "2.5.5" end diff --git a/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb b/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb index e2008c4e512d..61763cc511a0 100644 --- a/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +++ b/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb @@ -181,7 +181,7 @@ # module Bundler::FileUtils # The version number. - VERSION = "1.8.0" + VERSION = "1.8.1" def self.private_module_function(name) #:nodoc: module_function name @@ -279,38 +279,67 @@ def remove_trailing_slash(dir) #:nodoc: end private_module_function :remove_trailing_slash + # :markup: markdown # - # Creates directories at the paths in the given +list+ - # (a single path or an array of paths); - # returns +list+ if it is an array, [list] otherwise. + # call-seq: + # Bundler::FileUtils.mkdir(*paths, mode: 0775, noop: nil, verbose: nil) -> array_of_paths or nil # - # Argument +list+ or its elements - # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]. + # Argument `paths` may be one path or an array of paths; + # each must be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]. # - # With no keyword arguments, creates a directory at each +path+ in +list+ - # by calling: Dir.mkdir(path, mode); - # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]: + # By default, creates a directory entry at each of the given `paths`; + # each directory has permissions `0775`; + # returns an array of the paths: # - # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"] - # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"] + # ```ruby + # Bundler::FileUtils.mkdir('foo') # => ["foo"] + # Bundler::FileUtils.mkdir(%w[bar baz]) # => ["bar", "baz"] + # File.stat(Dir.new('foo')).mode.to_s(8) # => "40775" + # File.stat(Dir.new('bar')).mode.to_s(8) # => "40775" + # File.stat(Dir.new('baz')).mode.to_s(8) # => "40775" + # Bundler::FileUtils.rmdir(%w[foo bar baz]) # => ["foo", "bar", "baz"] + # ``` # - # Keyword arguments: + # Raises an exception if for any reason a directory cannot be created; + # a common reason is that the directory already exists. # - # - mode: mode - also calls File.chmod(mode, path); - # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod]. - # - noop: true - does not create directories. - # - verbose: true - prints an equivalent command: + # With keyword argument `mode` given, + # creates directories with the given permissions: # - # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true) - # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true) + # ```ruby + # Bundler::FileUtils.mkdir('bar', mode: 0664) + # File.stat(Dir.new('bar')).mode.to_s(8) # => "40664" + # Bundler::FileUtils.rmdir('bar') + # ``` # - # Output: + # With keyword argument `noop` given as `true`, + # does not create directories; + # returns `nil` + # + # ```ruby + # Bundler::FileUtils.mkdir('foo', noop: true) # => nil + # Dir.exist?('foo') # => false + # ``` + # + # With keyword argument `verbose` given as `true`, + # prints a shell command: # - # mkdir tmp0 tmp1 - # mkdir -m 700 tmp2 tmp3 + # ```ruby + # Bundler::FileUtils.mkdir('foo', verbose: true) + # Bundler::FileUtils.mkdir(%w[bar baz], mode: 0644, verbose: true) + # Bundler::FileUtils.mkdir(%w[bar baz], mode: 0644, verbose: true, noop: true) + # ``` # - # Raises an exception if any path points to an existing - # file or directory, or if for any reason a directory cannot be created. + # Output: + # + # ```text + # mkdir foo + # mkdir -m 644 bar baz + # mkdir -m 644 bar baz + # ``` + # + # Note that in the last call above (containing `noop: true`) + # the existence of the directory is not checked. # # Related: Bundler::FileUtils.mkdir_p. # @@ -686,7 +715,7 @@ def cp_lr(src, dest, noop: nil, verbose: nil, # Keyword arguments: # # - force: true - overwrites +dest+ if it exists. - # - relative: false - create links relative to +dest+. + # - relative: true - create links relative to +dest+. # - noop: true - does not create links. # - verbose: true - prints an equivalent command: # @@ -754,7 +783,7 @@ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil n = real_ddirs.size - i n -= 1 unless target_directory link2 = fu_clean_components(*Array.new([n, 0].max, '..'), *real_sdirs[i..-1]) - link1 = link2 if link1.size > link2.size + link1 = link2 if !link2.empty? and link1.size > link2.size end s = File.join(link1) fu_output_message [cmd, s, d].flatten.join(' ') if verbose @@ -2037,10 +2066,6 @@ def fu_windows?; true end #:nodoc: def fu_windows?; false end #:nodoc: end - def fu_copy_stream0(src, dest, blksize = nil) #:nodoc: - IO.copy_stream(src, dest) - end - def fu_stream_blksize(*streams) #:nodoc: streams.each do |s| next unless s.respond_to?(:stat) diff --git a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb index 93e403a5bb36..00f2bca14ad3 100644 --- a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +++ b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb @@ -1,10 +1,7 @@ require_relative '../../../../../vendored_net_http' require_relative '../../../../../vendored_uri' -begin - require 'cgi/escape' -rescue LoadError - require 'cgi/util' # for escaping -end +require 'cgi/escape' +require 'cgi/util' unless defined?(CGI::EscapeExt) require_relative '../../../../connection_pool/lib/connection_pool' autoload :OpenSSL, 'openssl' @@ -180,7 +177,7 @@ class Gem::Net::HTTP::Persistent ## # The version of Gem::Net::HTTP::Persistent you are using - VERSION = '4.0.6' + VERSION = '4.0.8' ## # Error class for errors raised by Gem::Net::HTTP::Persistent. Various @@ -475,6 +472,13 @@ def self.detect_idle_timeout uri, max = 10 attr_reader :verify_hostname + + ## + # Sets whether to ignore end-of-file when reading a response body + # with Content-Length headers. + + attr_accessor :ignore_eof + ## # Creates a new Gem::Net::HTTP::Persistent. # @@ -514,6 +518,7 @@ def initialize name: nil, proxy: nil, pool_size: DEFAULT_POOL_SIZE @max_retries = 1 @socket_options = [] @ssl_generation = 0 # incremented when SSL session variables change + @ignore_eof = nil @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if Socket.const_defined? :TCP_NODELAY @@ -642,6 +647,7 @@ def connection_for uri reset connection end + http.ignore_eof = @ignore_eof if @ignore_eof http.keep_alive_timeout = @idle_timeout if @idle_timeout http.max_retries = @max_retries if http.respond_to?(:max_retries=) http.read_timeout = @read_timeout if @read_timeout diff --git a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb index 04a1e754bfdd..5e45ea77d1af 100644 --- a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +++ b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb @@ -4,7 +4,7 @@ class Gem::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool # :nodoc: attr_reader :key # :nodoc: def initialize(options = {}, &block) - super + super(**options, &block) @available = Gem::Net::HTTP::Persistent::TimedStackMulti.new(@size, &block) @key = "current-#{@available.object_id}" diff --git a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb index 034fbe39b811..57dfefdaae50 100644 --- a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +++ b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb @@ -1,5 +1,10 @@ class Gem::Net::HTTP::Persistent::TimedStackMulti < Bundler::ConnectionPool::TimedStack # :nodoc: + ## + # Detects if Bundler::ConnectionPool 3.0+ is being used (needed for TimedStack subclass compatibility) + + CP_USES_KEYWORD_ARGS = Gem::Version.new(Bundler::ConnectionPool::VERSION) >= Gem::Version.new('3.0.0') # :nodoc: + ## # Returns a new hash that has arrays for keys # @@ -11,7 +16,11 @@ def self.hash_of_arrays # :nodoc: end def initialize(size = 0, &block) - super + if CP_USES_KEYWORD_ARGS + super(size: size, &block) + else + super(size, &block) + end @enqueued = 0 @ques = self.class.hash_of_arrays diff --git a/bundler/lib/bundler/vendor/thor/lib/thor.rb b/bundler/lib/bundler/vendor/thor/lib/thor.rb index 945bdbd5515f..b78593a6ac3b 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor.rb @@ -625,7 +625,7 @@ def normalize_command_name(meth) #:nodoc: # alias name. def find_command_possibilities(meth) len = meth.to_s.length - possibilities = all_commands.reject { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort + possibilities = all_commands.reject {|k, v| v.is_a?(HiddenCommand) }.merge(map).keys.select { |n| meth == n[0, len] }.sort unique_possibilities = possibilities.map { |k| map[k] || k }.uniq if possibilities.include?(meth) @@ -671,4 +671,37 @@ def help(command = nil, subcommand = false) self.class.help(shell, subcommand) end end + + map TREE_MAPPINGS => :tree + + desc "tree", "Print a tree of all available commands" + def tree + build_command_tree(self.class, "") + end + +private + + def build_command_tree(klass, indent) + # Print current class name if it's not the root Bundler::Thor class + unless klass == Bundler::Thor + say "#{indent}#{klass.namespace || 'default'}", :blue + indent = "#{indent} " + end + + # Print all commands for this class + visible_commands = klass.commands.reject { |_, cmd| cmd.hidden? || cmd.name == "help" } + commands_count = visible_commands.count + visible_commands.sort.each_with_index do |(command_name, command), i| + description = command.description.split("\n").first || "" + icon = i == (commands_count - 1) ? "└─" : "├─" + say "#{indent}#{icon} ", nil, false + say command_name, :green, false + say " (#{description})" unless description.empty? + end + + # Print all subcommands (from registered Bundler::Thor subclasses) + klass.subcommand_classes.each do |_, subclass| + build_command_tree(subclass, indent) + end + end end diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb index 70526e615f18..229f294b10c1 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb @@ -2,6 +2,38 @@ class Bundler::Thor module Actions + WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"} + + # Injects the given content into a file, raising an error if the contents of + # the file are not changed. Different from gsub_file, this method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: give :verbose => false to not log the status and the flag + # for injection (:after or :before) or :force => true for + # insert two or more times the same content. + # + # ==== Examples + # + # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # + # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") + # end + # + def insert_into_file!(destination, *args, &block) + data = block_given? ? block : args.shift + + config = args.shift || {} + config[:after] = /\z/ unless config.key?(:before) || config.key?(:after) + config = config.merge({error_on_no_change: true}) + + action InjectIntoFile.new(self, destination, data, config) + end + alias_method :inject_into_file!, :insert_into_file! + # Injects the given content into a file. Different from gsub_file, this # method is reversible. # @@ -21,8 +53,6 @@ module Actions # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") # end # - WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"} - def insert_into_file(destination, *args, &block) data = block_given? ? block : args.shift @@ -47,6 +77,7 @@ def initialize(base, destination, data, config) @replacement = data.is_a?(Proc) ? data.call : data @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + @error_on_no_change = @config.fetch(:error_on_no_change, false) end def invoke! @@ -59,6 +90,8 @@ def invoke! if exists? if replace!(/#{flag}/, content, config[:force]) say_status(:invoke) + elsif @error_on_no_change + raise Bundler::Thor::Error, "The content of #{destination} did not change" elsif replacement_present? say_status(:unchanged, color: :blue) else diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/base.rb b/bundler/lib/bundler/vendor/thor/lib/thor/base.rb index b156899c1e0a..f378a48d589e 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/base.rb @@ -13,8 +13,9 @@ class Bundler::Thor autoload :RakeCompat, File.expand_path("rake_compat", __dir__) autoload :Group, File.expand_path("group", __dir__) - # Shortcuts for help. + # Shortcuts for help and tree commands. HELP_MAPPINGS = %w(-h -? --help -D) + TREE_MAPPINGS = %w(-t --tree) # Bundler::Thor methods that should not be overwritten by the user. THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index da02b9422758..fcd84f45e762 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -311,13 +311,11 @@ def file_collision_help(block_given) #:nodoc: end def show_diff(destination, content) #:nodoc: - diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" - require "tempfile" Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp| temp.write content temp.rewind - system %(#{diff_cmd} "#{destination}" "#{temp.path}") + system(*diff_tool, destination, temp.path) end end @@ -369,15 +367,25 @@ def answer_match(possibilities, answer, case_insensitive) def merge(destination, content) #:nodoc: require "tempfile" - Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| + Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp| temp.write content temp.rewind - system(merge_tool, temp.path, destination) + system(*merge_tool, temp.path, destination) end end def merge_tool #:nodoc: - @merge_tool ||= ENV["THOR_MERGE"] || "git difftool --no-index" + @merge_tool ||= begin + require "shellwords" + Shellwords.split(ENV["THOR_MERGE"] || "git difftool --no-index") + end + end + + def diff_tool #:nodoc: + @diff_cmd ||= begin + require "shellwords" + Shellwords.split(ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u") + end end end end diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb index 5d708fadcad2..b09675682c87 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "basic" class Bundler::Thor diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/version.rb b/bundler/lib/bundler/vendor/thor/lib/thor/version.rb index 5474a2f71bad..3fe3e3ae82e1 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "1.4.0" + VERSION = "1.5.0" end diff --git a/bundler/lib/bundler/vendor/uri/lib/uri/common.rb b/bundler/lib/bundler/vendor/uri/lib/uri/common.rb index 935392c19463..29ef7297469f 100644 --- a/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +++ b/bundler/lib/bundler/vendor/uri/lib/uri/common.rb @@ -271,7 +271,7 @@ def self.parse(uri) # # => # # def self.join(*str) - DEFAULT_PARSER.join(*str) + PARSER.join(*str) end # diff --git a/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb b/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb index 30dab609035b..e8326dda4f40 100644 --- a/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +++ b/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb @@ -466,7 +466,7 @@ def userinfo=(userinfo) # # uri = Bundler::URI.parse("http://john:S3nsit1ve@my.example.com") # uri.user = "sam" - # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com" + # uri.to_s #=> "http://sam@my.example.com" # def user=(user) check_user(user) @@ -1021,9 +1021,21 @@ def merge_path(base, rel) # RFC2396, Section 5.2, 6), a) base_path << '' if base_path.last == '..' - while i = base_path.index('..') - base_path.slice!(i - 1, 2) + # Remove "/.." pairs in a single left-to-right pass (O(n)). + # This deliberately differs from the relative-path stack handling + # below: a leading ".." (one with no preceding segment left to + # cancel) discards the whole base path, reproducing the previous + # index/slice! implementation exactly. + reduced = [] + base_path.each do |seg| + if seg == '..' + break if reduced.empty? + reduced.pop + else + reduced << seg + end end + base_path = reduced if (first = rel_path.first) and first.empty? base_path.clear diff --git a/bundler/lib/bundler/vendor/uri/lib/uri/version.rb b/bundler/lib/bundler/vendor/uri/lib/uri/version.rb index ad76308e81d1..e3ecb556085e 100644 --- a/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +++ b/bundler/lib/bundler/vendor/uri/lib/uri/version.rb @@ -1,6 +1,6 @@ module Bundler::URI # :stopdoc: - VERSION = '1.1.1'.freeze + VERSION = '1.1.2'.freeze VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze # :startdoc: end diff --git a/lib/rubygems/vendor/net-http/lib/net/http.rb b/lib/rubygems/vendor/net-http/lib/net/http.rb index e1ccc0561004..bdc4283eb380 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http.rb @@ -36,7 +36,7 @@ class HTTPHeaderSyntaxError < StandardError; end # For information about \HTTP, see: # # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol]. - # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview]. + # - {Technology}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technology]. # # == About the Examples # @@ -71,7 +71,7 @@ class HTTPHeaderSyntaxError < StandardError; end # # - If performance is important, consider using sessions, which lower request overhead. # This {session}[rdoc-ref:Gem::Net::HTTP@Sessions] has multiple requests for - # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods] + # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Method] # and {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]: # # Gem::Net::HTTP.start(hostname) do |http| @@ -81,6 +81,7 @@ class HTTPHeaderSyntaxError < StandardError; end # body = 'Some text' # http.post(path, body) # Can also have a block. # http.put(path, body) + # http.query(path, body) # Can also have a block. # http.delete(path) # http.options(path) # http.trace(path) @@ -197,7 +198,7 @@ class HTTPHeaderSyntaxError < StandardError; end # In the block, you can use these instance methods, # each of which that sends a single request: # - # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]: + # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Method]: # # - #get, #request_get: GET. # - #head, #request_head: HEAD. @@ -206,6 +207,7 @@ class HTTPHeaderSyntaxError < StandardError; end # - #options: OPTIONS. # - #trace: TRACE. # - #patch: PATCH. + # - #query, #request_query: QUERY. # # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]: # @@ -446,7 +448,7 @@ class HTTPHeaderSyntaxError < StandardError; end # if the response has header 'Content-Range'. # # Otherwise decompression (or not) depends on the value of header - # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-encoding-response-header]: + # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Content-Encoding_2]: # # - 'deflate', 'gzip', or 'x-gzip': # decompresses the body and deletes the header. @@ -459,7 +461,7 @@ class HTTPHeaderSyntaxError < StandardError; end # # First, what's elsewhere. Class Gem::Net::HTTP: # - # - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-Object-label-What-27s+Here]. + # - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-object-whats-here]. # # This is a categorized summary of methods and attributes. # @@ -552,6 +554,8 @@ class HTTPHeaderSyntaxError < StandardError; end # Sends a PROPPATCH request and returns a response object. # - {#put}[rdoc-ref:Gem::Net::HTTP#put]: # Sends a PUT request and returns a response object. + # - {#query}[rdoc-ref:Gem::Net::HTTP#query]: + # Sends a QUERY request and returns a response object. # - {#request}[rdoc-ref:Gem::Net::HTTP#request]: # Sends a request and returns a response object. # - {#request_get}[rdoc-ref:Gem::Net::HTTP#request_get]: @@ -723,7 +727,7 @@ class HTTPHeaderSyntaxError < StandardError; end class HTTP < Protocol # :stopdoc: - VERSION = "0.7.0" + VERSION = "0.10.0" HTTPVersion = '1.1' begin require 'zlib' @@ -1178,6 +1182,7 @@ def initialize(address, port = nil) # :nodoc: @debug_output = options[:debug_output] @response_body_encoding = options[:response_body_encoding] @ignore_eof = options[:ignore_eof] + @tcpsocket_supports_open_timeout = nil @proxy_from_env = false @proxy_uri = nil @@ -1302,7 +1307,7 @@ def response_body_encoding=(value) # Sets whether to determine the proxy from environment variable # 'ENV['http_proxy']'; - # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Gem::Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27]. + # see {Proxy Using ENV}[rdoc-ref:Gem::Net::HTTP@Proxy+Using+ENVHTTPProxy]. attr_writer :proxy_from_env # Sets the proxy address; @@ -1320,6 +1325,9 @@ def response_body_encoding=(value) # Sets the proxy password; # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server]. attr_writer :proxy_pass + + # Sets whether the proxy uses SSL; + # see {Proxy Server}[rdoc-ref:Gem::Net::HTTP@Proxy+Server]. attr_writer :proxy_use_ssl # Returns the IP address for the connection. @@ -1526,7 +1534,7 @@ def use_ssl=(flag) :verify_depth, :verify_mode, :verify_hostname, - ] # :nodoc: + ].freeze # :nodoc: SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym }.freeze # :nodoc: @@ -1568,11 +1576,11 @@ def use_ssl=(flag) attr_accessor :cert_store # Sets or returns the available SSL ciphers. - # See {OpenSSL::SSL::SSLContext#ciphers=}[OpenSSL::SSL::SSL::Context#ciphers=]. + # See {OpenSSL::SSL::SSLContext#ciphers=}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D]. attr_accessor :ciphers # Sets or returns the extra X509 certificates to be added to the certificate chain. - # See {OpenSSL::SSL::SSLContext#add_certificate}[OpenSSL::SSL::SSL::Context#add_certificate]. + # See {OpenSSL::SSL::SSLContext#add_certificate}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-add_certificate]. attr_accessor :extra_chain_cert # Sets or returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. @@ -1582,15 +1590,15 @@ def use_ssl=(flag) attr_accessor :ssl_timeout # Sets or returns the SSL version. - # See {OpenSSL::SSL::SSLContext#ssl_version=}[OpenSSL::SSL::SSL::Context#ssl_version=]. + # See {OpenSSL::SSL::SSLContext#ssl_version=}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D]. attr_accessor :ssl_version # Sets or returns the minimum SSL version. - # See {OpenSSL::SSL::SSLContext#min_version=}[OpenSSL::SSL::SSL::Context#min_version=]. + # See {OpenSSL::SSL::SSLContext#min_version=}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D]. attr_accessor :min_version # Sets or returns the maximum SSL version. - # See {OpenSSL::SSL::SSLContext#max_version=}[OpenSSL::SSL::SSL::Context#max_version=]. + # See {OpenSSL::SSL::SSLContext#max_version=}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D]. attr_accessor :max_version # Sets or returns the callback for the server certification verification. @@ -1606,7 +1614,7 @@ def use_ssl=(flag) # Sets or returns whether to verify that the server certificate is valid # for the hostname. - # See {OpenSSL::SSL::SSLContext#verify_hostname=}[OpenSSL::SSL::SSL::Context#verify_hostname=]. + # See {OpenSSL::SSL::SSLContext#verify_hostname=}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-verify_hostname]. attr_accessor :verify_hostname # Returns the X509 certificate chain (an array of strings) @@ -1654,6 +1662,21 @@ def start # :yield: http self end + # Finishes the \HTTP session: + # + # http = Gem::Net::HTTP.new(hostname) + # http.start + # http.started? # => true + # http.finish # => nil + # http.started? # => false + # + # Raises IOError if not in a session. + def finish + raise IOError, 'HTTP session not yet started' unless started? + do_finish + end + + # :stopdoc: def do_start connect @started = true @@ -1676,14 +1699,15 @@ def connect end debug "opening connection to #{conn_addr}:#{conn_port}..." - s = Gem::Timeout.timeout(@open_timeout, Gem::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})" + begin + s = timeouted_connect(conn_addr, conn_port) + rescue => e + if (defined?(IO::TimeoutError) && e.is_a?(IO::TimeoutError)) || e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions + e = Gem::Net::OpenTimeout.new(e) end - } + raise e, "Failed to open TCP connection to " + + "#{conn_addr}:#{conn_port} (#{e.message})" + end s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) debug "opened" if use_ssl? @@ -1776,23 +1800,30 @@ def connect end private :connect - def on_connect + tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]] + tcp_socket_parameters.include?([:key, :open_timeout]) + else + # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize + # See discussion in https://github.com/ruby/net-http/pull/224 + Socket.method(:tcp).parameters.include?([:key, :open_timeout]) end - private :on_connect + private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT - # Finishes the \HTTP session: - # - # http = Gem::Net::HTTP.new(hostname) - # http.start - # http.started? # => true - # http.finish # => nil - # http.started? # => false - # - # Raises IOError if not in a session. - def finish - raise IOError, 'HTTP session not yet started' unless started? - do_finish + def timeouted_connect(conn_addr, conn_port) + if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + else + Gem::Timeout.timeout(@open_timeout, Gem::Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } + end + end + private :timeouted_connect + + def on_connect end + private :on_connect def do_finish @started = false @@ -1843,6 +1874,8 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ss } end + # :startdoc: + class << HTTP # Returns true if self is a class which was created by HTTP::Proxy. def proxy_class? @@ -1937,6 +1970,7 @@ def proxy_pass alias proxyport proxy_port #:nodoc: obsolete private + # :stopdoc: def unescape(value) require 'cgi/escape' @@ -1965,6 +1999,7 @@ def edit_path(path) path end end + # :startdoc: # # HTTP operations @@ -2092,6 +2127,25 @@ def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segm send_entity(path, data, initheader, dest, Patch, &block) end + # Sends a QUERY request to the server; + # returns an instance of a subclass of Gem::Net::HTTPResponse. + # + # The request is based on the Gem::Net::HTTP::Query object + # created from string +path+, string +data+, and initial headers hash +initheader+. + # + # data = '{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}' + # http = Gem::Net::HTTP.new(hostname) + # http.query('/todos/1', data) # => # + # + # Related: + # + # - Gem::Net::HTTP::Query: request class for \HTTP method QUERY. + # - Gem::Net::HTTP.query: sends QUERY request, returns response body. + # + def query(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ + send_entity(path, data, initheader, dest, Query, &block) + end + # Sends a PUT request to the server; # returns an instance of a subclass of Gem::Net::HTTPResponse. # @@ -2326,6 +2380,36 @@ def request_put(path, data, initheader = nil, &block) #:nodoc: request Put.new(path, initheader), data, &block end + # Sends a QUERY request to the server; + # returns an instance of a subclass of Gem::Net::HTTPResponse. + # + # The request is based on the Gem::Net::HTTP::Query object + # created from string +path+, string +body+, and initial headers hash +initheader+. + # + # http = Gem::Net::HTTP.new(hostname) + # http.query('/todos/1', 'xyzzy') + # # => # + # + # With no block given, returns the response object: + # + # http = Gem::Net::HTTP.new(hostname) + # http.request_query('/todos') # => # + # + # With a block given, calls the block with the response object + # and returns the response object: + # + # http.request_query('/todos') do |res| + # p res + # end # => # + # + # Output: + # + # # + # + def request_query(path, data, initheader = nil, &block) # :yield: +response+ + request Query.new(path, initheader), data, &block + end + alias get2 request_get #:nodoc: obsolete alias head2 request_head #:nodoc: obsolete alias post2 request_post #:nodoc: obsolete @@ -2419,7 +2503,9 @@ def send_entity(path, data, initheader, dest, type, &block) res end - IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc: + # :stopdoc: + + IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE QUERY/.freeze # :nodoc: def transport_request(req) count = 0 @@ -2433,11 +2519,17 @@ def transport_request(req) # still read the received response. end + informational_count = 0 begin res = HTTPResponse.read_new(@socket) res.decode_content = req.decode_content res.body_encoding = @response_body_encoding res.ignore_eof = @ignore_eof + if res.kind_of?(HTTPInformation) + informational_count += 1 + raise HTTPBadResponse, 'too many informational responses' if + informational_count > HTTPResponse::MAX_INFORMATIONAL_RESPONSES + end end while res.kind_of?(HTTPInformation) res.uri = req.uri @@ -2576,7 +2668,7 @@ def debug(msg) alias_method :D, :debug end - # for backward compatibility until Ruby 3.5 + # for backward compatibility until Ruby 4.0 # https://bugs.ruby-lang.org/issues/20900 # https://github.com/bblimke/webmock/pull/1081 HTTPSession = HTTP diff --git a/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb b/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb index c629c0113b1a..218df9a8bd15 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb @@ -3,7 +3,7 @@ module Gem::Net # Gem::Net::HTTP exception class. # You cannot use Gem::Net::HTTPExceptions directly; instead, you must use # its subclasses. - module HTTPExceptions + module HTTPExceptions # :nodoc: def initialize(msg, res) #:nodoc: super msg @response = res @@ -12,6 +12,7 @@ def initialize(msg, res) #:nodoc: alias data response #:nodoc: obsolete end + # :stopdoc: class HTTPError < ProtocolError include HTTPExceptions end diff --git a/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb b/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb index 0737b3f02afa..dd49c1521637 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb @@ -19,16 +19,13 @@ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: if Gem::URI === uri_or_path then raise ArgumentError, "not an HTTP Gem::URI" unless Gem::URI::HTTP === uri_or_path - hostname = uri_or_path.hostname + hostname = uri_or_path.host raise ArgumentError, "no host component for Gem::URI" unless (hostname && hostname.length > 0) @uri = uri_or_path.dup - host = @uri.hostname.dup - host << ":" << @uri.port.to_s if @uri.port != @uri.default_port @path = uri_or_path.request_uri raise ArgumentError, "no HTTP request path given" unless @path else @uri = nil - host = nil raise ArgumentError, "no HTTP request path given" unless uri_or_path raise ArgumentError, "HTTP request path is empty" if uri_or_path.empty? @path = uri_or_path.dup @@ -51,7 +48,7 @@ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: initialize_http_header initheader self['Accept'] ||= '*/*' self['User-Agent'] ||= 'Ruby' - self['Host'] ||= host if host + self['Host'] ||= @uri.authority if @uri @body = nil @body_stream = nil @body_data = nil @@ -245,7 +242,7 @@ def update_uri(addr, port, ssl) # :nodoc: internal use only end if host = self['host'] - host.sub!(/:.*/m, '') + host = Gem::URI.parse("//#{host}").host # Remove a port component from the existing Host header elsif host = @uri.host else host = addr @@ -264,6 +261,8 @@ def update_uri(addr, port, ssl) # :nodoc: internal use only private + # :stopdoc: + class Chunker #:nodoc: def initialize(sock) @sock = sock @@ -337,6 +336,9 @@ def encode_multipart_form_data(out, params, opt) boundary = opt[:boundary] require 'securerandom' unless defined?(SecureRandom) boundary ||= SecureRandom.urlsafe_base64(40) + if /[\r\n]/.match?(boundary.to_s) + raise ArgumentError, "multipart boundary cannot include CR/LF" + end chunked_p = chunked? buf = +'' @@ -350,7 +352,10 @@ def encode_multipart_form_data(out, params, opt) buf << "--#{boundary}\r\n" if filename filename = quote_string(filename, charset) - type = h[:content_type] || 'application/octet-stream' + type = (h[:content_type] || 'application/octet-stream').to_s + if /[\r\n]/.match?(type) + raise ArgumentError, "field content type cannot include CR/LF" + end buf << "Content-Disposition: form-data; " \ "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \ "Content-Type: #{type}\r\n\r\n" @@ -385,6 +390,9 @@ def encode_multipart_form_data(out, params, opt) def quote_string(str, charset) str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset + if /[\r\n]/.match?(str) + raise ArgumentError, "multipart field name or filename cannot include CR/LF" + end str.gsub(/[\\"]/, '\\\\\&') end diff --git a/lib/rubygems/vendor/net-http/lib/net/http/header.rb b/lib/rubygems/vendor/net-http/lib/net/http/header.rb index 5cb1da01ec07..67622f23d28b 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/header.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/header.rb @@ -179,7 +179,9 @@ # - #each_value: Passes each string field value to the block. # module Gem::Net::HTTPHeader + # The maximum length of HTTP header keys. MAX_KEY_LENGTH = 1024 + # The maximum length of HTTP header values. MAX_FIELD_LENGTH = 65536 def initialize_http_header(initheader) #:nodoc: @@ -191,11 +193,9 @@ def initialize_http_header(initheader) #:nodoc: warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE else value = value.strip # raise error for invalid byte sequences - if key.to_s.bytesize > MAX_KEY_LENGTH - raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..." - end + validate_field_name(key) if value.to_s.bytesize > MAX_FIELD_LENGTH - raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}" + raise ArgumentError, "header #{key} has too long field value: #{value.bytesize} bytes (limit #{MAX_FIELD_LENGTH})" end if value.count("\r\n") > 0 raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF" @@ -261,20 +261,34 @@ def []=(key, val) def add_field(key, val) stringified_downcased_key = key.downcase.to_s if @header.key?(stringified_downcased_key) - append_field_value(@header[stringified_downcased_key], val) + append_field_value(@header[stringified_downcased_key], val, key) else set_field(key, val) end end + # :stopdoc: + private def validate_field_name(key) + if key.to_s.bytesize > MAX_KEY_LENGTH + raise ArgumentError, "too long (#{key.to_s.bytesize} bytes) header: #{key.to_s[0, 30].inspect}..." + end + if /[\x00-\x1f\x7f:]/n.match?(key.to_s.b) + raise ArgumentError, "header field name cannot include control characters or colon: #{key.to_s[0, 30].inspect}" + end + end + private def set_field(key, val) + validate_field_name(key) case val when Enumerable ary = [] - append_field_value(ary, val) + append_field_value(ary, val, key) @header[key.downcase.to_s] = ary else val = val.to_s # for compatibility use to_s instead of to_str + if val.bytesize > MAX_FIELD_LENGTH + raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})" + end if val.b.count("\r\n") > 0 raise ArgumentError, 'header field value cannot include CR/LF' end @@ -282,18 +296,22 @@ def add_field(key, val) end end - private def append_field_value(ary, val) + private def append_field_value(ary, val, key) case val when Enumerable - val.each{|x| append_field_value(ary, x)} + val.each{|x| append_field_value(ary, x, key)} else val = val.to_s + if val.bytesize > MAX_FIELD_LENGTH + raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})" + end if /[\r\n]/n.match?(val.b) raise ArgumentError, 'header field value cannot include CR/LF' end ary.push val end end + # :startdoc: # Returns the array field value for the given +key+, # or +nil+ if there is no such field; @@ -490,7 +508,7 @@ def each_capitalized alias canonical_each each_capitalized - def capitalize(name) + def capitalize(name) # :nodoc: name.to_s.split('-'.freeze).map {|s| s.capitalize }.join('-'.freeze) end private :capitalize @@ -613,11 +631,35 @@ def set_range(r, e = nil) # res = Gem::Net::HTTP.get_response(hostname, '/todos/1') # res.content_length # => nil # + # The value must consist of digits only. + # Multiple 'Content-Length' values are accepted + # only when they are all identical; + # otherwise Gem::Net::HTTPHeaderSyntaxError is raised. + # See {RFC 9110 Section 8.6}[https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6]. def content_length - return nil unless key?('Content-Length') - len = self['Content-Length'].slice(/\d+/) or - raise Gem::Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' - len.to_i + values = @header['content-length'] + return nil if values.nil? + + lengths = [] + values.each do |value| + value = value.strip + if value.empty? + raise Gem::Net::HTTPHeaderSyntaxError, + "empty Content-Length value" + end + lengths.concat(value.split(/\s*,\s*/, -1)) + end + if lengths.uniq.size != 1 + raise Gem::Net::HTTPHeaderSyntaxError, + "Content-Length has multiple different values: " + + values.join(", ") + end + length = lengths.first + unless /\A\d+\z/.match?(length) + raise Gem::Net::HTTPHeaderSyntaxError, + "wrong Content-Length format: #{length}" + end + length.to_i end # Sets the value of field 'Content-Length' to the given numeric; @@ -643,7 +685,7 @@ def content_length=(len) end # Returns +true+ if field 'Transfer-Encoding' - # exists and has value 'chunked', + # exists and has 'chunked' as its final transfer coding, # +false+ otherwise; # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]: # @@ -651,10 +693,13 @@ def content_length=(len) # res['Transfer-Encoding'] # => "chunked" # res.chunked? # => true # + # 'gzip, chunked' is chunked but 'chunked, gzip' is not, + # because only the final transfer coding frames the message. + # See {RFC 9112 Section 6.3}[https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3]. def chunked? return false unless @header['transfer-encoding'] field = self['Transfer-Encoding'] - (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false + (/(?:\A|[^\-\w])chunked(?![\-\w])\s*(?:,\s*)*\z/i =~ field) ? true : false end # Returns a Range object representing the value of field @@ -770,7 +815,7 @@ def type_params # # Gem::Net::HTTPHeader#content_type= is an alias for Gem::Net::HTTPHeader#set_content_type. def set_content_type(type, params = {}) - @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] + set_field('Content-Type', type + params.map{|k,v|"; #{k}=#{v}"}.join('')) end alias content_type= set_content_type @@ -957,12 +1002,12 @@ def proxy_basic_auth(account, password) @header['proxy-authorization'] = [basic_encode(account, password)] end - def basic_encode(account, password) + def basic_encode(account, password) # :nodoc: 'Basic ' + ["#{account}:#{password}"].pack('m0') end private :basic_encode -# Returns whether the HTTP session is to be closed. + # Returns whether the HTTP session is to be closed. def connection_close? token = /(?:\A|,)\s*close\s*(?:\z|,)/i @header['connection']&.grep(token) {return true} @@ -970,7 +1015,7 @@ def connection_close? false end -# Returns whether the HTTP session is to be kept alive. + # Returns whether the HTTP session is to be kept alive. def connection_keep_alive? token = /(?:\A|,)\s*keep-alive\s*(?:\z|,)/i @header['connection']&.grep(token) {return true} diff --git a/lib/rubygems/vendor/net-http/lib/net/http/request.rb b/lib/rubygems/vendor/net-http/lib/net/http/request.rb index 495ec9be5424..567786c930d9 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/request.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/request.rb @@ -61,6 +61,7 @@ # - Gem::Net::HTTP::Options # - Gem::Net::HTTP::Trace # - Gem::Net::HTTP::Patch +# - Gem::Net::HTTP::Query # # Subclasses for WebDAV requests: # diff --git a/lib/rubygems/vendor/net-http/lib/net/http/requests.rb b/lib/rubygems/vendor/net-http/lib/net/http/requests.rb index 45727e7f61fc..ca56e6449d11 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/requests.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/requests.rb @@ -19,9 +19,9 @@ # # - Request body: optional. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes. # # Related: # @@ -29,6 +29,7 @@ # - Gem::Net::HTTP#get: sends +GET+ request, returns response object. # class Gem::Net::HTTP::Get < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'GET' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -51,15 +52,16 @@ class Gem::Net::HTTP::Get < Gem::Net::HTTPRequest # # - Request body: optional. # - Response body: no. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes. # # Related: # # - Gem::Net::HTTP#head: sends +HEAD+ request, returns response object. # class Gem::Net::HTTP::Head < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'HEAD' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = false @@ -85,9 +87,9 @@ class Gem::Net::HTTP::Head < Gem::Net::HTTPRequest # # - Request body: yes. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: no. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes. # # Related: # @@ -95,6 +97,7 @@ class Gem::Net::HTTP::Head < Gem::Net::HTTPRequest # - Gem::Net::HTTP#post: sends +POST+ request, returns response object. # class Gem::Net::HTTP::Post < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'POST' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -120,9 +123,9 @@ class Gem::Net::HTTP::Post < Gem::Net::HTTPRequest # # - Request body: yes. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no. # # Related: # @@ -130,6 +133,7 @@ class Gem::Net::HTTP::Post < Gem::Net::HTTPRequest # - Gem::Net::HTTP#put: sends +PUT+ request, returns response object. # class Gem::Net::HTTP::Put < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'PUT' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -153,15 +157,16 @@ class Gem::Net::HTTP::Put < Gem::Net::HTTPRequest # # - Request body: optional. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no. # # Related: # # - Gem::Net::HTTP#delete: sends +DELETE+ request, returns response object. # class Gem::Net::HTTP::Delete < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'DELETE' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -184,15 +189,16 @@ class Gem::Net::HTTP::Delete < Gem::Net::HTTPRequest # # - Request body: optional. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no. # # Related: # # - Gem::Net::HTTP#options: sends +OPTIONS+ request, returns response object. # class Gem::Net::HTTP::Options < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'OPTIONS' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -215,15 +221,16 @@ class Gem::Net::HTTP::Options < Gem::Net::HTTPRequest # # - Request body: no. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no. # # Related: # # - Gem::Net::HTTP#trace: sends +TRACE+ request, returns response object. # class Gem::Net::HTTP::Trace < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'TRACE' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -249,20 +256,56 @@ class Gem::Net::HTTP::Trace < Gem::Net::HTTPRequest # # - Request body: yes. # - Response body: yes. -# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no. -# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no. -# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: no. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no. # # Related: # # - Gem::Net::HTTP#patch: sends +PATCH+ request, returns response object. # class Gem::Net::HTTP::Patch < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'PATCH' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true end +# \Class for representing +# {HTTP method QUERY}[https://www.rfc-editor.org/rfc/rfc10008.html]: +# +# require 'rubygems/vendor/net-http/lib/net/http' +# uri = Gem::URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# uri.path = '/posts' +# req = Gem::Net::HTTP::Query.new(uri) # => # +# req.body = '{"title": "foo","body": "bar","userId": 1}' +# req.content_type = 'application/json' +# res = Gem::Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers]. +# +# Properties: +# +# - Request body: yes. +# - Response body: yes. +# - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes. +# - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes. +# - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes. +# +# Related: +# +# - Gem::Net::HTTP#query: sends +QUERY+ request, returns response object. +# +class Gem::Net::HTTP::Query < Gem::Net::HTTPRequest + # :stopdoc: + METHOD = 'QUERY' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + # # WebDAV methods --- RFC2518 # @@ -285,6 +328,7 @@ class Gem::Net::HTTP::Patch < Gem::Net::HTTPRequest # - Gem::Net::HTTP#propfind: sends +PROPFIND+ request, returns response object. # class Gem::Net::HTTP::Propfind < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'PROPFIND' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -308,6 +352,7 @@ class Gem::Net::HTTP::Propfind < Gem::Net::HTTPRequest # - Gem::Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object. # class Gem::Net::HTTP::Proppatch < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'PROPPATCH' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -331,6 +376,7 @@ class Gem::Net::HTTP::Proppatch < Gem::Net::HTTPRequest # - Gem::Net::HTTP#mkcol: sends +MKCOL+ request, returns response object. # class Gem::Net::HTTP::Mkcol < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'MKCOL' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -354,6 +400,7 @@ class Gem::Net::HTTP::Mkcol < Gem::Net::HTTPRequest # - Gem::Net::HTTP#copy: sends +COPY+ request, returns response object. # class Gem::Net::HTTP::Copy < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'COPY' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -377,6 +424,7 @@ class Gem::Net::HTTP::Copy < Gem::Net::HTTPRequest # - Gem::Net::HTTP#move: sends +MOVE+ request, returns response object. # class Gem::Net::HTTP::Move < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'MOVE' REQUEST_HAS_BODY = false RESPONSE_HAS_BODY = true @@ -400,6 +448,7 @@ class Gem::Net::HTTP::Move < Gem::Net::HTTPRequest # - Gem::Net::HTTP#lock: sends +LOCK+ request, returns response object. # class Gem::Net::HTTP::Lock < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'LOCK' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true @@ -423,8 +472,8 @@ class Gem::Net::HTTP::Lock < Gem::Net::HTTPRequest # - Gem::Net::HTTP#unlock: sends +UNLOCK+ request, returns response object. # class Gem::Net::HTTP::Unlock < Gem::Net::HTTPRequest + # :stopdoc: METHOD = 'UNLOCK' REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true end - diff --git a/lib/rubygems/vendor/net-http/lib/net/http/response.rb b/lib/rubygems/vendor/net-http/lib/net/http/response.rb index cbbd191d879b..61d8dc557724 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/response.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/response.rb @@ -133,6 +133,13 @@ # there is a protocol error. # class Gem::Net::HTTPResponse + # The maximum total size in bytes of the response header. + MAX_RESPONSE_HEADER_LENGTH = 1024 * 1024 # 1 MiB + + # The maximum number of informational (1xx) responses accepted before the + # final response. + MAX_INFORMATIONAL_RESPONSES = 100 + class << self # true if the response has a body. def body_permitted? @@ -152,10 +159,17 @@ def read_new(sock) #:nodoc: internal use only res end + def read_line(sock, limit, ignore_eof = false) #:nodoc: internal use only + sock.readuntil("\n", ignore_eof, limit: limit) + rescue Gem::Net::ReadLimitExceeded + raise Gem::Net::HTTPBadResponse, 'response line too long' + end + private + # :stopdoc: def read_status_line(sock) - str = sock.readline + str = read_line(sock, MAX_RESPONSE_HEADER_LENGTH).chop m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or raise Gem::Net::HTTPBadResponse, "wrong status line: #{str.dump}" m.captures @@ -169,12 +183,19 @@ def response_class(code) def each_response_header(sock) key = value = nil + remaining = MAX_RESPONSE_HEADER_LENGTH while true - line = sock.readuntil("\n", true).sub(/\s+\z/, '') + line = read_line(sock, MAX_RESPONSE_HEADER_LENGTH, true) + remaining -= line.bytesize + raise Gem::Net::HTTPBadResponse, 'response header too large' if remaining < 0 + line = line.chomp break if line.empty? if line[0] == ?\s or line[0] == ?\t and value - value << ' ' unless value.empty? - value << line.strip + folded = line.strip + unless folded.empty? + value << ' ' unless value.empty? + value << folded + end else yield key, value if key key, value = line.strip.split(/\s*:\s*/, 2) @@ -259,7 +280,7 @@ def body_encoding=(value) # header. attr_accessor :ignore_eof - def inspect + def inspect # :nodoc: "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" end @@ -598,15 +619,20 @@ def read_body_0(dest) @socket = inflate_body_io - clen = content_length() - if clen - @socket.read clen, dest, @ignore_eof - return - end - clen = range_length() - if clen - @socket.read clen, dest - return + # Transfer-Encoding overrides Content-Length, so a body that is not + # chunk-framed runs until the server closes the connection. + # See RFC 9112 Section 6.3. + unless @header['transfer-encoding'] + clen = content_length() + if clen + @socket.read clen, dest, @ignore_eof + return + end + clen = range_length() + if clen + @socket.read clen, dest + return + end end @socket.read_all dest end @@ -622,7 +648,7 @@ def read_body_0(dest) def read_chunked(dest, chunk_data_io) # :nodoc: total = 0 while true - line = @socket.readline + line = self.class.read_line(@socket, MAX_RESPONSE_HEADER_LENGTH).chop hexlen = line.slice(/[0-9a-fA-F]+/) or raise Gem::Net::HTTPBadResponse, "wrong chunk size line: #{line}" len = hexlen.hex @@ -634,7 +660,7 @@ def read_chunked(dest, chunk_data_io) # :nodoc: @socket.read 2 # \r\n end end - until @socket.readline.empty? + until self.class.read_line(@socket, MAX_RESPONSE_HEADER_LENGTH).chop.empty? # none end end diff --git a/lib/rubygems/vendor/net-http/lib/net/http/responses.rb b/lib/rubygems/vendor/net-http/lib/net/http/responses.rb index 32738adc1442..62ce1cba1b29 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/responses.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/responses.rb @@ -4,7 +4,9 @@ module Gem::Net + # Unknown HTTP response class HTTPUnknownResponse < HTTPResponse + # :stopdoc: HAS_BODY = true EXCEPTION_TYPE = HTTPError # end @@ -19,6 +21,7 @@ class HTTPUnknownResponse < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_informational_response]. # class HTTPInformation < HTTPResponse + # :stopdoc: HAS_BODY = false EXCEPTION_TYPE = HTTPError # end @@ -34,6 +37,7 @@ class HTTPInformation < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success]. # class HTTPSuccess < HTTPResponse + # :stopdoc: HAS_BODY = true EXCEPTION_TYPE = HTTPError # end @@ -49,6 +53,7 @@ class HTTPSuccess < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_redirection]. # class HTTPRedirection < HTTPResponse + # :stopdoc: HAS_BODY = true EXCEPTION_TYPE = HTTPRetriableError # end @@ -63,6 +68,7 @@ class HTTPRedirection < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors]. # class HTTPClientError < HTTPResponse + # :stopdoc: HAS_BODY = true EXCEPTION_TYPE = HTTPClientException # end @@ -77,6 +83,7 @@ class HTTPClientError < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_server_errors]. # class HTTPServerError < HTTPResponse + # :stopdoc: HAS_BODY = true EXCEPTION_TYPE = HTTPFatalError # end @@ -94,6 +101,7 @@ class HTTPServerError < HTTPResponse # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#100]. # class HTTPContinue < HTTPInformation + # :stopdoc: HAS_BODY = false end @@ -111,6 +119,7 @@ class HTTPContinue < HTTPInformation # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#101]. # class HTTPSwitchProtocol < HTTPInformation + # :stopdoc: HAS_BODY = false end @@ -127,6 +136,7 @@ class HTTPSwitchProtocol < HTTPInformation # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#102]. # class HTTPProcessing < HTTPInformation + # :stopdoc: HAS_BODY = false end @@ -145,6 +155,7 @@ class HTTPProcessing < HTTPInformation # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#103]. # class HTTPEarlyHints < HTTPInformation + # :stopdoc: HAS_BODY = false end @@ -162,6 +173,7 @@ class HTTPEarlyHints < HTTPInformation # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#200]. # class HTTPOK < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -179,6 +191,7 @@ class HTTPOK < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#201]. # class HTTPCreated < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -196,6 +209,7 @@ class HTTPCreated < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#202]. # class HTTPAccepted < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -215,6 +229,7 @@ class HTTPAccepted < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#203]. # class HTTPNonAuthoritativeInformation < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -232,6 +247,7 @@ class HTTPNonAuthoritativeInformation < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#204]. # class HTTPNoContent < HTTPSuccess + # :stopdoc: HAS_BODY = false end @@ -250,6 +266,7 @@ class HTTPNoContent < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#205]. # class HTTPResetContent < HTTPSuccess + # :stopdoc: HAS_BODY = false end @@ -268,6 +285,7 @@ class HTTPResetContent < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#206]. # class HTTPPartialContent < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -285,6 +303,7 @@ class HTTPPartialContent < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#207]. # class HTTPMultiStatus < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -304,6 +323,7 @@ class HTTPMultiStatus < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#208]. # class HTTPAlreadyReported < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -321,6 +341,7 @@ class HTTPAlreadyReported < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#226]. # class HTTPIMUsed < HTTPSuccess + # :stopdoc: HAS_BODY = true end @@ -338,6 +359,7 @@ class HTTPIMUsed < HTTPSuccess # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#300]. # class HTTPMultipleChoices < HTTPRedirection + # :stopdoc: HAS_BODY = true end HTTPMultipleChoice = HTTPMultipleChoices @@ -356,6 +378,7 @@ class HTTPMultipleChoices < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#301]. # class HTTPMovedPermanently < HTTPRedirection + # :stopdoc: HAS_BODY = true end @@ -373,6 +396,7 @@ class HTTPMovedPermanently < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#302]. # class HTTPFound < HTTPRedirection + # :stopdoc: HAS_BODY = true end HTTPMovedTemporarily = HTTPFound @@ -390,6 +414,7 @@ class HTTPFound < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#303]. # class HTTPSeeOther < HTTPRedirection + # :stopdoc: HAS_BODY = true end @@ -407,6 +432,7 @@ class HTTPSeeOther < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#304]. # class HTTPNotModified < HTTPRedirection + # :stopdoc: HAS_BODY = false end @@ -423,6 +449,7 @@ class HTTPNotModified < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#305]. # class HTTPUseProxy < HTTPRedirection + # :stopdoc: HAS_BODY = false end @@ -440,6 +467,7 @@ class HTTPUseProxy < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#307]. # class HTTPTemporaryRedirect < HTTPRedirection + # :stopdoc: HAS_BODY = true end @@ -456,6 +484,7 @@ class HTTPTemporaryRedirect < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#308]. # class HTTPPermanentRedirect < HTTPRedirection + # :stopdoc: HAS_BODY = true end @@ -472,6 +501,7 @@ class HTTPPermanentRedirect < HTTPRedirection # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#400]. # class HTTPBadRequest < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -488,6 +518,7 @@ class HTTPBadRequest < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#401]. # class HTTPUnauthorized < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -504,6 +535,7 @@ class HTTPUnauthorized < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#402]. # class HTTPPaymentRequired < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -521,6 +553,7 @@ class HTTPPaymentRequired < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#403]. # class HTTPForbidden < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -537,6 +570,7 @@ class HTTPForbidden < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#404]. # class HTTPNotFound < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -553,6 +587,7 @@ class HTTPNotFound < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#405]. # class HTTPMethodNotAllowed < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -570,6 +605,7 @@ class HTTPMethodNotAllowed < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#406]. # class HTTPNotAcceptable < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -586,6 +622,7 @@ class HTTPNotAcceptable < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#407]. # class HTTPProxyAuthenticationRequired < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -602,6 +639,7 @@ class HTTPProxyAuthenticationRequired < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#408]. # class HTTPRequestTimeout < HTTPClientError + # :stopdoc: HAS_BODY = true end HTTPRequestTimeOut = HTTPRequestTimeout @@ -619,6 +657,7 @@ class HTTPRequestTimeout < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#409]. # class HTTPConflict < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -636,6 +675,7 @@ class HTTPConflict < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#410]. # class HTTPGone < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -653,6 +693,7 @@ class HTTPGone < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#411]. # class HTTPLengthRequired < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -670,6 +711,7 @@ class HTTPLengthRequired < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#412]. # class HTTPPreconditionFailed < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -686,6 +728,7 @@ class HTTPPreconditionFailed < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#413]. # class HTTPPayloadTooLarge < HTTPClientError + # :stopdoc: HAS_BODY = true end HTTPRequestEntityTooLarge = HTTPPayloadTooLarge @@ -703,6 +746,7 @@ class HTTPPayloadTooLarge < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#414]. # class HTTPURITooLong < HTTPClientError + # :stopdoc: HAS_BODY = true end HTTPRequestURITooLong = HTTPURITooLong @@ -721,6 +765,7 @@ class HTTPURITooLong < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#415]. # class HTTPUnsupportedMediaType < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -737,6 +782,7 @@ class HTTPUnsupportedMediaType < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#416]. # class HTTPRangeNotSatisfiable < HTTPClientError + # :stopdoc: HAS_BODY = true end HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable @@ -754,6 +800,7 @@ class HTTPRangeNotSatisfiable < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#417]. # class HTTPExpectationFailed < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -774,6 +821,7 @@ class HTTPExpectationFailed < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#421]. # class HTTPMisdirectedRequest < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -790,6 +838,7 @@ class HTTPMisdirectedRequest < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#422]. # class HTTPUnprocessableEntity < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -805,6 +854,7 @@ class HTTPUnprocessableEntity < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#423]. # class HTTPLocked < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -821,6 +871,7 @@ class HTTPLocked < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#424]. # class HTTPFailedDependency < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -840,6 +891,7 @@ class HTTPFailedDependency < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#426]. # class HTTPUpgradeRequired < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -856,6 +908,7 @@ class HTTPUpgradeRequired < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#428]. # class HTTPPreconditionRequired < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -872,6 +925,7 @@ class HTTPPreconditionRequired < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429]. # class HTTPTooManyRequests < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -889,6 +943,7 @@ class HTTPTooManyRequests < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#431]. # class HTTPRequestHeaderFieldsTooLarge < HTTPClientError + # :stopdoc: HAS_BODY = true end @@ -906,6 +961,7 @@ class HTTPRequestHeaderFieldsTooLarge < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#451]. # class HTTPUnavailableForLegalReasons < HTTPClientError + # :stopdoc: HAS_BODY = true end # 444 No Response - Nginx @@ -926,6 +982,7 @@ class HTTPUnavailableForLegalReasons < HTTPClientError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#500]. # class HTTPInternalServerError < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -943,6 +1000,7 @@ class HTTPInternalServerError < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#501]. # class HTTPNotImplemented < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -960,6 +1018,7 @@ class HTTPNotImplemented < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#502]. # class HTTPBadGateway < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -977,6 +1036,7 @@ class HTTPBadGateway < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#503]. # class HTTPServiceUnavailable < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -994,6 +1054,7 @@ class HTTPServiceUnavailable < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#504]. # class HTTPGatewayTimeout < HTTPServerError + # :stopdoc: HAS_BODY = true end HTTPGatewayTimeOut = HTTPGatewayTimeout @@ -1011,6 +1072,7 @@ class HTTPGatewayTimeout < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#505]. # class HTTPVersionNotSupported < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -1027,6 +1089,7 @@ class HTTPVersionNotSupported < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#506]. # class HTTPVariantAlsoNegotiates < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -1043,6 +1106,7 @@ class HTTPVariantAlsoNegotiates < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#507]. # class HTTPInsufficientStorage < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -1059,6 +1123,7 @@ class HTTPInsufficientStorage < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#508]. # class HTTPLoopDetected < HTTPServerError + # :stopdoc: HAS_BODY = true end # 509 Bandwidth Limit Exceeded - Apache bw/limited extension @@ -1076,6 +1141,7 @@ class HTTPLoopDetected < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#510]. # class HTTPNotExtended < HTTPServerError + # :stopdoc: HAS_BODY = true end @@ -1092,12 +1158,14 @@ class HTTPNotExtended < HTTPServerError # - {Wikipedia}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#511]. # class HTTPNetworkAuthenticationRequired < HTTPServerError + # :stopdoc: HAS_BODY = true end end class Gem::Net::HTTPResponse + # :stopdoc: CODE_CLASS_TO_OBJ = { '1' => Gem::Net::HTTPInformation, '2' => Gem::Net::HTTPSuccess, diff --git a/lib/rubygems/vendor/net-protocol/COPYING b/lib/rubygems/vendor/net-protocol/COPYING new file mode 100644 index 000000000000..48e5a96de7c8 --- /dev/null +++ b/lib/rubygems/vendor/net-protocol/COPYING @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto . +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + +1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + +2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a. place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b. use the modified software only within your corporation or + organization. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a. distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b. accompany the distribution with the machine-readable source of + the software. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + +5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + +6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/lib/rubygems/vendor/net-protocol/LICENSE.txt b/lib/rubygems/vendor/net-protocol/LICENSE.txt deleted file mode 100644 index a009caefea81..000000000000 --- a/lib/rubygems/vendor/net-protocol/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. diff --git a/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb b/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb index 53d34d8d98eb..3c803fefe5d7 100644 --- a/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb +++ b/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb @@ -21,12 +21,12 @@ require 'socket' require_relative '../../../timeout/lib/timeout' -require 'io/wait' +require 'io/wait' if RUBY_VERSION < '3.2' module Gem::Net # :nodoc: class Protocol #:nodoc: internal use only - VERSION = "0.2.2" + VERSION = "0.4.0" private def Protocol.protocol_param(name, val) @@ -54,9 +54,20 @@ def ssl_socket_connect(s, timeout) s.connect end end + + tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]] + tcp_socket_parameters.include?([:key, :open_timeout]) + else + # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize + # See discussion in https://github.com/ruby/net-http/pull/224 + Socket.method(:tcp).parameters.include?([:key, :open_timeout]) + end + private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT end + # :stopdoc: class ProtocolError < StandardError; end class ProtoSyntaxError < ProtocolError; end class ProtoFatalError < ProtocolError; end @@ -66,6 +77,29 @@ class ProtoAuthError < ProtocolError; end class ProtoCommandError < ProtocolError; end class ProtoRetriableError < ProtocolError; end ProtocRetryError = ProtoRetriableError + # :startdoc: + + ## + # ReadLimitExceeded, a subclass of ProtocolError, is raised if the + # terminator is not found within the byte limit given to + # Gem::Net::BufferedIO#readuntil. + # + # The limit is the largest result readuntil may return, counting the + # terminator itself, so a limit of 4 accepts "abc\n" and rejects + # "abcd\n". Unlike the limit of IO#gets it never truncates a result to + # fit. Either the whole thing comes back or this is raised, except + # under ignore_eof, which still returns what was buffered when the + # stream ended. The count is in bytes while the IO hands back binary + # strings, which every real one does. + # + # It bounds one call, not a connection. The unconsumed buffer can + # still run one BUFSIZE past the limit, and a peer sending endless + # short lines is not bounded at all. + # + # Nothing is consumed when this is raised, so the usual response is to + # close the connection rather than read on under a wider limit. + + class ReadLimitExceeded < ProtocolError; end ## # OpenTimeout, a subclass of Gem::Timeout::Error, is raised if a connection cannot @@ -78,6 +112,7 @@ class OpenTimeout < Gem::Timeout::Error; end # response cannot be read within the read_timeout. class ReadTimeout < Gem::Timeout::Error + # :stopdoc: def initialize(io = nil) @io = io end @@ -97,6 +132,7 @@ def message # response cannot be written within the write_timeout. Not raised on Windows. class WriteTimeout < Gem::Timeout::Error + # :stopdoc: def initialize(io = nil) @io = io end @@ -153,6 +189,7 @@ def close public def read(len, dest = ''.b, ignore_eof = false) + raise ArgumentError, "negative length #{len} given" if len < 0 LOG "reading #{len} bytes..." read_bytes = 0 begin @@ -191,14 +228,31 @@ def read_all(dest = ''.b) dest end - def readuntil(terminator, ignore_eof = false) + def readuntil(terminator, ignore_eof = false, limit: nil) + unless limit.nil? || (Integer === limit && limit > 0) + # Integer === calls nothing on limit, and only an Integer is + # echoed back, so validation never runs the caller's code. + got = Integer === limit ? limit : "a non-Integer" + raise ArgumentError, "limit must be a positive Integer, got #{got}" + end offset = @rbuf_offset begin until idx = @rbuf.index(terminator, offset) - offset = @rbuf.bytesize + if limit && rbuf_size > limit + raise ReadLimitExceeded, "exceeded the #{limit} byte read limit" + end + # Rewind so a terminator split across reads is still found. The + # floor guards two things. String#index reads a negative offset + # as counting from the end, and an offset below @rbuf_offset + # matches inside bytes already returned. + offset = [@rbuf.bytesize - terminator.bytesize + 1, @rbuf_offset].max rbuf_fill end - return rbuf_consume(idx + terminator.bytesize - @rbuf_offset) + len = idx + terminator.bytesize - @rbuf_offset + if limit && len > limit + raise ReadLimitExceeded, "exceeded the #{limit} byte read limit" + end + return rbuf_consume(len) rescue EOFError raise unless ignore_eof return rbuf_consume @@ -303,9 +357,9 @@ def writing @debug_output << '<- ' if @debug_output yield @debug_output << "\n" if @debug_output - bytes = @written_bytes + @written_bytes + ensure @written_bytes = nil - bytes end def write0(*strs) @@ -331,6 +385,9 @@ def write0(*strs) # next string end # continue looping + when :wait_readable + (io = @io.to_io).wait_readable(@write_timeout) or raise Gem::Net::WriteTimeout.new(io) + # continue looping when :wait_writable (io = @io.to_io).wait_writable(@write_timeout) or raise Gem::Net::WriteTimeout.new(io) # continue looping @@ -373,12 +430,15 @@ def initialize(*, **) def each_message_chunk LOG 'reading message...' LOG_off() - read_bytes = 0 - while (line = readuntil("\r\n")) != ".\r\n" - read_bytes += line.size - yield line.delete_prefix('.') + begin + read_bytes = 0 + while (line = readuntil("\r\n")) != ".\r\n" + read_bytes += line.size + yield line.delete_prefix('.') + end + ensure + LOG_on() end - LOG_on() LOG "read message (#{read_bytes} bytes)" end @@ -404,12 +464,15 @@ def write_message_0(src) def write_message(src) LOG "writing message from #{src.class}" LOG_off() - len = writing { - using_each_crlf_line { - write_message_0 src + begin + len = writing { + using_each_crlf_line { + write_message_0 src + } } - } - LOG_on() + ensure + LOG_on() + end LOG "wrote #{len} bytes" len end @@ -417,16 +480,19 @@ def write_message(src) def write_message_by_block(&block) LOG 'writing message from block' LOG_off() - len = writing { - using_each_crlf_line { - begin - block.call(WriteAdapter.new(self.method(:write_message_0))) - rescue LocalJumpError - # allow `break' from writer block - end + begin + len = writing { + using_each_crlf_line { + begin + block.call(WriteAdapter.new(self.method(:write_message_0))) + rescue LocalJumpError + # allow `break' from writer block + end + } } - } - LOG_on() + ensure + LOG_on() + end LOG "wrote #{len} bytes" len end @@ -446,6 +512,8 @@ def using_each_crlf_line write0 "\r\n" end write0 ".\r\n" + nil + ensure @wbuf = nil end @@ -484,6 +552,7 @@ def buffer_filling(buf, src) # The writer adapter class # class WriteAdapter + # :stopdoc: def initialize(writer) @writer = writer end diff --git a/lib/rubygems/vendor/optparse/lib/optparse.rb b/lib/rubygems/vendor/optparse/lib/optparse.rb index d39d9dd4e02f..66d5fe390e8f 100644 --- a/lib/rubygems/vendor/optparse/lib/optparse.rb +++ b/lib/rubygems/vendor/optparse/lib/optparse.rb @@ -426,8 +426,9 @@ # class Gem::OptionParser # The version string - VERSION = "0.8.0" - Version = VERSION # for compatibility + VERSION = "0.8.1" + # An alias for compatibility + Version = VERSION # :stopdoc: NoArgument = [NO_ARGUMENT = :NONE, nil].freeze @@ -471,7 +472,6 @@ def candidate(key, icase = false, pat = nil, &_) Completion.candidate(key, icase, pat, &method(:each)) end - public def complete(key, icase = false, pat = nil) candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size} if candidates.size == 1 @@ -561,7 +561,7 @@ def initialize(pattern = nil, conv = nil, # Parses +arg+ and returns rest of +arg+ and matched portion to the # argument pattern. Yields when the pattern doesn't match substring. # - def parse_arg(arg) # :nodoc: + private def parse_arg(arg) # :nodoc: pattern or return nil, [arg] unless m = pattern.match(arg) yield(InvalidArgument, arg) @@ -579,14 +579,13 @@ def parse_arg(arg) # :nodoc: yield(InvalidArgument, arg) # didn't match whole arg return arg[s.length..-1], m end - private :parse_arg # # Parses argument, converts and returns +arg+, +block+ and result of # conversion. Yields at semi-error condition instead of raising an # exception. # - def conv_arg(arg, val = []) # :nodoc: + private def conv_arg(arg, val = []) # :nodoc: v, = *val if conv val = conv.call(*val) @@ -598,7 +597,6 @@ def conv_arg(arg, val = []) # :nodoc: end return arg, block, val end - private :conv_arg # # Produces the summary text. Each line of the summary is yielded to the @@ -882,14 +880,13 @@ def reject(t) # +lopts+:: Long style option list. # +nlopts+:: Negated long style options list. # - def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc: + private def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc: sopts.each {|o| @short[o] = sw} if sopts lopts.each {|o| @long[o] = sw} if lopts nlopts.each {|o| @long[o] = nsw} if nsw and nlopts used = @short.invert.update(@long.invert) @list.delete_if {|o| Switch === o and !used[o]} end - private :update # # Inserts +switch+ at the head of the list, and associates short, long @@ -1458,14 +1455,13 @@ def to_a; summarize("#{banner}".split(/^/)) end # +prv+:: Previously specified argument. # +msg+:: Exception message. # - def notwice(obj, prv, msg) # :nodoc: + private def notwice(obj, prv, msg) # :nodoc: unless !prv or prv == obj raise(ArgumentError, "argument #{msg} given twice: #{obj}", ParseError.filter_backtrace(caller(2))) end obj end - private :notwice SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc: @@ -1732,7 +1728,7 @@ def order!(argv = default_argv, into: nil, **keywords, &nonopt) parse_in_order(argv, setter, **keywords, &nonopt) end - def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc: + private def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc: opt, arg, val, rest = nil nonopt ||= proc {|a| throw :terminate, a} argv.unshift(arg) if arg = catch(:terminate) { @@ -1823,10 +1819,9 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, argv end - private :parse_in_order # Calls callback with _val_. - def callback!(cb, max_arity, *args) # :nodoc: + private def callback!(cb, max_arity, *args) # :nodoc: args.compact! if (size = args.size) < max_arity and cb.to_proc.lambda? @@ -1836,7 +1831,6 @@ def callback!(cb, max_arity, *args) # :nodoc: end cb.call(*args) end - private :callback! # # Parses command line arguments +argv+ in permutation mode and returns @@ -1950,24 +1944,22 @@ def self.getopts(*args, symbolize_names: false) # Traverses @stack, sending each element method +id+ with +args+ and # +block+. # - def visit(id, *args, &block) # :nodoc: + private def visit(id, *args, &block) # :nodoc: @stack.reverse_each do |el| el.__send__(id, *args, &block) end nil end - private :visit # # Searches +key+ in @stack for +id+ hash and returns or yields the result. # - def search(id, key) # :nodoc: + private def search(id, key) # :nodoc: block_given = block_given? visit(:search, id, key) do |k| return block_given ? yield(k) : k end end - private :search # # Completes shortened long style option switch and returns pair of @@ -1978,7 +1970,7 @@ def search(id, key) # :nodoc: # +icase+:: Search case insensitive if true. # +pat+:: Optional pattern for completion. # - def complete(typ, opt, icase = false, *pat) # :nodoc: + private def complete(typ, opt, icase = false, *pat) # :nodoc: if pat.empty? search(typ, opt) {|sw| return [sw, opt]} # exact match or... end @@ -1988,7 +1980,6 @@ def complete(typ, opt, icase = false, *pat) # :nodoc: exc = ambiguous ? AmbiguousOption : InvalidOption raise exc.new(opt, additional: proc {|o| additional_message(typ, o)}) end - private :complete # # Returns additional info. @@ -2323,42 +2314,42 @@ def message # Raises when ambiguously completable string is encountered. # class AmbiguousOption < ParseError - const_set(:Reason, 'ambiguous option') + Reason = 'ambiguous option' # :nodoc: end # # Raises when there is an argument for a switch which takes no argument. # class NeedlessArgument < ParseError - const_set(:Reason, 'needless argument') + Reason = 'needless argument' # :nodoc: end # # Raises when a switch with mandatory argument has no argument. # class MissingArgument < ParseError - const_set(:Reason, 'missing argument') + Reason = 'missing argument' # :nodoc: end # # Raises when switch is undefined. # class InvalidOption < ParseError - const_set(:Reason, 'invalid option') + Reason = 'invalid option' # :nodoc: end # # Raises when the given argument does not match required format. # class InvalidArgument < ParseError - const_set(:Reason, 'invalid argument') + Reason = 'invalid argument' # :nodoc: end # # Raises when the given argument word can't be completed uniquely. # class AmbiguousArgument < InvalidArgument - const_set(:Reason, 'ambiguous argument') + Reason = 'ambiguous argument' # :nodoc: end # @@ -2457,9 +2448,11 @@ def initialize(*args) # :nodoc: # and DecimalNumeric. See Acceptable argument classes (in source code). # module Acceptables - const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger) - const_set(:OctalInteger, Gem::OptionParser::OctalInteger) - const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric) + # :stopdoc: + DecimalInteger = Gem::OptionParser::DecimalInteger + OctalInteger = Gem::OptionParser::OctalInteger + DecimalNumeric = Gem::OptionParser::DecimalNumeric + # :startdoc: end end diff --git a/lib/rubygems/vendor/timeout/lib/timeout.rb b/lib/rubygems/vendor/timeout/lib/timeout.rb index 376b8c0e2b9e..5235bc98460b 100644 --- a/lib/rubygems/vendor/timeout/lib/timeout.rb +++ b/lib/rubygems/vendor/timeout/lib/timeout.rb @@ -20,9 +20,9 @@ module Gem::Timeout # The version - VERSION = "0.4.4" + VERSION = "0.6.1" - # Internal error raised to when a timeout is triggered. + # Internal exception raised to when a timeout is triggered. class ExitException < Exception def exception(*) # :nodoc: self @@ -44,12 +44,101 @@ def self.handle_timeout(message) # :nodoc: end # :stopdoc: - CONDVAR = ConditionVariable.new - QUEUE = Queue.new - QUEUE_MUTEX = Mutex.new - TIMEOUT_THREAD_MUTEX = Mutex.new - @timeout_thread = nil - private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX + + # We keep a private reference so that time mocking libraries won't break Gem::Timeout. + GET_TIME = Process.method(:clock_gettime) + if defined?(Ractor.make_shareable) + # Ractor.make_shareable(Method) only works on Ruby 4+ + Ractor.make_shareable(GET_TIME) rescue nil + end + private_constant :GET_TIME + + class State + def initialize + @condvar = ConditionVariable.new + @queue = Queue.new + @queue_mutex = Mutex.new + + @timeout_thread = nil + @timeout_thread_mutex = Mutex.new + end + + if defined?(Ractor.store_if_absent) && defined?(Ractor.shareable?) && Ractor.shareable?(GET_TIME) + # Ractor support if + # 1. Ractor.store_if_absent is available + # 2. Method object can be shareable (4.0~) + def self.instance + Ractor.store_if_absent :timeout_gem_state do + State.new + end + end + else + GLOBAL_STATE = State.new + + def self.instance + GLOBAL_STATE + end + end + + def create_timeout_thread + # Threads unexpectedly inherit the interrupt mask: https://github.com/ruby/timeout/issues/41 + # So reset the interrupt mask to the default one for the timeout thread + Thread.handle_interrupt(Object => :immediate) do + watcher = Thread.new do + requests = [] + while true + until @queue.empty? and !requests.empty? # wait to have at least one request + req = @queue.pop + requests << req unless req.done? + end + closest_deadline = requests.min_by(&:deadline).deadline + + now = 0.0 + @queue_mutex.synchronize do + while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and @queue.empty? + @condvar.wait(@queue_mutex, closest_deadline - now) + end + end + + requests.each do |req| + req.interrupt if req.expired?(now) + end + requests.reject!(&:done?) + end + end + + if !watcher.group.enclosed? && (!defined?(Ractor.main?) || Ractor.main?) + ThreadGroup::Default.add(watcher) + end + + watcher.name = "Gem::Timeout stdlib thread" + watcher.thread_variable_set(:"\0__detached_thread__", true) + watcher + end + end + + def ensure_timeout_thread_created + unless @timeout_thread&.alive? + # If the Mutex is already owned we are in a signal handler. + # In that case, just return and let the main thread create the Gem::Timeout thread. + return if @timeout_thread_mutex.owned? + + Sync.synchronize @timeout_thread_mutex do + unless @timeout_thread&.alive? + @timeout_thread = create_timeout_thread + end + end + end + end + + def add_request(request) + Sync.synchronize @queue_mutex do + @queue << request + @condvar.signal + end + end + end + private_constant :State class Request attr_reader :deadline @@ -64,6 +153,7 @@ def initialize(thread, timeout, exception_class, message) @done = false # protected by @mutex end + # Only called by the timeout thread, so does not need Sync.synchronize def done? @mutex.synchronize do @done @@ -74,6 +164,7 @@ def expired?(now) now >= @deadline end + # Only called by the timeout thread, so does not need Sync.synchronize def interrupt @mutex.synchronize do unless @done @@ -84,64 +175,36 @@ def interrupt end def finished - @mutex.synchronize do + Sync.synchronize @mutex do @done = true end end end private_constant :Request - def self.create_timeout_thread - watcher = Thread.new do - requests = [] - while true - until QUEUE.empty? and !requests.empty? # wait to have at least one request - req = QUEUE.pop - requests << req unless req.done? - end - closest_deadline = requests.min_by(&:deadline).deadline - - now = 0.0 - QUEUE_MUTEX.synchronize do - while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? - CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) - end - end - - requests.each do |req| - req.interrupt if req.expired?(now) - end - requests.reject!(&:done?) - end - end - ThreadGroup::Default.add(watcher) unless watcher.group.enclosed? - watcher.name = "Gem::Timeout stdlib thread" - watcher.thread_variable_set(:"\0__detached_thread__", true) - watcher - end - private_class_method :create_timeout_thread - - def self.ensure_timeout_thread_created - unless @timeout_thread and @timeout_thread.alive? - # If the Mutex is already owned we are in a signal handler. - # In that case, just return and let the main thread create the @timeout_thread. - return if TIMEOUT_THREAD_MUTEX.owned? - TIMEOUT_THREAD_MUTEX.synchronize do - unless @timeout_thread and @timeout_thread.alive? - @timeout_thread = create_timeout_thread - end + module Sync + # Calls mutex.synchronize(&block) but if that fails on CRuby due to being in a trap handler, + # run mutex.synchronize(&block) in a separate Thread instead. + def self.synchronize(mutex, &block) + begin + mutex.synchronize(&block) + rescue ThreadError => e + raise e unless e.message == "can't be called from trap context" + # Workaround CRuby issue https://bugs.ruby-lang.org/issues/19473 + # which raises on Mutex#synchronize in trap handler. + # It's expensive to create a Thread just for this, + # but better than failing. + Thread.new { + mutex.synchronize(&block) + }.join end end end - - # We keep a private reference so that time mocking libraries won't break - # Gem::Timeout. - GET_TIME = Process.method(:clock_gettime) - private_constant :GET_TIME + private_constant :Sync # :startdoc: - # Perform an operation in a block, raising an error if it takes longer than + # Perform an operation in a block, raising an exception if it takes longer than # +sec+ seconds to complete. # # +sec+:: Number of seconds to wait for the block to terminate. Any non-negative number @@ -149,45 +212,91 @@ def self.ensure_timeout_thread_created # value of 0 or +nil+ will execute the block without any timeout. # Any negative number will raise an ArgumentError. # +klass+:: Exception Class to raise if the block fails to terminate - # in +sec+ seconds. Omitting will use the default, Gem::Timeout::Error + # in +sec+ seconds. Omitting will use the default, Gem::Timeout::Error. # +message+:: Error message to raise with Exception Class. - # Omitting will use the default, "execution expired" + # Omitting will use the default, "execution expired". # # Returns the result of the block *if* the block completed before - # +sec+ seconds, otherwise throws an exception, based on the value of +klass+. + # +sec+ seconds, otherwise raises an exception, based on the value of +klass+. + # + # The exception raised to terminate the given block is the given +klass+, or + # Gem::Timeout::ExitException if +klass+ is not given. The reason for that behavior + # is that Gem::Timeout::Error inherits from RuntimeError and might be caught unexpectedly by +rescue+. + # Gem::Timeout::ExitException inherits from Exception so it will only be rescued by rescue Exception. + # Note that the Gem::Timeout::ExitException is translated to a Gem::Timeout::Error once it reaches the Gem::Timeout.timeout call, + # so outside that call it will be a Gem::Timeout::Error. # - # The exception thrown to terminate the given block cannot be rescued inside - # the block unless +klass+ is given explicitly. However, the block can use - # ensure to prevent the handling of the exception. For that reason, this - # method cannot be relied on to enforce timeouts for untrusted blocks. + # In general, be aware that the code block may rescue the exception, and in such a case not respect the timeout. + # Also, the block can use +ensure+ to prevent the handling of the exception. + # For those reasons, this method cannot be relied on to enforce timeouts for untrusted blocks. # # If a scheduler is defined, it will be used to handle the timeout by invoking - # Scheduler#timeout_after. + # Fiber::Scheduler#timeout_after. # # Note that this is both a method of module Gem::Timeout, so you can include # Gem::Timeout into your classes so they have a #timeout method, as well as # a module method, so you can call it directly as Gem::Timeout.timeout(). - def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ + # + # ==== Ensuring the exception does not fire inside ensure blocks + # + # When using Gem::Timeout.timeout, it can be desirable to ensure the timeout exception does not fire inside an +ensure+ block. + # The simplest and best way to do so is to put the Gem::Timeout.timeout call inside the body of the +begin+/+ensure+/+end+: + # + # begin + # Gem::Timeout.timeout(sec) { some_long_operation } + # ensure + # cleanup # safe, cannot be interrupted by timeout + # end + # + # If that is not feasible, e.g. if there are +ensure+ blocks inside +some_long_operation+, + # they need to not be interrupted by timeout, and it's not possible to move these ensure blocks outside, + # one can use Thread.handle_interrupt to delay the timeout exception like so: + # + # Thread.handle_interrupt(Gem::Timeout::Error => :never) { + # Gem::Timeout.timeout(sec, Gem::Timeout::Error) do + # setup # timeout cannot happen here, no matter how long it takes + # Thread.handle_interrupt(Gem::Timeout::Error => :immediate) { + # some_long_operation # timeout can happen here + # } + # ensure + # cleanup # timeout cannot happen here, no matter how long it takes + # end + # } + # + # An important thing to note is the need to pass an exception +klass+ to Gem::Timeout.timeout, + # otherwise it does not work. Specifically, using Thread.handle_interrupt(Gem::Timeout::ExitException => ...) + # is unsupported and causes subtle errors like raising the wrong exception outside the block, do not use that. + # + # Note that Thread.handle_interrupt is somewhat dangerous because if setup or cleanup hangs + # then the current thread will hang too and the timeout will never fire. + # Also note the block might run for longer than +sec+ seconds: + # e.g. +some_long_operation+ executes for +sec+ seconds + whatever time cleanup takes. + # + # If you want the timeout to only happen on blocking operations, one can use +:on_blocking+ + # instead of +:immediate+. However, that means if the block uses no blocking operations after +sec+ seconds, + # the block will not be interrupted. + def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec message ||= "execution expired" if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after) - return scheduler.timeout_after(sec, klass || Error, message, &block) - end - - Gem::Timeout.ensure_timeout_thread_created - perform = Proc.new do |exc| - request = Request.new(Thread.current, sec, exc, message) - QUEUE_MUTEX.synchronize do - QUEUE << request - CONDVAR.signal + perform = Proc.new do |exc| + scheduler.timeout_after(sec, exc, message, &block) end - begin - return yield(sec) - ensure - request.finished + else + state = State.instance + state.ensure_timeout_thread_created + + perform = Proc.new do |exc| + request = Request.new(Thread.current, sec, exc, message) + state.add_request(request) + begin + return yield(sec) + ensure + request.finished + end end end @@ -197,5 +306,9 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ Error.handle_timeout(message, &perform) end end - module_function :timeout + + # See Gem::Timeout.timeout + private def timeout(*args, &block) + Gem::Timeout.timeout(*args, &block) + end end diff --git a/lib/rubygems/vendor/uri/lib/uri/common.rb b/lib/rubygems/vendor/uri/lib/uri/common.rb index f6c7b1bd0469..b648d9b8ca6f 100644 --- a/lib/rubygems/vendor/uri/lib/uri/common.rb +++ b/lib/rubygems/vendor/uri/lib/uri/common.rb @@ -271,7 +271,7 @@ def self.parse(uri) # # => # # def self.join(*str) - DEFAULT_PARSER.join(*str) + PARSER.join(*str) end # diff --git a/lib/rubygems/vendor/uri/lib/uri/generic.rb b/lib/rubygems/vendor/uri/lib/uri/generic.rb index d0bc77dfda28..e1b14f43fdbb 100644 --- a/lib/rubygems/vendor/uri/lib/uri/generic.rb +++ b/lib/rubygems/vendor/uri/lib/uri/generic.rb @@ -466,7 +466,7 @@ def userinfo=(userinfo) # # uri = Gem::URI.parse("http://john:S3nsit1ve@my.example.com") # uri.user = "sam" - # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com" + # uri.to_s #=> "http://sam@my.example.com" # def user=(user) check_user(user) @@ -1021,9 +1021,21 @@ def merge_path(base, rel) # RFC2396, Section 5.2, 6), a) base_path << '' if base_path.last == '..' - while i = base_path.index('..') - base_path.slice!(i - 1, 2) + # Remove "/.." pairs in a single left-to-right pass (O(n)). + # This deliberately differs from the relative-path stack handling + # below: a leading ".." (one with no preceding segment left to + # cancel) discards the whole base path, reproducing the previous + # index/slice! implementation exactly. + reduced = [] + base_path.each do |seg| + if seg == '..' + break if reduced.empty? + reduced.pop + else + reduced << seg + end end + base_path = reduced if (first = rel_path.first) and first.empty? base_path.clear diff --git a/lib/rubygems/vendor/uri/lib/uri/version.rb b/lib/rubygems/vendor/uri/lib/uri/version.rb index 7ee577887b37..56c7229bd672 100644 --- a/lib/rubygems/vendor/uri/lib/uri/version.rb +++ b/lib/rubygems/vendor/uri/lib/uri/version.rb @@ -1,6 +1,6 @@ module Gem::URI # :stopdoc: - VERSION = '1.1.1'.freeze + VERSION = '1.1.2'.freeze VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze # :startdoc: end diff --git a/tool/automatiek/net-http-persistent-v4.0.6.patch b/tool/automatiek/net-http-persistent-v4.0.8.patch similarity index 60% rename from tool/automatiek/net-http-persistent-v4.0.6.patch rename to tool/automatiek/net-http-persistent-v4.0.8.patch index 65867aefdc8b..c9740ed36fc2 100644 --- a/tool/automatiek/net-http-persistent-v4.0.6.patch +++ b/tool/automatiek/net-http-persistent-v4.0.8.patch @@ -1,29 +1,29 @@ diff --git b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb -index f57e64df9c..52a8051183 100644 --- b/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +++ a/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb -@@ -1,12 +1,11 @@ +@@ -1,14 +1,9 @@ -require_relative '../../../../../../../../lib/rubygems/vendor/net-http/lib/net/http' -require_relative '../../../../../../../../lib/rubygems/vendor/uri/lib/uri' --require 'cgi' # for escaping --require_relative '../../../../connection_pool/lib/connection_pool' -- +require_relative '../../../../../vendored_net_http' +require_relative '../../../../../vendored_uri' - begin -- require_relative '../../../../../../../../lib/rubygems/vendor/net-http/lib/net/http/pipeline' -+ require 'cgi/escape' - rescue LoadError -+ require 'cgi/util' # for escaping - end -+require_relative '../../../../connection_pool/lib/connection_pool' + require 'cgi/escape' + require 'cgi/util' unless defined?(CGI::EscapeExt) + require_relative '../../../../connection_pool/lib/connection_pool' +-begin +- require_relative '../../../../../../../../lib/rubygems/vendor/net-http/lib/net/http/pipeline' +-rescue LoadError +-end +- autoload :OpenSSL, 'openssl' -@@ -762,23 +761,6 @@ def max_retries= retries - reconnect - end + ## +@@ -770,23 +765,6 @@ + @max_retries = retries + reconnect +- end +- - ## - # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a - # block is given. Returns all responses received. @@ -39,17 +39,6 @@ index f57e64df9c..52a8051183 100644 - connection_for uri do |connection| - connection.http.pipeline requests, &block - end -- end -- - ## - # Sets this client's SSL private key - -@@ -826,7 +808,7 @@ def proxy= proxy - @proxy_connection_id = [nil, *@proxy_args].join ':' - - if @proxy_uri.query then -- @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? } -+ @no_proxy = Gem::URI.decode_www_form(@proxy_uri.query).filter_map { |k, v| v if k == 'no_proxy' }.join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? } - end - end + end + ## diff --git a/tool/automatiek/net-http-v0.7.0.patch b/tool/automatiek/net-http-v0.10.0.patch similarity index 91% rename from tool/automatiek/net-http-v0.7.0.patch rename to tool/automatiek/net-http-v0.10.0.patch index 7ef5cb203897..0c66888b2e38 100644 --- a/tool/automatiek/net-http-v0.7.0.patch +++ b/tool/automatiek/net-http-v0.10.0.patch @@ -1,5 +1,5 @@ -diff --git b/lib/rubygems/vendor/net-http/lib/net/http/status.rb a/lib/rubygems/vendor/net-http/lib/net/http/status.rb -index cd5177fcbc..9110b108b8 100644 +diff --git a/lib/rubygems/vendor/net-http/lib/net/http/status.rb b/lib/rubygems/vendor/net-http/lib/net/http/status.rb +index cd5177f..9110b10 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http/status.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http/status.rb @@ -50,13 +50,13 @@ @@ -28,7 +28,7 @@ index cd5177fcbc..9110b108b8 100644 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', diff --git a/lib/rubygems/vendor/net-http/lib/net/http.rb b/lib/rubygems/vendor/net-http/lib/net/http.rb -index e5258175add..e1ccc056100 100644 +index 7a480fc..bdc4283 100644 --- a/lib/rubygems/vendor/net-http/lib/net/http.rb +++ b/lib/rubygems/vendor/net-http/lib/net/http.rb @@ -22,7 +22,6 @@ @@ -39,7 +39,7 @@ index e5258175add..e1ccc056100 100644 autoload :OpenSSL, 'openssl' module Gem::Net #:nodoc: -@@ -1531,6 +1530,29 @@ def use_ssl=(flag) +@@ -1539,6 +1538,29 @@ def use_ssl=(flag) SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym }.freeze # :nodoc: @@ -69,7 +69,7 @@ index e5258175add..e1ccc056100 100644 # Sets or returns the path to a CA certification file in PEM format. attr_accessor :ca_file -@@ -1714,7 +1736,7 @@ def connect +@@ -1738,7 +1760,7 @@ def connect # Server Name Indication (SNI) RFC 3546/6066 case @address diff --git a/tool/automatiek/thor-v1.4.0.patch b/tool/automatiek/thor-v1.5.0.patch similarity index 77% rename from tool/automatiek/thor-v1.4.0.patch rename to tool/automatiek/thor-v1.5.0.patch index 216e3eb41c9e..ece40ec57aba 100644 --- a/tool/automatiek/thor-v1.4.0.patch +++ b/tool/automatiek/thor-v1.5.0.patch @@ -1,41 +1,27 @@ -diff --git a/bundler/lib/bundler/vendor/thor/lib/thor.rb b/bundler/lib/bundler/vendor/thor/lib/thor.rb -index bfd9f5c914..945bdbd551 100644 ---- a/bundler/lib/bundler/vendor/thor/lib/thor.rb -+++ b/bundler/lib/bundler/vendor/thor/lib/thor.rb -@@ -625,7 +625,7 @@ def normalize_command_name(meth) #:nodoc: - # alias name. - def find_command_possibilities(meth) - len = meth.to_s.length -- possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort -+ possibilities = all_commands.reject { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort - unique_possibilities = possibilities.map { |k| map[k] || k }.uniq - - if possibilities.include?(meth) - diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb b/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb -index 95f8b16e31..f0ce6df96c 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -2,7 +2,7 @@ require_relative "group" - + require "digest/sha2" -require "pathname" +require "pathname" unless defined?(Pathname) - + class Bundler::Thor::Runner < Bundler::Thor #:nodoc: map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb -index 6a9176331..5d708fadc 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb -@@ -1,5 +1,4 @@ +@@ -1,7 +1,6 @@ + # frozen_string_literal: true + require_relative "basic" -require_relative "lcs_diff" class Bundler::Thor module Shell -@@ -7,8 +6,6 @@ module Shell +@@ -9,8 +8,6 @@ # Bundler::Thor::Shell::Basic to see all available methods. # class Color < Basic @@ -45,7 +31,6 @@ index 6a9176331..5d708fadc 100644 CLEAR = "\e[0m" # The start of an ANSI bold sequence. diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb -index 6091485ac..0277b882b 100644 --- a/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb +++ b/bundler/lib/bundler/vendor/thor/lib/thor/shell/html.rb @@ -1,5 +1,4 @@ @@ -54,7 +39,7 @@ index 6091485ac..0277b882b 100644 class Bundler::Thor module Shell -@@ -7,8 +6,6 @@ module Shell +@@ -7,8 +6,6 @@ # Bundler::Thor::Shell::Basic to see all available methods. # class HTML < Basic @@ -65,7 +50,6 @@ index 6091485ac..0277b882b 100644 diff --git a/bundler/lib/bundler/vendor/thor/lib/thor/shell/lcs_diff.rb b/bundler/lib/bundler/vendor/thor/lib/thor/shell/lcs_diff.rb deleted file mode 100644 -index 81268a9f0..000000000 --- a/bundler/lib/bundler/vendor/thor/lib/thor/shell/lcs_diff.rb +++ /dev/null @@ -1,49 +0,0 @@ diff --git a/tool/automatiek/timeout-v0.4.3.patch b/tool/automatiek/timeout-v0.6.1.patch similarity index 70% rename from tool/automatiek/timeout-v0.4.3.patch rename to tool/automatiek/timeout-v0.6.1.patch index 91cca25244b9..a283d854eefa 100644 --- a/tool/automatiek/timeout-v0.4.3.patch +++ b/tool/automatiek/timeout-v0.6.1.patch @@ -1,5 +1,4 @@ diff --git a/lib/rubygems/vendor/timeout/lib/timeout.rb b/lib/rubygems/vendor/timeout/lib/timeout.rb -index cc5d05ce670..455c504f470 100644 --- a/lib/rubygems/vendor/timeout/lib/timeout.rb +++ b/lib/rubygems/vendor/timeout/lib/timeout.rb @@ -1,5 +1,5 @@ @@ -9,9 +8,9 @@ index cc5d05ce670..455c504f470 100644 # # == Synopsis # -@@ -166,7 +166,7 @@ def self.ensure_timeout_thread_created - # a module method, so you can call it directly as Gem::Timeout.timeout(). - def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ +@@ -277,7 +277,7 @@ + # the block will not be interrupted. + def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? - raise ArgumentError, "Gem::Timeout sec must be a non-negative number" if 0 > sec + raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec diff --git a/tool/automatiek/vendor.rb b/tool/automatiek/vendor.rb index 444695b731ad..2713bb6aadec 100755 --- a/tool/automatiek/vendor.rb +++ b/tool/automatiek/vendor.rb @@ -81,19 +81,19 @@ def require_target vendored_gems = [ # RubyGems VendoredGem.new(name: "molinillo", namespace: "Molinillo", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/molinillo", license_path: "LICENSE", extra_dependencies: %w[tsort/lib/rubygems/vendor/tsort], patch_name: "molinillo-master.patch"), - VendoredGem.new(name: "net-http", namespace: "Net", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/net-http", license_path: "COPYING", extra_dependencies: %w[net-protocol timeout uri/lib/rubygems/vendor/uri], skip_dependencies: %w[uri], patch_name: "net-http-v0.7.0.patch"), - VendoredGem.new(name: "net-http-persistent", namespace: "Net::HTTP::Persistent", prefix: "Gem", vendor_lib: "bundler/lib/bundler/vendor/net-http-persistent", license_path: "README.rdoc", extra_dependencies: %w[net-http uri/lib/rubygems/vendor/uri], patch_name: "net-http-persistent-v4.0.6.patch"), - VendoredGem.new(name: "net-protocol", namespace: "Net", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/net-protocol", license_path: "LICENSE.txt"), + VendoredGem.new(name: "net-http", namespace: "Net", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/net-http", license_path: "COPYING", extra_dependencies: %w[timeout uri/lib/rubygems/vendor/uri], skip_dependencies: %w[uri], patch_name: "net-http-v0.10.0.patch"), + VendoredGem.new(name: "net-http-persistent", namespace: "Net::HTTP::Persistent", prefix: "Gem", vendor_lib: "bundler/lib/bundler/vendor/net-http-persistent", license_path: "README.rdoc", extra_dependencies: %w[net-http uri/lib/rubygems/vendor/uri], patch_name: "net-http-persistent-v4.0.8.patch"), + VendoredGem.new(name: "net-protocol", namespace: "Net", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/net-protocol", license_path: "COPYING"), VendoredGem.new(name: "optparse", namespace: "OptionParser", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/optparse", license_path: "COPYING", extra_dependencies: %w[uri/lib/rubygems/vendor/uri], patch_name: "optparse-v0.8.0.patch"), VendoredGem.new(name: "securerandom", namespace: "SecureRandom", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/securerandom", license_path: "COPYING"), - VendoredGem.new(name: "timeout", namespace: "Timeout", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/timeout", license_path: "COPYING", patch_name: "timeout-v0.4.3.patch"), + VendoredGem.new(name: "timeout", namespace: "Timeout", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/timeout", license_path: "COPYING", patch_name: "timeout-v0.6.1.patch"), VendoredGem.new(name: "tsort", namespace: "TSort", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/tsort", license_path: "LICENSE.txt"), VendoredGem.new(name: "uri", namespace: "URI", prefix: "Gem", vendor_lib: "lib/rubygems/vendor/uri", license_path: "COPYING"), # Bundler VendoredGem.new(name: "connection_pool", namespace: "ConnectionPool", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/connection_pool", license_path: "LICENSE", patch_name: "connection_pool-v2.4.1.patch", extra_dependencies: %w[timeout]), VendoredGem.new(name: "fileutils", namespace: "FileUtils", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/fileutils", license_path: "COPYING"), VendoredGem.new(name: "pub_grub", namespace: "PubGrub", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/pub_grub", license_path: "LICENSE.txt"), - VendoredGem.new(name: "thor", namespace: "Thor", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/thor", license_path: "LICENSE.md", patch_name: "thor-v1.4.0.patch"), + VendoredGem.new(name: "thor", namespace: "Thor", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/thor", license_path: "LICENSE.md", patch_name: "thor-v1.5.0.patch"), VendoredGem.new(name: "uri", namespace: "URI", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/uri", license_path: "COPYING"), VendoredGem.new(name: "securerandom", namespace: "SecureRandom", prefix: "Bundler", vendor_lib: "bundler/lib/bundler/vendor/securerandom", license_path: "COPYING"), ].group_by(&:name) diff --git a/tool/bundler/vendor_gems.rb b/tool/bundler/vendor_gems.rb index ebffc281f50b..398799a33d03 100644 --- a/tool/bundler/vendor_gems.rb +++ b/tool/bundler/vendor_gems.rb @@ -2,15 +2,16 @@ source "https://rubygems.org" -gem "fileutils", "1.8.0" +gem "connection_pool", "2.5.5" # connection_pool 3.x does not parse on Ruby 3.3.0 +gem "fileutils", "1.8.1" gem "molinillo", github: "cocoapods/molinillo", ref: "1d62d7d5f448e79418716dc779a4909509ccda2a" -gem "net-http", "0.7.0" # net-http-0.8.0 is broken with JRuby -gem "net-http-persistent", "4.0.6" -gem "net-protocol", "0.2.2" -gem "optparse", "0.8.0" +gem "net-http", "0.10.0" +gem "net-http-persistent", "4.0.8" +gem "net-protocol", "0.4.0" +gem "optparse", "0.8.1" gem "pub_grub", github: "jhawthorn/pub_grub", ref: "df6add45d1b4d122daff2f959c9bd1ca93d14261" gem "securerandom", "0.4.1" -gem "timeout", "0.4.4" -gem "thor", "1.4.0" +gem "timeout", "0.6.1" +gem "thor", "1.5.0" gem "tsort", "0.2.0" -gem "uri", "1.1.1" +gem "uri", "1.1.2" diff --git a/tool/bundler/vendor_gems.rb.lock b/tool/bundler/vendor_gems.rb.lock index c6f63b246cf7..879d45439482 100644 --- a/tool/bundler/vendor_gems.rb.lock +++ b/tool/bundler/vendor_gems.rb.lock @@ -15,20 +15,21 @@ GIT GEM remote: https://rubygems.org/ specs: - connection_pool (2.5.4) - fileutils (1.8.0) - net-http (0.7.0) - uri - net-http-persistent (4.0.6) - connection_pool (~> 2.2, >= 2.2.4) - net-protocol (0.2.2) + connection_pool (2.5.5) + fileutils (1.8.1) + net-http (0.10.0) + net-protocol (>= 0.3.0) + uri (>= 0.11.1) + net-http-persistent (4.0.8) + connection_pool (>= 2.2.4, < 4) + net-protocol (0.4.0) timeout - optparse (0.8.0) + optparse (0.8.1) securerandom (0.4.1) - thor (1.4.0) - timeout (0.4.4) + thor (1.5.0) + timeout (0.6.1) tsort (0.2.0) - uri (1.1.1) + uri (1.1.2) PLATFORMS java @@ -40,33 +41,34 @@ PLATFORMS x86_64-linux DEPENDENCIES - fileutils (= 1.8.0) + connection_pool (= 2.5.5) + fileutils (= 1.8.1) molinillo! - net-http (= 0.7.0) - net-http-persistent (= 4.0.6) - net-protocol (= 0.2.2) - optparse (= 0.8.0) + net-http (= 0.10.0) + net-http-persistent (= 4.0.8) + net-protocol (= 0.4.0) + optparse (= 0.8.1) pub_grub! securerandom (= 0.4.1) - thor (= 1.4.0) - timeout (= 0.4.4) + thor (= 1.5.0) + timeout (= 0.6.1) tsort (= 0.2.0) - uri (= 1.1.1) + uri (= 1.1.2) CHECKSUMS - connection_pool (2.5.4) sha256=e9e1922327416091f3f6542f5f4446c2a20745276b9aa796dd0bb2fd0ea1e70a - fileutils (1.8.0) sha256=8c6b1df54e2540bdb2f39258f08af78853aa70bad52b4d394bbc6424593c6e02 + connection_pool (2.5.5) sha256=e54ff92855753df1fd7c59fa04a398833355f27dd14c074f8c83a05f72a716ad + fileutils (1.8.1) sha256=2d541a1ee34a9ed5c4f054be8fb819a26b5d654488a31dc8806917cf448b9cc3 molinillo (0.8.0) - net-http (0.7.0) sha256=4db7d9f558f8ffd4dcf832d0aefd02320c569c7d4f857def49e585069673a425 - net-http-persistent (4.0.6) sha256=2abb3a04438edf6cb9e0e7e505969605f709eda3e3c5211beadd621a2c84dd5d - net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 - optparse (0.8.0) sha256=ef6b7fbaf7ec331474f325bc08dd5622e6e1e651007a5341330ee4b08ce734f0 + net-http (0.10.0) sha256=1a2ff6e37a05724973c43f2bf7bf105f59c36a4cd0a7e9b1c021a0a566987c27 + net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6 + net-protocol (0.4.0) sha256=f9f90c8c6f64794a45160bf422177a336db12809de3beeeaa26b88fcd2e8c853 + optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a pub_grub (0.5.0) securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d - timeout (0.4.4) sha256=f0f6f970104b82427cd990680f539b6bbb8b1e55efa913a55c6492935e4e0edb + thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 + timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f - uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + uri (1.1.2) sha256=89292e98eabbef6248b0a0ed5e7e398e152f83f17560023a6d94a7f492b169fb BUNDLED WITH 4.0.22