From a79a1d30289b326c8b6c6deb00f1431c693a4b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 14 Dec 2023 14:16:59 +0100 Subject: [PATCH] [rubygems/rubygems] Upgrade vendored libraries To match the versions that will be included in final ruby release. https://github.com/rubygems/rubygems/commit/84394919fb --- .../connection_pool/lib/connection_pool.rb | 55 +- .../lib/connection_pool/version.rb | 2 +- lib/bundler/vendor/fileutils/lib/fileutils.rb | 28 +- lib/bundler/vendor/tsort/lib/tsort.rb | 3 + lib/bundler/vendor/uri/lib/uri/common.rb | 386 ++++++++----- lib/bundler/vendor/uri/lib/uri/generic.rb | 1 + .../vendor/uri/lib/uri/rfc3986_parser.rb | 126 +++-- lib/bundler/vendor/uri/lib/uri/version.rb | 2 +- lib/rubygems/optparse/lib/optparse.rb | 56 +- lib/rubygems/resolv/lib/resolv.rb | 511 +++++++++++++++++- lib/rubygems/tsort/lib/tsort.rb | 3 + 11 files changed, 951 insertions(+), 222 deletions(-) diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb index ee59d32507..317088a866 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb @@ -36,14 +36,57 @@ end # Accepts the following options: # - :size - number of connections to pool, defaults to 5 # - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds +# - :auto_reload_after_fork - automatically drop all connections after fork, defaults to true # class Bundler::ConnectionPool - DEFAULTS = {size: 5, timeout: 5} + DEFAULTS = {size: 5, timeout: 5, auto_reload_after_fork: true} def self.wrap(options, &block) Wrapper.new(options, &block) end + if Process.respond_to?(:fork) + INSTANCES = ObjectSpace::WeakMap.new + private_constant :INSTANCES + + def self.after_fork + INSTANCES.values.each do |pool| + next unless pool.auto_reload_after_fork + + # We're on after fork, so we know all other threads are dead. + # All we need to do is to ensure the main thread doesn't have a + # checked out connection + pool.checkin(force: true) + pool.reload do |connection| + # Unfortunately we don't know what method to call to close the connection, + # so we try the most common one. + connection.close if connection.respond_to?(:close) + end + end + nil + end + + if ::Process.respond_to?(:_fork) # MRI 3.1+ + module ForkTracker + def _fork + pid = super + if pid == 0 + Bundler::ConnectionPool.after_fork + end + pid + end + end + Process.singleton_class.prepend(ForkTracker) + end + else + INSTANCES = nil + private_constant :INSTANCES + + def self.after_fork + # noop + end + end + def initialize(options = {}, &block) raise ArgumentError, "Connection pool requires a block" unless block @@ -51,10 +94,12 @@ class Bundler::ConnectionPool @size = Integer(options.fetch(:size)) @timeout = options.fetch(:timeout) + @auto_reload_after_fork = options.fetch(:auto_reload_after_fork) @available = TimedStack.new(@size, &block) @key = :"pool-#{@available.object_id}" @key_count = :"pool-#{@available.object_id}-count" + INSTANCES[self] = self if INSTANCES end def with(options = {}) @@ -81,16 +126,16 @@ class Bundler::ConnectionPool end end - def checkin + def checkin(force: false) if ::Thread.current[@key] - if ::Thread.current[@key_count] == 1 + if ::Thread.current[@key_count] == 1 || force @available.push(::Thread.current[@key]) ::Thread.current[@key] = nil ::Thread.current[@key_count] = nil else ::Thread.current[@key_count] -= 1 end - else + elsif !force raise Bundler::ConnectionPool::Error, "no connections are checked out" end @@ -117,6 +162,8 @@ class Bundler::ConnectionPool # Size of this connection pool attr_reader :size + # Automatically drop all connections after fork + attr_reader :auto_reload_after_fork # Number of pool entries available for checkout at this instant. def available diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb index 56ebf69902..384d6fc977 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb @@ -1,3 +1,3 @@ class Bundler::ConnectionPool - VERSION = "2.3.0" + VERSION = "2.4.1" end diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb index 211311c069..6db19caf6f 100644 --- a/lib/bundler/vendor/fileutils/lib/fileutils.rb +++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb @@ -180,7 +180,7 @@ end # - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452]. # module Bundler::FileUtils - VERSION = "1.7.0" + VERSION = "1.7.2" def self.private_module_function(name) #:nodoc: module_function name @@ -192,8 +192,6 @@ module Bundler::FileUtils # # Bundler::FileUtils.pwd # => "/rdoc/fileutils" # - # Bundler::FileUtils.getwd is an alias for Bundler::FileUtils.pwd. - # # Related: Bundler::FileUtils.cd. # def pwd @@ -235,8 +233,6 @@ module Bundler::FileUtils # cd .. # cd fileutils # - # Bundler::FileUtils.chdir is an alias for Bundler::FileUtils.cd. - # # Related: Bundler::FileUtils.pwd. # def cd(dir, verbose: nil, &block) # :yield: dir @@ -515,8 +511,6 @@ module Bundler::FileUtils # Raises an exception if +dest+ is the path to an existing file # and keyword argument +force+ is not +true+. # - # Bundler::FileUtils#link is an alias for Bundler::FileUtils#ln. - # # Related: Bundler::FileUtils.link_entry (has different options). # def ln(src, dest, force: nil, noop: nil, verbose: nil) @@ -707,8 +701,6 @@ module Bundler::FileUtils # ln -sf src2.txt dest2.txt # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3 # - # Bundler::FileUtils.symlink is an alias for Bundler::FileUtils.ln_s. - # # Related: Bundler::FileUtils.ln_sf. # def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil) @@ -876,8 +868,6 @@ module Bundler::FileUtils # # Raises an exception if +src+ is a directory. # - # Bundler::FileUtils.copy is an alias for Bundler::FileUtils.cp. - # # Related: {methods for copying}[rdoc-ref:FileUtils@Copying]. # def cp(src, dest, preserve: nil, noop: nil, verbose: nil) @@ -1164,8 +1154,6 @@ module Bundler::FileUtils # mv src0 dest0 # mv src1.txt src1 dest1 # - # Bundler::FileUtils.move is an alias for Bundler::FileUtils.mv. - # def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil) fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose return if noop @@ -1223,8 +1211,6 @@ module Bundler::FileUtils # # rm src0.dat src0.txt # - # Bundler::FileUtils.remove is an alias for Bundler::FileUtils.rm. - # # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting]. # def rm(list, force: nil, noop: nil, verbose: nil) @@ -1250,8 +1236,6 @@ module Bundler::FileUtils # # See Bundler::FileUtils.rm for keyword arguments. # - # Bundler::FileUtils.safe_unlink is an alias for Bundler::FileUtils.rm_f. - # # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting]. # def rm_f(list, noop: nil, verbose: nil) @@ -1339,8 +1323,6 @@ module Bundler::FileUtils # # See Bundler::FileUtils.rm_r for keyword arguments. # - # Bundler::FileUtils.rmtree is an alias for Bundler::FileUtils.rm_rf. - # # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting]. # def rm_rf(list, noop: nil, verbose: nil, secure: nil) @@ -1642,7 +1624,13 @@ module Bundler::FileUtils st = File.stat(s) unless File.exist?(d) and compare_file(s, d) remove_file d, true - copy_file s, d + if d.end_with?('/') + mkdir_p d + copy_file s, d + File.basename(s) + else + mkdir_p File.expand_path('..', d) + copy_file s, d + end File.utime st.atime, st.mtime, d if preserve File.chmod fu_mode(mode, st), d if mode File.chown uid, gid, d if uid or gid diff --git a/lib/bundler/vendor/tsort/lib/tsort.rb b/lib/bundler/vendor/tsort/lib/tsort.rb index 4a0e1a4e25..cf8731f760 100644 --- a/lib/bundler/vendor/tsort/lib/tsort.rb +++ b/lib/bundler/vendor/tsort/lib/tsort.rb @@ -122,6 +122,9 @@ # module Bundler::TSort + + VERSION = "0.2.0" + class Cyclic < StandardError end diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb index 914a4c7581..93f4f226ad 100644 --- a/lib/bundler/vendor/uri/lib/uri/common.rb +++ b/lib/bundler/vendor/uri/lib/uri/common.rb @@ -68,16 +68,32 @@ module Bundler::URI end private_constant :Schemes + # Registers the given +klass+ as the class to be instantiated + # when parsing a \Bundler::URI with the given +scheme+: # - # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+. - # Note that currently only schemes which after .upcase are valid constant names - # can be registered (no -/+/. allowed). + # Bundler::URI.register_scheme('MS_SEARCH', Bundler::URI::Generic) # => Bundler::URI::Generic + # Bundler::URI.scheme_list['MS_SEARCH'] # => Bundler::URI::Generic # + # Note that after calling String#upcase on +scheme+, it must be a valid + # constant name. def self.register_scheme(scheme, klass) Schemes.const_set(scheme.to_s.upcase, klass) end - # Returns a Hash of the defined schemes. + # Returns a hash of the defined schemes: + # + # Bundler::URI.scheme_list + # # => + # {"MAILTO"=>Bundler::URI::MailTo, + # "LDAPS"=>Bundler::URI::LDAPS, + # "WS"=>Bundler::URI::WS, + # "HTTP"=>Bundler::URI::HTTP, + # "HTTPS"=>Bundler::URI::HTTPS, + # "LDAP"=>Bundler::URI::LDAP, + # "FILE"=>Bundler::URI::File, + # "FTP"=>Bundler::URI::FTP} + # + # Related: Bundler::URI.register_scheme. def self.scheme_list Schemes.constants.map { |name| [name.to_s.upcase, Schemes.const_get(name)] @@ -88,9 +104,21 @@ module Bundler::URI private_constant :INITIAL_SCHEMES Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor) + # Returns a new object constructed from the given +scheme+, +arguments+, + # and +default+: # - # Construct a Bundler::URI instance, using the scheme to detect the appropriate class - # from +Bundler::URI.scheme_list+. + # - The new object is an instance of Bundler::URI.scheme_list[scheme.upcase]. + # - The object is initialized by calling the class initializer + # using +scheme+ and +arguments+. + # See Bundler::URI::Generic.new. + # + # Examples: + # + # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top'] + # Bundler::URI.for('https', *values) + # # => # + # Bundler::URI.for('foo', *values, default: Bundler::URI::HTTP) + # # => # # def self.for(scheme, *arguments, default: Generic) const_name = scheme.to_s.upcase @@ -121,95 +149,49 @@ module Bundler::URI # class BadURIError < Error; end + # Returns a 9-element array representing the parts of the \Bundler::URI + # formed from the string +uri+; + # each array element is a string or +nil+: # - # == Synopsis - # - # Bundler::URI::split(uri) - # - # == Args - # - # +uri+:: - # String with Bundler::URI. - # - # == Description - # - # Splits the string on following parts and returns array with result: - # - # * Scheme - # * Userinfo - # * Host - # * Port - # * Registry - # * Path - # * Opaque - # * Query - # * Fragment - # - # == Usage - # - # require 'bundler/vendor/uri/lib/uri' - # - # Bundler::URI.split("http://www.ruby-lang.org/") - # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil] + # names = %w[scheme userinfo host port registry path opaque query fragment] + # values = Bundler::URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # names.zip(values) + # # => + # [["scheme", "https"], + # ["userinfo", "john.doe"], + # ["host", "www.example.com"], + # ["port", "123"], + # ["registry", nil], + # ["path", "/forum/questions/"], + # ["opaque", nil], + # ["query", "tag=networking&order=newest"], + # ["fragment", "top"]] # def self.split(uri) RFC3986_PARSER.split(uri) end + # Returns a new \Bundler::URI object constructed from the given string +uri+: # - # == Synopsis + # Bundler::URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => # + # Bundler::URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => # # - # Bundler::URI::parse(uri_str) - # - # == Args - # - # +uri_str+:: - # String with Bundler::URI. - # - # == Description - # - # Creates one of the Bundler::URI's subclasses instance from the string. - # - # == Raises - # - # Bundler::URI::InvalidURIError:: - # Raised if Bundler::URI given is not a correct one. - # - # == Usage - # - # require 'bundler/vendor/uri/lib/uri' - # - # uri = Bundler::URI.parse("http://www.ruby-lang.org/") - # # => # - # uri.scheme - # # => "http" - # uri.host - # # => "www.ruby-lang.org" - # - # It's recommended to first ::escape the provided +uri_str+ if there are any - # invalid Bundler::URI characters. + # It's recommended to first ::escape string +uri+ + # if it may contain invalid Bundler::URI characters. # def self.parse(uri) RFC3986_PARSER.parse(uri) end + # Merges the given Bundler::URI strings +str+ + # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html]. # - # == Synopsis + # Each string in +str+ is converted to an + # {RFC3986 Bundler::URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged. # - # Bundler::URI::join(str[, str, ...]) - # - # == Args - # - # +str+:: - # String(s) to work with, will be converted to RFC3986 URIs before merging. - # - # == Description - # - # Joins URIs. - # - # == Usage - # - # require 'bundler/vendor/uri/lib/uri' + # Examples: # # Bundler::URI.join("http://example.com/","main.rbx") # # => # @@ -254,7 +236,7 @@ module Bundler::URI # Bundler::URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") # # => ["http://foo.example.com/bla", "mailto:test@example.com"] # - def self.extract(str, schemes = nil, &block) + def self.extract(str, schemes = nil, &block) # :nodoc: warn "Bundler::URI.extract is obsolete", uplevel: 1 if $VERBOSE DEFAULT_PARSER.extract(str, schemes, &block) end @@ -291,7 +273,7 @@ module Bundler::URI # p $& # end # - def self.regexp(schemes = nil) + def self.regexp(schemes = nil)# :nodoc: warn "Bundler::URI.regexp is obsolete", uplevel: 1 if $VERBOSE DEFAULT_PARSER.make_regexp(schemes) end @@ -314,40 +296,86 @@ module Bundler::URI TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze - # Encodes given +str+ to URL-encoded form data. + # Returns a URL-encoded string derived from the given string +str+. # - # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP - # (ASCII space) to + and converts others to %XX. + # The returned string: # - # If +enc+ is given, convert +str+ to the encoding before percent encoding. + # - Preserves: # - # This is an implementation of - # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data. + # - Characters '*', '.', '-', and '_'. + # - Character in ranges 'a'..'z', 'A'..'Z', + # and '0'..'9'. # - # See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form. + # Example: + # + # Bundler::URI.encode_www_form_component('*.-_azAZ09') + # # => "*.-_azAZ09" + # + # - Converts: + # + # - Character ' ' to character '+'. + # - Any other character to "percent notation"; + # the percent notation for character c is '%%%X' % c.ord. + # + # Example: + # + # Bundler::URI.encode_www_form_component('Here are some punctuation characters: ,;?:') + # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A" + # + # Encoding: + # + # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored. + # - Otherwise +str+ is converted first to Encoding::UTF_8 + # (with suitable character replacements), + # and then to encoding +enc+. + # + # In either case, the returned string has forced encoding Encoding::US_ASCII. + # + # Related: Bundler::URI.encode_uri_component (encodes ' ' as '%20'). def self.encode_www_form_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc) end - # Decodes given +str+ of URL-encoded form data. + # Returns a string decoded from the given \URL-encoded string +str+. # - # This decodes + to SP. + # The given string is first encoded as Encoding::ASCII-8BIT (using String#b), + # then decoded (as below), and finally force-encoded to the given encoding +enc+. # - # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form. + # The returned string: + # + # - Preserves: + # + # - Characters '*', '.', '-', and '_'. + # - Character in ranges 'a'..'z', 'A'..'Z', + # and '0'..'9'. + # + # Example: + # + # Bundler::URI.decode_www_form_component('*.-_azAZ09') + # # => "*.-_azAZ09" + # + # - Converts: + # + # - Character '+' to character ' '. + # - Each "percent notation" to an ASCII character. + # + # Example: + # + # Bundler::URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') + # # => "Here are some punctuation characters: ,;?:" + # + # Related: Bundler::URI.decode_uri_component (preserves '+'). def self.decode_www_form_component(str, enc=Encoding::UTF_8) _decode_uri_component(/\+|%\h\h/, str, enc) end - # Encodes +str+ using URL encoding - # - # This encodes SP to %20 instead of +. + # Like Bundler::URI.encode_www_form_component, except that ' ' (space) + # is encoded as '%20' (instead of '+'). def self.encode_uri_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc) end - # Decodes given +str+ of URL-encoded data. - # - # This does not decode + to SP. + # Like Bundler::URI.decode_www_form_component, except that '+' is preserved. def self.decode_uri_component(str, enc=Encoding::UTF_8) _decode_uri_component(/%\h\h/, str, enc) end @@ -372,33 +400,104 @@ module Bundler::URI end private_class_method :_decode_uri_component - # Generates URL-encoded form data from given +enum+. + # Returns a URL-encoded string derived from the given + # {Enumerable}[rdoc-ref:Enumerable@Enumerable+in+Ruby+Classes] + # +enum+. # - # This generates application/x-www-form-urlencoded data defined in HTML5 - # from given an Enumerable object. + # The result is suitable for use as form data + # for an \HTTP request whose Content-Type is + # 'application/x-www-form-urlencoded'. # - # This internally uses Bundler::URI.encode_www_form_component(str). + # The returned string consists of the elements of +enum+, + # each converted to one or more URL-encoded strings, + # and all joined with character '&'. # - # This method doesn't convert the encoding of given items, so convert them - # before calling this method if you want to send data as other than original - # encoding or mixed encoding data. (Strings which are encoded in an HTML5 - # ASCII incompatible encoding are converted to UTF-8.) + # Simple examples: # - # This method doesn't handle files. When you send a file, use - # multipart/form-data. + # Bundler::URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]]) + # # => "foo=0&bar=1&baz=2" + # Bundler::URI.encode_www_form({foo: 0, bar: 1, baz: 2}) + # # => "foo=0&bar=1&baz=2" # - # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer + # The returned string is formed using method Bundler::URI.encode_www_form_component, + # which converts certain characters: # - # Bundler::URI.encode_www_form([["q", "ruby"], ["lang", "en"]]) - # #=> "q=ruby&lang=en" - # Bundler::URI.encode_www_form("q" => "ruby", "lang" => "en") - # #=> "q=ruby&lang=en" - # Bundler::URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en") - # #=> "q=ruby&q=perl&lang=en" - # Bundler::URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]]) - # #=> "q=ruby&q=perl&lang=en" + # Bundler::URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@') + # # => "f%23o=%2F&b-r=%24&b+z=%40" + # + # When +enum+ is Array-like, each element +ele+ is converted to a field: + # + # - If +ele+ is an array of two or more elements, + # the field is formed from its first two elements + # (and any additional elements are ignored): + # + # name = Bundler::URI.encode_www_form_component(ele[0], enc) + # value = Bundler::URI.encode_www_form_component(ele[1], enc) + # "#{name}=#{value}" + # + # Examples: + # + # Bundler::URI.encode_www_form([%w[foo bar], %w[baz bat bah]]) + # # => "foo=bar&baz=bat" + # Bundler::URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']]) + # # => "foo=0&bar=baz" + # + # - If +ele+ is an array of one element, + # the field is formed from ele[0]: + # + # Bundler::URI.encode_www_form_component(ele[0]) + # + # Example: + # + # Bundler::URI.encode_www_form([['foo'], [:bar], [0]]) + # # => "foo&bar&0" + # + # - Otherwise the field is formed from +ele+: + # + # Bundler::URI.encode_www_form_component(ele) + # + # Example: + # + # Bundler::URI.encode_www_form(['foo', :bar, 0]) + # # => "foo&bar&0" + # + # The elements of an Array-like +enum+ may be mixture: + # + # Bundler::URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat]) + # # => "foo=0&bar=1&baz&bat" + # + # When +enum+ is Hash-like, + # each +key+/+value+ pair is converted to one or more fields: + # + # - If +value+ is + # {Array-convertible}[rdoc-ref:implicit_conversion.rdoc@Array-Convertible+Objects], + # each element +ele+ in +value+ is paired with +key+ to form a field: + # + # name = Bundler::URI.encode_www_form_component(key, enc) + # value = Bundler::URI.encode_www_form_component(ele, enc) + # "#{name}=#{value}" + # + # Example: + # + # Bundler::URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]}) + # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2" + # + # - Otherwise, +key+ and +value+ are paired to form a field: + # + # name = Bundler::URI.encode_www_form_component(key, enc) + # value = Bundler::URI.encode_www_form_component(value, enc) + # "#{name}=#{value}" + # + # Example: + # + # Bundler::URI.encode_www_form({foo: 0, bar: 1, baz: 2}) + # # => "foo=0&bar=1&baz=2" + # + # The elements of a Hash-like +enum+ may be mixture: + # + # Bundler::URI.encode_www_form({foo: [0, 1], bar: 2}) + # # => "foo=0&foo=1&bar=2" # - # See Bundler::URI.encode_www_form_component, Bundler::URI.decode_www_form. def self.encode_www_form(enum, enc=nil) enum.map do |k,v| if v.nil? @@ -419,22 +518,39 @@ module Bundler::URI end.join('&') end - # Decodes URL-encoded form data from given +str+. + # Returns name/value pairs derived from the given string +str+, + # which must be an ASCII string. # - # This decodes application/x-www-form-urlencoded data - # and returns an array of key-value arrays. + # The method may be used to decode the body of Net::HTTPResponse object +res+ + # for which res['Content-Type'] is 'application/x-www-form-urlencoded'. # - # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser, - # so this supports only &-separator, and doesn't support ;-separator. + # The returned data is an array of 2-element subarrays; + # each subarray is a name/value pair (both are strings). + # Each returned string has encoding +enc+, + # and has had invalid characters removed via + # {String#scrub}[rdoc-ref:String#scrub]. # - # ary = Bundler::URI.decode_www_form("a=1&a=2&b=3") - # ary #=> [['a', '1'], ['a', '2'], ['b', '3']] - # ary.assoc('a').last #=> '1' - # ary.assoc('b').last #=> '3' - # ary.rassoc('a').last #=> '2' - # Hash[ary] #=> {"a"=>"2", "b"=>"3"} + # A simple example: + # + # Bundler::URI.decode_www_form('foo=0&bar=1&baz') + # # => [["foo", "0"], ["bar", "1"], ["baz", ""]] + # + # The returned strings have certain conversions, + # similar to those performed in Bundler::URI.decode_www_form_component: + # + # Bundler::URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40') + # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]] + # + # The given string may contain consecutive separators: + # + # Bundler::URI.decode_www_form('foo=0&&bar=1&&baz=2') + # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]] + # + # A different separator may be specified: + # + # Bundler::URI.decode_www_form('foo=0--bar=1--baz', separator: '--') + # # => [["foo", "0"], ["bar", "1"], ["baz", ""]] # - # See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form. def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false) raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only? ary = [] @@ -713,7 +829,15 @@ end # module Bundler::URI module Bundler # - # Returns +uri+ converted to an Bundler::URI object. + # Returns a \Bundler::URI object derived from the given +uri+, + # which may be a \Bundler::URI string or an existing \Bundler::URI object: + # + # # Returns a new Bundler::URI. + # uri = Bundler::URI('http://github.com/ruby/ruby') + # # => # + # # Returns the given Bundler::URI. + # Bundler::URI(uri) + # # => # # def URI(uri) if uri.is_a?(Bundler::URI::Generic) diff --git a/lib/bundler/vendor/uri/lib/uri/generic.rb b/lib/bundler/vendor/uri/lib/uri/generic.rb index 9ae6915826..762c425ac1 100644 --- a/lib/bundler/vendor/uri/lib/uri/generic.rb +++ b/lib/bundler/vendor/uri/lib/uri/generic.rb @@ -1376,6 +1376,7 @@ module Bundler::URI end str end + alias to_str to_s # # Compares two URIs. diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb index a85511c146..4c9882f595 100644 --- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb @@ -1,9 +1,73 @@ -# frozen_string_literal: false +# frozen_string_literal: true module Bundler::URI class RFC3986_Parser # :nodoc: # Bundler::URI defined in RFC3986 - RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*+):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?\g(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ - RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ + HOST = %r[ + (?\[(?: + (? + (?:\h{1,4}:){6} + (?\h{1,4}:\h{1,4} + | (?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d) + \.\g\.\g\.\g) + ) + | ::(?:\h{1,4}:){5}\g + | \h{1,4}?::(?:\h{1,4}:){4}\g + | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g + | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g + | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g + | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g + | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4} + | (?:(?:\h{1,4}:){,6}\h{1,4})?:: + ) + | (?v\h++\.[!$&-.0-9:;=A-Z_a-z~]++) + )\]) + | \g + | (?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+) + ]x + + USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/ + + SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source + SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source + SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source + FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source + + RFC3986_URI = %r[\A + (?#{SEG}){0} + (? + (?#{SCHEME}): + (?// + (? + (?:(?#{USERINFO.source})@)? + (?#{HOST.source.delete(" \n")}) + (?::(?\d*+))? + ) + (?(?:/\g*+)?) + | (?/((?!/)\g++)?) + | (?(?!/)\g++) + | (?) + ) + (?:\?(?[^\#]*+))? + (?:\#(?#{FRAGMENT}))? + )\z]x + + RFC3986_relative_ref = %r[\A + (?#{SEG}){0} + (? + (?// + (? + (?:(?#{USERINFO.source})@)? + (?#{HOST.source.delete(" \n")}(?\d*+))? + ) + (?(?:/\g*+)?) + | (?/\g*+) + | (?#{SEG_NC}++(?:/\g*+)?) + | (?) + ) + (?:\?(?[^#]*+))? + (?:\#(?#{FRAGMENT}))? + )\z]x attr_reader :regexp def initialize @@ -19,9 +83,9 @@ module Bundler::URI uri.ascii_only? or raise InvalidURIError, "Bundler::URI must be ascii only #{uri.dump}" if m = RFC3986_URI.match(uri) - query = m["query".freeze] - scheme = m["scheme".freeze] - opaque = m["path-rootless".freeze] + query = m["query"] + scheme = m["scheme"] + opaque = m["path-rootless"] if opaque opaque << "?#{query}" if query [ scheme, @@ -32,35 +96,35 @@ module Bundler::URI nil, # path opaque, nil, # query - m["fragment".freeze] + m["fragment"] ] else # normal [ scheme, - m["userinfo".freeze], - m["host".freeze], - m["port".freeze], + m["userinfo"], + m["host"], + m["port"], nil, # registry - (m["path-abempty".freeze] || - m["path-absolute".freeze] || - m["path-empty".freeze]), + (m["path-abempty"] || + m["path-absolute"] || + m["path-empty"]), nil, # opaque query, - m["fragment".freeze] + m["fragment"] ] end elsif m = RFC3986_relative_ref.match(uri) [ nil, # scheme - m["userinfo".freeze], - m["host".freeze], - m["port".freeze], + m["userinfo"], + m["host"], + m["port"], nil, # registry, - (m["path-abempty".freeze] || - m["path-absolute".freeze] || - m["path-noscheme".freeze] || - m["path-empty".freeze]), + (m["path-abempty"] || + m["path-absolute"] || + m["path-noscheme"] || + m["path-empty"]), nil, # opaque - m["query".freeze], - m["fragment".freeze] + m["query"], + m["fragment"] ] else raise InvalidURIError, "bad Bundler::URI(is not Bundler::URI?): #{uri.inspect}" @@ -92,14 +156,14 @@ module Bundler::URI def default_regexp # :nodoc: { - SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/, - USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/, - HOST: /\A(?:(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{,4}::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/, - ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, - REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, - QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, - FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, - OPAQUE: /\A(?:[^\/].*)?\z/, + SCHEME: %r[\A#{SCHEME}\z]o, + USERINFO: %r[\A#{USERINFO}\z]o, + HOST: %r[\A#{HOST}\z]o, + ABS_PATH: %r[\A/#{SEG}*+\z]o, + REL_PATH: %r[\A(?!/)#{SEG}++\z]o, + QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z], + FRAGMENT: %r[\A#{FRAGMENT}\z]o, + OPAQUE: %r[\A(?:[^/].*)?\z], PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/, } end diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb index 84b08eee30..1fa1c7c09a 100644 --- a/lib/bundler/vendor/uri/lib/uri/version.rb +++ b/lib/bundler/vendor/uri/lib/uri/version.rb @@ -1,6 +1,6 @@ module Bundler::URI # :stopdoc: - VERSION_CODE = '001202'.freeze + VERSION_CODE = '001300'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb index 1e50bda769..8e700016b0 100644 --- a/lib/rubygems/optparse/lib/optparse.rb +++ b/lib/rubygems/optparse/lib/optparse.rb @@ -48,7 +48,7 @@ # # == Gem::OptionParser # -# === New to \Gem::OptionParser? +# === New to +Gem::OptionParser+? # # See the {Tutorial}[optparse/tutorial.rdoc]. # @@ -152,14 +152,14 @@ # Gem::OptionParser supports the ability to coerce command line arguments # into objects for us. # -# Gem::OptionParser comes with a few ready-to-use kinds of type +# Gem::OptionParser comes with a few ready-to-use kinds of type # coercion. They are: # -# - Date -- Anything accepted by +Date.parse+ -# - DateTime -- Anything accepted by +DateTime.parse+ -# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ -# - URI -- Anything accepted by +URI.parse+ -# - Shellwords -- Anything accepted by +Shellwords.shellwords+ +# - Date -- Anything accepted by +Date.parse+ (need to require +optparse/date+) +# - DateTime -- Anything accepted by +DateTime.parse+ (need to require +optparse/date+) +# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ (need to require +optparse/time+) +# - URI -- Anything accepted by +URI.parse+ (need to require +optparse/uri+) +# - Shellwords -- Anything accepted by +Shellwords.shellwords+ (need to require +optparse/shellwords+) # - String -- Any non-empty string # - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040) # - Float -- Any float. (e.g. 10, 3.14, -100E+13) @@ -425,7 +425,7 @@ # If you have any questions, file a ticket at http://bugs.ruby-lang.org. # class Gem::OptionParser - Gem::OptionParser::Version = "0.3.0" + Gem::OptionParser::Version = "0.4.0" # :stopdoc: NoArgument = [NO_ARGUMENT = :NONE, nil].freeze @@ -1775,7 +1775,16 @@ XXX # # params["bar"] = "x" # --bar x # # params["zot"] = "z" # --zot Z # - def getopts(*args) + # Option +symbolize_names+ (boolean) specifies whether returned Hash keys should be Symbols; defaults to +false+ (use Strings). + # + # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option", symbolize_names: true) + # # params[:a] = true # -a + # # params[:b] = "1" # -b1 + # # params[:foo] = "1" # --foo + # # params[:bar] = "x" # --bar x + # # params[:zot] = "z" # --zot Z + # + def getopts(*args, symbolize_names: false) argv = Array === args.first ? args.shift : default_argv single_options, *long_options = *args @@ -1804,14 +1813,14 @@ XXX end parse_in_order(argv, result.method(:[]=)) - result + symbolize_names ? result.transform_keys(&:to_sym) : result end # # See #getopts. # - def self.getopts(*args) - new.getopts(*args) + def self.getopts(*args, symbolize_names: false) + new.getopts(*args, symbolize_names: symbolize_names) end # @@ -2084,10 +2093,23 @@ XXX f |= Regexp::IGNORECASE if /i/ =~ o f |= Regexp::MULTILINE if /m/ =~ o f |= Regexp::EXTENDED if /x/ =~ o - k = o.delete("imx") - k = nil if k.empty? + case o = o.delete("imx") + when "" + when "u" + s = s.encode(Encoding::UTF_8) + when "e" + s = s.encode(Encoding::EUC_JP) + when "s" + s = s.encode(Encoding::SJIS) + when "n" + f |= Regexp::NOENCODING + else + raise Gem::OptionParser::InvalidArgument, "unknown regexp option - #{o}" + end + else + s ||= all end - Regexp.new(s || all, f, k) + Regexp.new(s, f) end # @@ -2276,8 +2298,8 @@ XXX # rescue Gem::OptionParser::ParseError # end # - def getopts(*args) - options.getopts(self, *args) + def getopts(*args, symbolize_names: false) + options.getopts(self, *args, symbolize_names: symbolize_names) end # diff --git a/lib/rubygems/resolv/lib/resolv.rb b/lib/rubygems/resolv/lib/resolv.rb index 0a41cdde5c..1209d5167a 100644 --- a/lib/rubygems/resolv/lib/resolv.rb +++ b/lib/rubygems/resolv/lib/resolv.rb @@ -37,6 +37,8 @@ end class Gem::Resolv + VERSION = "0.3.0" + ## # Looks up the first IP address for +name+. @@ -82,8 +84,8 @@ class Gem::Resolv ## # Creates a new Gem::Resolv using +resolvers+. - def initialize(resolvers=[Hosts.new, DNS.new]) - @resolvers = resolvers + def initialize(resolvers=nil, use_ipv6: nil) + @resolvers = resolvers || [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(use_ipv6: use_ipv6))] end ## @@ -196,7 +198,7 @@ class Gem::Resolv next unless addr @addr2name[addr] = [] unless @addr2name.include? addr @addr2name[addr] << hostname - @addr2name[addr] += aliases + @addr2name[addr].concat(aliases) @name2addr[hostname] = [] unless @name2addr.include? hostname @name2addr[hostname] << addr aliases.each {|n| @@ -310,6 +312,8 @@ class Gem::Resolv # String:: Path to a file using /etc/resolv.conf's format. # Hash:: Must contain :nameserver, :search and :ndots keys. # :nameserver_port can be used to specify port number of nameserver address. + # :raise_timeout_errors can be used to raise timeout errors + # as exceptions instead of treating the same as an NXDOMAIN response. # # The value of :nameserver should be an address string or # an array of address strings. @@ -406,6 +410,11 @@ class Gem::Resolv end def use_ipv6? # :nodoc: + use_ipv6 = @config.use_ipv6? + unless use_ipv6.nil? + return use_ipv6 + end + begin list = Socket.ip_address_list rescue NotImplementedError @@ -748,7 +757,7 @@ class Gem::Resolv next if @socks_hash[bind_host] begin sock = UDPSocket.new(af) - rescue Errno::EAFNOSUPPORT + rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT next # The kernel doesn't support the address family. end @socks << sock @@ -965,7 +974,7 @@ class Gem::Resolv next unless keyword case keyword when 'nameserver' - nameserver += args + nameserver.concat(args) when 'domain' next if args.empty? search = [args[0]] @@ -1004,6 +1013,7 @@ class Gem::Resolv @mutex.synchronize { unless @initialized @nameserver_port = [] + @use_ipv6 = nil @search = nil @ndots = 1 case @config_info @@ -1028,8 +1038,12 @@ class Gem::Resolv if config_hash.include? :nameserver_port @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] } end + if config_hash.include? :use_ipv6 + @use_ipv6 = config_hash[:use_ipv6] + end @search = config_hash[:search] if config_hash.include? :search @ndots = config_hash[:ndots] if config_hash.include? :ndots + @raise_timeout_errors = config_hash[:raise_timeout_errors] if @nameserver_port.empty? @nameserver_port << ['0.0.0.0', Port] @@ -1083,6 +1097,10 @@ class Gem::Resolv @nameserver_port end + def use_ipv6? + @use_ipv6 + end + def generate_candidates(name) candidates = nil name = Name.create(name) @@ -1116,6 +1134,7 @@ class Gem::Resolv def resolv(name) candidates = generate_candidates(name) timeouts = @timeouts || generate_timeouts + timeout_error = false begin candidates.each {|candidate| begin @@ -1127,11 +1146,13 @@ class Gem::Resolv end } } + timeout_error = true raise ResolvError.new("DNS resolv timeout: #{name}") rescue NXDomain end } rescue ResolvError + raise if @raise_timeout_errors && timeout_error end end @@ -1487,14 +1508,14 @@ class Gem::Resolv } end - def put_name(d) - put_labels(d.to_a) + def put_name(d, compress: true) + put_labels(d.to_a, compress: compress) end - def put_labels(d) + def put_labels(d, compress: true) d.each_index {|i| domain = d[i..-1] - if idx = @names[domain] + if compress && idx = @names[domain] self.put_pack("n", 0xc000 | idx) return else @@ -1518,13 +1539,15 @@ class Gem::Resolv id, flag, qdcount, ancount, nscount, arcount = msg.get_unpack('nnnnnn') o.id = id + o.tc = (flag >> 9) & 1 + o.rcode = flag & 15 + return o unless o.tc.zero? + o.qr = (flag >> 15) & 1 o.opcode = (flag >> 11) & 15 o.aa = (flag >> 10) & 1 - o.tc = (flag >> 9) & 1 o.rd = (flag >> 8) & 1 o.ra = (flag >> 7) & 1 - o.rcode = flag & 15 (1..qdcount).each { name, typeclass = msg.get_question o.add_question(name, typeclass) @@ -1616,6 +1639,14 @@ class Gem::Resolv strings end + def get_list + [].tap do |values| + while @index < @limit + values << yield + end + end + end + def get_name return Name.new(self.get_labels) end @@ -1676,6 +1707,378 @@ class Gem::Resolv end end + ## + # SvcParams for service binding RRs. [RFC9460] + + class SvcParams + include Enumerable + + ## + # Create a list of SvcParams with the given initial content. + # + # +params+ has to be an enumerable of +SvcParam+s. + # If its content has +SvcParam+s with the duplicate key, + # the one appears last takes precedence. + + def initialize(params = []) + @params = {} + + params.each do |param| + add param + end + end + + ## + # Get SvcParam for the given +key+ in this list. + + def [](key) + @params[canonical_key(key)] + end + + ## + # Get the number of SvcParams in this list. + + def count + @params.count + end + + ## + # Get whether this list is empty. + + def empty? + @params.empty? + end + + ## + # Add the SvcParam +param+ to this list, overwriting the existing one with the same key. + + def add(param) + @params[param.class.key_number] = param + end + + ## + # Remove the +SvcParam+ with the given +key+ and return it. + + def delete(key) + @params.delete(canonical_key(key)) + end + + ## + # Enumerate the +SvcParam+s in this list. + + def each(&block) + return enum_for(:each) unless block + @params.each_value(&block) + end + + def encode(msg) # :nodoc: + @params.keys.sort.each do |key| + msg.put_pack('n', key) + msg.put_length16 do + @params.fetch(key).encode(msg) + end + end + end + + def self.decode(msg) # :nodoc: + params = msg.get_list do + key, = msg.get_unpack('n') + msg.get_length16 do + SvcParam::ClassHash[key].decode(msg) + end + end + + return self.new(params) + end + + private + + def canonical_key(key) # :nodoc: + case key + when Integer + key + when /\Akey(\d+)\z/ + Integer($1) + when Symbol + SvcParam::ClassHash[key].key_number + else + raise TypeError, 'key must be either String or Symbol' + end + end + end + + + ## + # Base class for SvcParam. [RFC9460] + + class SvcParam + + ## + # Get the presentation name of the SvcParamKey. + + def self.key_name + const_get(:KeyName) + end + + ## + # Get the registered number of the SvcParamKey. + + def self.key_number + const_get(:KeyNumber) + end + + ClassHash = Hash.new do |h, key| # :nodoc: + case key + when Integer + Generic.create(key) + when /\Akey(?\d+)\z/ + Generic.create(key.to_int) + when Symbol + raise KeyError, "unknown key #{key}" + else + raise TypeError, 'key must be either String or Symbol' + end + end + + ## + # Generic SvcParam abstract class. + + class Generic < SvcParam + + ## + # SvcParamValue in wire-format byte string. + + attr_reader :value + + ## + # Create generic SvcParam + + def initialize(value) + @value = value + end + + def encode(msg) # :nodoc: + msg.put_bytes(@value) + end + + def self.decode(msg) # :nodoc: + return self.new(msg.get_bytes) + end + + def self.create(key_number) + c = Class.new(Generic) + key_name = :"key#{key_number}" + c.const_set(:KeyName, key_name) + c.const_set(:KeyNumber, key_number) + self.const_set(:"Key#{key_number}", c) + ClassHash[key_name] = ClassHash[key_number] = c + return c + end + end + + ## + # "mandatory" SvcParam -- Mandatory keys in service binding RR + + class Mandatory < SvcParam + KeyName = :mandatory + KeyNumber = 0 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # Mandatory keys. + + attr_reader :keys + + ## + # Initialize "mandatory" ScvParam. + + def initialize(keys) + @keys = keys.map(&:to_int) + end + + def encode(msg) # :nodoc: + @keys.sort.each do |key| + msg.put_pack('n', key) + end + end + + def self.decode(msg) # :nodoc: + keys = msg.get_list { msg.get_unpack('n')[0] } + return self.new(keys) + end + end + + ## + # "alpn" SvcParam -- Additional supported protocols + + class ALPN < SvcParam + KeyName = :alpn + KeyNumber = 1 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # Supported protocol IDs. + + attr_reader :protocol_ids + + ## + # Initialize "alpn" ScvParam. + + def initialize(protocol_ids) + @protocol_ids = protocol_ids.map(&:to_str) + end + + def encode(msg) # :nodoc: + msg.put_string_list(@protocol_ids) + end + + def self.decode(msg) # :nodoc: + return self.new(msg.get_string_list) + end + end + + ## + # "no-default-alpn" SvcParam -- No support for default protocol + + class NoDefaultALPN < SvcParam + KeyName = :'no-default-alpn' + KeyNumber = 2 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + def encode(msg) # :nodoc: + # no payload + end + + def self.decode(msg) # :nodoc: + return self.new + end + end + + ## + # "port" SvcParam -- Port for alternative endpoint + + class Port < SvcParam + KeyName = :port + KeyNumber = 3 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # Port number. + + attr_reader :port + + ## + # Initialize "port" ScvParam. + + def initialize(port) + @port = port.to_int + end + + def encode(msg) # :nodoc: + msg.put_pack('n', @port) + end + + def self.decode(msg) # :nodoc: + port, = msg.get_unpack('n') + return self.new(port) + end + end + + ## + # "ipv4hint" SvcParam -- IPv4 address hints + + class IPv4Hint < SvcParam + KeyName = :ipv4hint + KeyNumber = 4 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # Set of IPv4 addresses. + + attr_reader :addresses + + ## + # Initialize "ipv4hint" ScvParam. + + def initialize(addresses) + @addresses = addresses.map {|address| IPv4.create(address) } + end + + def encode(msg) # :nodoc: + @addresses.each do |address| + msg.put_bytes(address.address) + end + end + + def self.decode(msg) # :nodoc: + addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) } + return self.new(addresses) + end + end + + ## + # "ipv6hint" SvcParam -- IPv6 address hints + + class IPv6Hint < SvcParam + KeyName = :ipv6hint + KeyNumber = 6 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # Set of IPv6 addresses. + + attr_reader :addresses + + ## + # Initialize "ipv6hint" ScvParam. + + def initialize(addresses) + @addresses = addresses.map {|address| IPv6.create(address) } + end + + def encode(msg) # :nodoc: + @addresses.each do |address| + msg.put_bytes(address.address) + end + end + + def self.decode(msg) # :nodoc: + addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) } + return self.new(addresses) + end + end + + ## + # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461] + + class DoHPath < SvcParam + KeyName = :dohpath + KeyNumber = 7 + ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc: + + ## + # URI template for DoH queries. + + attr_reader :template + + ## + # Initialize "dohpath" ScvParam. + + def initialize(template) + @template = template.encode('utf-8') + end + + def encode(msg) # :nodoc: + msg.put_bytes(@template) + end + + def self.decode(msg) # :nodoc: + template = msg.get_bytes.force_encoding('utf-8') + return self.new(template) + end + end + end + ## # A DNS query abstract class. @@ -2328,7 +2731,7 @@ class Gem::Resolv msg.put_pack("n", @priority) msg.put_pack("n", @weight) msg.put_pack("n", @port) - msg.put_name(@target) + msg.put_name(@target, compress: false) end def self.decode_rdata(msg) # :nodoc: @@ -2339,6 +2742,84 @@ class Gem::Resolv return self.new(priority, weight, port, target) end end + + ## + # Common implementation for SVCB-compatible resource records. + + class ServiceBinding + + ## + # Create a service binding resource record. + + def initialize(priority, target, params = []) + @priority = priority.to_int + @target = Name.create(target) + @params = SvcParams.new(params) + end + + ## + # The priority of this target host. + # + # The range is 0-65535. + # If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode. + + attr_reader :priority + + ## + # The domain name of the target host. + + attr_reader :target + + ## + # The service paramters for the target host. + + attr_reader :params + + ## + # Whether this RR is in AliasMode. + + def alias_mode? + self.priority == 0 + end + + ## + # Whether this RR is in ServiceMode. + + def service_mode? + !alias_mode? + end + + def encode_rdata(msg) # :nodoc: + msg.put_pack("n", @priority) + msg.put_name(@target, compress: false) + @params.encode(msg) + end + + def self.decode_rdata(msg) # :nodoc: + priority, = msg.get_unpack("n") + target = msg.get_name + params = SvcParams.decode(msg) + return self.new(priority, target, params) + end + end + + ## + # SVCB resource record [RFC9460] + + class SVCB < ServiceBinding + TypeValue = 64 + ClassValue = IN::ClassValue + ClassHash[[TypeValue, ClassValue]] = self # :nodoc: + end + + ## + # HTTPS resource record [RFC9460] + + class HTTPS < ServiceBinding + TypeValue = 65 + ClassValue = IN::ClassValue + ClassHash[[TypeValue, ClassValue]] = self # :nodoc: + end end end end @@ -2558,11 +3039,7 @@ class Gem::Resolv attr_reader :address def to_s # :nodoc: - address = sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")) - unless address.sub!(/(^|:)0(:0)+(:|$)/, '::') - address.sub!(/(^|:)0(:|$)/, '::') - end - return address + sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::') end def inspect # :nodoc: diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb index f825f14257..05f3683d38 100644 --- a/lib/rubygems/tsort/lib/tsort.rb +++ b/lib/rubygems/tsort/lib/tsort.rb @@ -122,6 +122,9 @@ # module Gem::TSort + + VERSION = "0.2.0" + class Cyclic < StandardError end