lib/mongo/uri.rb in mongo-2.13.3 vs lib/mongo/uri.rb in mongo-2.14.0.rc1
- old
+ new
@@ -386,10 +386,16 @@
raise_invalid_error!(INVALID_OPTS_DELIM)
end
[ creds_hosts, db_opts ].map { |s| s.reverse }
end
+ def options_mapper
+ @options_mapper ||= OptionsMapper.new(
+ logger: @options[:logger],
+ )
+ end
+
def parse_uri_options!(string)
uri_options = {}
unless string
return uri_options
end
@@ -401,11 +407,11 @@
if value.nil?
raise_invalid_error!("Option #{key} has no value")
end
key = decode(key)
value = decode(value)
- add_uri_option(key, value, uri_options)
+ options_mapper.add_uri_option(key, value, uri_options)
end
uri_options
end
def parse_user!(string)
@@ -451,397 +457,10 @@
def encode(value)
CGI.escape(value).gsub('+', '%20')
end
- # Hash for storing map of URI option parameters to conversion strategies
- URI_OPTION_MAP = {}
-
- # Simple internal dsl to register a MongoDB URI option in the URI_OPTION_MAP.
- #
- # @param uri_key [String] The MongoDB URI option to register.
- # @param name [Symbol] The name of the option in the driver.
- # @param extra [Hash] Extra options.
- # * :group [Symbol] Nested hash where option will go.
- # * :type [Symbol] Name of function to transform value.
- def self.uri_option(uri_key, name, extra = {})
- URI_OPTION_MAP[uri_key] = { :name => name }.merge(extra)
- end
-
- # Replica Set Options
- uri_option 'replicaset', :replica_set
-
- # Timeout Options
- uri_option 'connecttimeoutms', :connect_timeout, :type => :ms
- uri_option 'sockettimeoutms', :socket_timeout, :type => :ms
- uri_option 'serverselectiontimeoutms', :server_selection_timeout, :type => :ms
- uri_option 'localthresholdms', :local_threshold, :type => :ms
- uri_option 'heartbeatfrequencyms', :heartbeat_frequency, :type => :ms
- uri_option 'maxidletimems', :max_idle_time, :type => :ms
-
- # Write Options
- uri_option 'w', :w, :group => :write_concern, type: :w
- uri_option 'journal', :j, :group => :write_concern, :type => :bool
- uri_option 'fsync', :fsync, :group => :write_concern, type: :bool
- uri_option 'wtimeoutms', :wtimeout, :group => :write_concern, :type => :integer
-
- # Read Options
- uri_option 'readpreference', :mode, :group => :read, :type => :read_mode
- uri_option 'readpreferencetags', :tag_sets, :group => :read, :type => :read_tags
- uri_option 'maxstalenessseconds', :max_staleness, :group => :read, :type => :max_staleness
-
- # Pool options
- uri_option 'minpoolsize', :min_pool_size, :type => :integer
- uri_option 'maxpoolsize', :max_pool_size, :type => :integer
- uri_option 'waitqueuetimeoutms', :wait_queue_timeout, :type => :ms
-
- # Security Options
- uri_option 'ssl', :ssl, :type => :repeated_bool
- uri_option 'tls', :ssl, :type => :repeated_bool
- uri_option 'tlsallowinvalidcertificates', :ssl_verify_certificate,
- :type => :inverse_bool
- uri_option 'tlsallowinvalidhostnames', :ssl_verify_hostname,
- :type => :inverse_bool
- uri_option 'tlscafile', :ssl_ca_cert
- uri_option 'tlscertificatekeyfile', :ssl_cert
- uri_option 'tlscertificatekeyfilepassword', :ssl_key_pass_phrase
- uri_option 'tlsinsecure', :ssl_verify, :type => :inverse_bool
-
- # Topology options
- uri_option 'directconnection', :direct_connection, type: :bool
- uri_option 'connect', :connect, type: :symbol
-
- # Auth Options
- uri_option 'authsource', :auth_source
- uri_option 'authmechanism', :auth_mech, :type => :auth_mech
- uri_option 'authmechanismproperties', :auth_mech_properties, :type => :auth_mech_props
-
- # Client Options
- uri_option 'appname', :app_name
- uri_option 'compressors', :compressors, :type => :array
- uri_option 'readconcernlevel', :level, group: :read_concern, type: :symbol
- uri_option 'retryreads', :retry_reads, :type => :bool
- uri_option 'retrywrites', :retry_writes, :type => :bool
- uri_option 'zlibcompressionlevel', :zlib_compression_level, :type => :zlib_compression_level
-
- # Applies URI value transformation by either using the default cast
- # or a transformation appropriate for the given type.
- #
- # @param key [String] URI option name.
- # @param value [String] The value to be transformed.
- # @param type [Symbol] The transform method.
- def apply_transform(key, value, type)
- if type
- if respond_to?("convert_#{type}", true)
- send("convert_#{type}", key, value)
- else
- send(type, value)
- end
- else
- value
- end
- end
-
- # Selects the output destination for an option.
- #
- # @param [Hash] uri_options The base target.
- # @param [Symbol] group Group subtarget.
- #
- # @return [Hash] The target for the option.
- def select_target(uri_options, group = nil)
- if group
- uri_options[group] ||= {}
- else
- uri_options
- end
- end
-
- # Merges a new option into the target.
- #
- # If the option exists at the target destination the merge will
- # be an addition.
- #
- # Specifically required to append an additional tag set
- # to the array of tag sets without overwriting the original.
- #
- # @param target [Hash] The destination.
- # @param value [Object] The value to be merged.
- # @param name [Symbol] The name of the option.
- def merge_uri_option(target, value, name)
- if target.key?(name)
- if REPEATABLE_OPTIONS.include?(name)
- target[name] += value
- else
- log_warn("Repeated option key: #{name}.")
- end
- else
- target.merge!(name => value)
- end
- end
-
- # Adds an option to the uri options hash via the supplied strategy.
- #
- # Acquires a target for the option based on group.
- # Transforms the value.
- # Merges the option into the target.
- #
- # @param key [String] URI option name.
- # @param value [String] The value of the option.
- # @param uri_options [Hash] The base option target.
- def add_uri_option(key, value, uri_options)
- strategy = URI_OPTION_MAP[key.downcase]
- if strategy.nil?
- log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
- return
- end
-
- target = select_target(uri_options, strategy[:group])
- value = apply_transform(key, value, strategy[:type])
- merge_uri_option(target, value, strategy[:name])
- end
-
- # Authentication mechanism transformation.
- #
- # @param value [String] The authentication mechanism.
- #
- # @return [Symbol] The transformed authentication mechanism.
- def auth_mech(value)
- (AUTH_MECH_MAP[value.upcase] || value).tap do |mech|
- log_warn("#{value} is not a valid auth mechanism") unless mech
- end
- end
-
- # Read preference mode transformation.
- #
- # @param value [String] The read mode string value.
- #
- # @return [Symbol] The read mode symbol.
- def read_mode(value)
- READ_MODE_MAP[value.downcase] || value
- end
-
- # Read preference tags transformation.
- #
- # @param value [String] The string representing tag set.
- #
- # @return [Array<Hash>] Array with tag set.
- def read_tags(value)
- [read_set(value)]
- end
-
- # Read preference tag set extractor.
- #
- # @param value [String] The tag set string.
- #
- # @return [Hash] The tag set hash.
- def read_set(value)
- hash_extractor('readPreferenceTags', value)
- end
-
- # Auth mechanism properties extractor.
- #
- # @param value [ String ] The auth mechanism properties string.
- #
- # @return [ Hash ] The auth mechanism properties hash.
- def auth_mech_props(value)
- properties = hash_extractor('authMechanismProperties', value)
- if properties && properties[:canonicalize_host_name]
- properties.merge!(canonicalize_host_name:
- properties[:canonicalize_host_name].downcase == 'true')
- end
- properties
- end
-
- # Parses the zlib compression level.
- #
- # @param value [ String ] The zlib compression level string.
- #
- # @return [ Integer | nil ] The compression level value if it is between -1 and 9 (inclusive),
- # otherwise nil (and a warning will be logged).
- def zlib_compression_level(value)
- if /\A-?\d+\z/ =~ value
- i = value.to_i
-
- if i >= -1 && i <= 9
- return i
- end
- end
-
- log_warn("#{value} is not a valid zlibCompressionLevel")
- nil
- end
-
- # Converts the value into a boolean and returns it wrapped in an array.
- #
- # @param name [ String ] Name of the URI option being processed.
- # @param value [ String ] URI option value.
- #
- # @return [ Array<true | false> ] The boolean value parsed and wraped
- # in an array.
- def convert_repeated_bool(name, value)
- [convert_bool(name, value)]
- end
-
- # Converts +value+ into an integer.
- #
- # If the value is not a valid integer, warns and returns nil.
- #
- # @param name [ String ] Name of the URI option being processed.
- # @param value [ String ] URI option value.
- #
- # @return [ nil | Integer ] Converted value.
- def convert_integer(name, value)
- unless /\A\d+\z/ =~ value
- log_warn("#{value} is not a valid integer for #{name}")
- return nil
- end
-
- value.to_i
- end
-
- # Converts +value+ into a symbol.
- #
- # @param name [ String ] Name of the URI option being processed.
- # @param value [ String ] URI option value.
- #
- # @return [ Symbol ] Converted value.
- def convert_symbol(name, value)
- value.to_sym
- end
-
- # Converts +value+ as a write concern.
- #
- # If +value+ is the word "majority", returns the symbol :majority.
- # If +value+ is a number, returns the number as an integer.
- # Otherwise returns the string +value+ unchanged.
- #
- # @param name [ String ] Name of the URI option being processed.
- # @param value [ String ] URI option value.
- #
- # @return [ Integer | Symbol | String ] Converted value.
- def convert_w(name, value)
- case value
- when 'majority'
- :majority
- when /\A[0-9]+\z/
- value.to_i
- else
- value
- end
- end
-
- # Converts +value+ to a boolean.
- #
- # Returns true for 'true', false for 'false', otherwise nil.
- #
- # @param name [ String ] Name of the URI option being processed.
- # @param value [ String ] URI option value.
- #
- # @return [ true | false | nil ] Converted value.
- def convert_bool(name, value)
- case value
- when "true", 'TRUE'
- true
- when "false", 'FALSE'
- false
- else
- log_warn("invalid boolean option for #{name}: #{value}")
- nil
- end
- end
-
- # Parses a boolean value and returns its inverse.
- #
- # @param value [ String ] The URI option value.
- #
- # @return [ true | false | nil ] The inverse of the boolean value parsed out, otherwise nil
- # (and a warning will be logged).
- def convert_inverse_bool(name, value)
- b = convert_bool(name, value)
-
- if b.nil?
- nil
- else
- !b
- end
- end
-
- # Parses the max staleness value, which must be either "0" or an integer greater or equal to 90.
- #
- # @param value [ String ] The max staleness string.
- #
- # @return [ Integer | nil ] The max staleness integer parsed out if it is valid, otherwise nil
- # (and a warning will be logged).
- def max_staleness(value)
- if /\A-?\d+\z/ =~ value
- int = value.to_i
-
- if int == -1
- int = nil
- end
-
- if int && (int >= 0 && int < 90 || int < 0)
- log_warn("max staleness should be either 0 or greater than 90: #{value}")
- end
-
- return int
- end
-
- log_warn("Invalid max staleness value: #{value}")
- nil
- end
-
- # Ruby's convention is to provide timeouts in seconds, not milliseconds and
- # to use fractions where more precision is necessary. The connection string
- # options are always in MS so we provide an easy conversion type.
- #
- # @param [ Integer ] value The millisecond value.
- #
- # @return [ Float ] The seconds value.
- #
- # @since 2.0.0
- def convert_ms(name, value)
- unless /\A-?\d+(\.\d+)?\z/ =~ value
- log_warn("Invalid ms value for #{name}: #{value}")
- return nil
- end
-
- if value[0] == '-'
- log_warn("#{name} cannot be a negative number")
- return nil
- end
-
- value.to_f / 1000
- end
-
- # Extract values from the string and put them into a nested hash.
- #
- # @param value [ String ] The string to build a hash from.
- #
- # @return [ Hash ] The hash built from the string.
- def hash_extractor(name, value)
- h = {}
- value.split(',').each do |tag|
- k, v = tag.split(':')
- if v.nil?
- log_warn("Invalid hash value for #{name}: key `#{k}` does not have a value: #{value}")
- end
-
- h[k.downcase.to_sym] = v
- end
- h
- end
-
- # Extract values from the string and put them into an array.
- #
- # @param [ String ] value The string to build an array from.
- #
- # @return [ Array ] The array built from the string.
- def array(value)
- value.split(',')
- end
-
def validate_uri_options!
# The URI options spec requires that we raise an error if there are conflicting values of
# 'tls' and 'ssl'. In order to fulfill this, we parse the values of each instance into an
# array; assuming all values in the array are the same, we replace the array with that value.
unless uri_options[:ssl].nil? || uri_options[:ssl].empty?
@@ -859,14 +478,25 @@
end
unless uri_options[:ssl_verify_hostname].nil?
raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsAllowInvalidHostnames' cannot both be specified")
end
+
+ unless uri_options[:ssl_verify_ocsp_endpoint].nil?
+ raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsDisableOCSPEndpointCheck' cannot both be specified")
+ end
end
- # Since we know that the only URI option that sets :ssl_cert is "tlsCertificateKeyFile", any
- # value set for :ssl_cert must also be set for :ssl_key.
+ unless uri_options[:ssl_verify_certificate].nil?
+ unless uri_options[:ssl_verify_ocsp_endpoint].nil?
+ raise_invalid_error_no_fmt!("tlsAllowInvalidCertificates' and 'tlsDisableOCSPEndpointCheck' cannot both be specified")
+ end
+ end
+
+ # Since we know that the only URI option that sets :ssl_cert is
+ # "tlsCertificateKeyFile", any value set for :ssl_cert must also be set
+ # for :ssl_key.
if uri_options[:ssl_cert]
uri_options[:ssl_key] = uri_options[:ssl_cert]
end
if uri_options[:write_concern] && !uri_options[:write_concern].empty?
@@ -889,6 +519,7 @@
end
end
end
end
+require 'mongo/uri/options_mapper'
require 'mongo/uri/srv_protocol'