class Mongo::URI
The URI
class provides a way for users to parse the MongoDB uri as defined in the connection string format spec.
docs.mongodb.org/manual/reference/connection-string/
@example Use the uri string to make a client connection.
uri = Mongo::URI.new('mongodb://localhost:27017') client = Mongo::Client.new(uri.servers, uri.options) client.login(uri.credentials) client[uri.database]
@since 2.0.0
Constants
- AUTH_DELIM
The character delimiting auth credentials.
@since 2.1.0
- AUTH_MECH_MAP
Map of
URI
authentication mechanisms to Ruby driver mechanisms@since 2.0.0
- AUTH_USER_PWD_DELIM
The character separating a username from the password.
@since 2.1.0
- DATABASE_DELIM
The character delimiting a database.
@since 2.1.0
- FORMAT
MongoDB
URI
format specification.@since 2.0.0
- HELP
MongoDB
URI
(connection string) documentation url@since 2.0.0
- HOST_DELIM
The character delimiting hosts.
@since 2.1.0
- HOST_PORT_DELIM
The character separating a host and port.
@since 2.1.0
- INDIV_URI_OPTS_DELIM
The character delimiting multiple options.
@since 2.1.0
- INVALID_HOST
Error
details for a missing host.@since 2.1.0
- INVALID_OPTS_DELIM
Error
details for providing options without a database delimiter.@since 2.1.0
- INVALID_OPTS_VALUE_DELIM
Error
details for an invalid options format.@since 2.1.0
- INVALID_PORT
Error
details for an invalid port.@since 2.1.0
- INVALID_SCHEME
Error
details for an invalid scheme.@since 2.1.0
- MONGODB_SCHEME
The mongodb connection string scheme root.
@since 2.5.0
- MONGODB_SRV_SCHEME
The mongodb srv protocol connection string scheme root.
@since 2.5.0
- PERCENT_CHAR
Percent sign that must be encoded in user creds.
@since 2.5.1
- READ_MODE_MAP
Map of
URI
read preference modes to Ruby driver read preference modes@since 2.0.0
- REPEATABLE_OPTIONS
Options
that are allowed to appear more than once in the uri.In order to follow the
URI
options spec requirement that all instances of 'tls' and 'ssl' have the same value, we need to keep track of all of the values passed in for those options. Assuming they don't conflict, they will be condensed to a single value immediately after parsing theURI
.@since 2.1.0
- SCHEME
The mongodb connection string scheme.
@deprecated Will be removed in 3.0.
@since 2.0.0
- SCHEME_DELIM
Scheme delimiter.
@since 2.5.0
- UNESCAPED_DATABASE
Error
details for a non-urlencoded auth database name.@since 2.1.0
- UNESCAPED_UNIX_SOCKET
Error
details for a non-urlencoded unix socket path.@since 2.1.0
- UNESCAPED_USER_PWD
Error
details for an non-urlencoded user name or password.@since 2.1.0
- UNIX_SOCKET
Unix socket suffix.
@since 2.1.0
- UNSAFE
Unsafe characters that must be urlencoded.
@since 2.1.0
- URI_OPTION_MAP
Hash for storing map of
URI
option parameters to conversion strategies- URI_OPTS_DELIM
The character delimiting options.
@since 2.1.0
- URI_OPTS_VALUE_DELIM
The character delimiting an option and its value.
@since 2.1.0
Attributes
The uri parser object options.
@since 2.0.0
The servers specified in the uri.
@since 2.0.0
The options specified in the uri.
@since 2.1.0
Public Class Methods
Get either a URI
object or a SRVProtocol
URI
object.
@example Get the uri object.
URI.get(string)
@return [URI, URI::SRVProtocol] The uri object.
@since 2.5.0
# File lib/mongo/uri.rb, line 218 def self.get(string, opts = {}) scheme, _, remaining = string.partition(SCHEME_DELIM) case scheme when MONGODB_SCHEME URI.new(string, opts) when MONGODB_SRV_SCHEME SRVProtocol.new(string, opts) else raise Error::InvalidURI.new(string, INVALID_SCHEME) end end
Create the new uri from the provided string.
@example Create the new URI
.
URI.new('mongodb://localhost:27017')
@param [ String ] string The uri string. @param [ Hash ] options The options.
@raise [ Error::InvalidURI
] If the uri does not match the spec.
@since 2.0.0
# File lib/mongo/uri.rb, line 256 def initialize(string, options = {}) @string = string @options = options parsed_scheme, _, remaining = string.partition(SCHEME_DELIM) raise_invalid_error!(INVALID_SCHEME) unless parsed_scheme == scheme if remaining.empty? raise_invalid_error!('No hosts in the URI') end parse!(remaining) # 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? unless @uri_options[:ssl].uniq.length == 1 raise_invalid_error_no_fmt!("all instances of 'tls' and 'ssl' must have the same value") end @uri_options[:ssl] = @uri_options[:ssl].first end # Check for conflicting TLS insecure options. unless @uri_options[:ssl_verify].nil? unless @uri_options[:ssl_verify_certificate].nil? raise_invalid_error_no_fmt!("'tlsInsecure' and 'tlsAllowInvalidCertificates' cannot both be specified") end unless @uri_options[:ssl_verify_hostname].nil? raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsAllowInvalidHostnames' 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 end
Private Class Methods
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.
# File lib/mongo/uri.rb, line 470 def self.uri_option(uri_key, name, extra = {}) URI_OPTION_MAP[uri_key] = { :name => name }.merge(extra) end
Public Instance Methods
Gets the options hash that needs to be passed to a Mongo::Client
on instantiation, so we don't have to merge the credentials and database in at that point - we only have a single point here.
@example Get the client options.
uri.client_options
@return [ Hash ] The options passed to the Mongo::Client
@since 2.0.0
# File lib/mongo/uri.rb, line 240 def client_options opts = uri_options.merge(:database => database) @user ? opts.merge(credentials) : opts end
Get the credentials provided in the URI
.
@example Get the credentials.
uri.credentials
@return [ Hash ] The credentials.
* :user [ String ] The user. * :password [ String ] The provided password.
@since 2.0.0
# File lib/mongo/uri.rb, line 305 def credentials { :user => @user, :password => @password } end
Get the database provided in the URI
.
@example Get the database.
uri.database
@return [String] The database.
@since 2.0.0
# File lib/mongo/uri.rb, line 317 def database @database ? @database : Database::ADMIN end
Private Instance Methods
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.
# File lib/mongo/uri.rb, line 593 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
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.
# File lib/mongo/uri.rb, line 535 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
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.
# File lib/mongo/uri.rb, line 1044 def array(value) value.split(',') end
Authentication mechanism transformation.
@param value [String] The authentication mechanism.
@return [Symbol] The transformed authentication mechanism.
# File lib/mongo/uri.rb, line 629 def auth_mech(value) AUTH_MECH_MAP[value.upcase].tap do |mech| log_warn("#{value} is not a valid auth mechanism") unless mech end end
Auth
mechanism properties extractor.
@param value [ String ] The auth mechanism properties string.
@return [ Hash ] The auth mechanism properties hash.
# File lib/mongo/uri.rb, line 667 def auth_mech_props(value) properties = hash_extractor('authMechanismProperties', value) if properties[:canonicalize_host_name] properties.merge!(canonicalize_host_name: %w(true TRUE).include?(properties[:canonicalize_host_name])) end properties end
Auth
source transformation, either db string or :external.
@param value [String] Authentication source.
@return [String] If auth source is database name. @return [:external] If auth source is external authentication.
# File lib/mongo/uri.rb, line 620 def auth_source(value) value == '$external' ? :external : decode(value) end
Parses the connectTimeoutMS value.
@param value [ String ] The connectTimeoutMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 920 def connect_timeout(value) ms_convert('connectTimeoutMS', value) 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.
# File lib/mongo/uri.rb, line 865 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
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.
# File lib/mongo/uri.rb, line 817 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
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.
# File lib/mongo/uri.rb, line 846 def convert_w(name, value) case value when 'majority' :majority when /\A[0-9]+\z/ value.to_i else value end end
# File lib/mongo/uri.rb, line 452 def decode(value) ::URI.decode(value) end
# File lib/mongo/uri.rb, line 456 def encode(value) ::URI.encode(value) end
# File lib/mongo/uri.rb, line 361 def extract_db_opts!(string) db_opts, _, creds_hosts = string.reverse.partition(DATABASE_DELIM) db_opts, creds_hosts = creds_hosts, db_opts if creds_hosts.empty? if db_opts.empty? && creds_hosts.include?(URI_OPTS_DELIM) raise_invalid_error!(INVALID_OPTS_DELIM) end [ creds_hosts, db_opts ].map { |s| s.reverse } 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.
# File lib/mongo/uri.rb, line 1027 def hash_extractor(name, value) value.split(',').reduce({}) do |set, tag| k, v = tag.split(':') if v.nil? log_warn("Invalid hash value for #{name}: #{value}") return nil end set.merge(decode(k).downcase.to_sym => decode(v)) end end
Parses the heartbeatFrequencyMS value.
@param value [ String ] The heartbeatFrequencyMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 940 def heartbeat_frequency(value) ms_convert('heartbeatFrequencyMS', value) 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).
# File lib/mongo/uri.rb, line 883 def inverse_bool(name, value) b = convert_bool(name, value) if b.nil? nil else !b end end
Parses the journal value.
@param value [ String ] The journal value.
@return [ true | false | nil ] The journal value parsed out, otherwise nil (and a warning
will be logged).
# File lib/mongo/uri.rb, line 732 def journal(value) convert_bool('journal', value) end
Parses the localThresholdMS value.
@param value [ String ] The localThresholdMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 930 def local_threshold(value) ms_convert('localThresholdMS', value) end
Parses the maxIdleTimeMS value.
@param value [ String ] The maxIdleTimeMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 950 def max_idle_time(value) ms_convert('maxIdleTimeMS', value) end
Parses the max pool size.
@param value [ String ] The max pool size string.
@return [ Integer | nil ] The min pool size if it is valid, otherwise nil (and a warning will)
be logged.
# File lib/mongo/uri.rb, line 701 def max_pool_size(value) if /\A\d+\z/ =~ value return value.to_i end log_warn("#{value} is not a valid maxPoolSize") nil 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).
# File lib/mongo/uri.rb, line 899 def max_staleness(value) if /\A\d+\z/ =~ value int = value.to_i if int >= 0 && int < 90 log_warn("max staleness must be either 0 or greater than 90: #{value}") end return int end log_warn("Invalid max staleness value: #{value}") nil 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.
# File lib/mongo/uri.rb, line 572 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
Parses the min pool size.
@param value [ String ] The min pool size string.
@return [ Integer | nil ] The min pool size if it is valid, otherwise nil (and a warning will
be logged).
# File lib/mongo/uri.rb, line 717 def min_pool_size(value) if /\A\d+\z/ =~ value return value.to_i end log_warn("#{value} is not a valid minPoolSize") 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
# File lib/mongo/uri.rb, line 1008 def ms_convert(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
# File lib/mongo/uri.rb, line 327 def parse!(remaining) hosts_and_db, options = remaining.split('?', 2) if options && options.index('?') raise_invalid_error!("Options contain an unescaped question mark (?), or the database name contains a question mark and was not escaped") end if options && !hosts_and_db.index('/') raise_invalid_error!("MongoDB URI must have a slash (/) after the hosts if options are given") end hosts, db = hosts_and_db.split('/', 2) if db && db.index('/') raise_invalid_error!("Database name contains an unescaped slash (/): #{db}") end if hosts.index('@') creds, hosts = hosts.split('@', 2) if hosts.empty? raise_invalid_error!("Empty hosts list") end if hosts.index('@') raise_invalid_error!("Unescaped @ in auth info") end end @servers = parse_servers!(hosts) @user = parse_user!(creds) @password = parse_password!(creds) @uri_options = Options::Redacted.new(parse_uri_options!(options)) if db @database = parse_database!(db) end end
# File lib/mongo/uri.rb, line 413 def parse_database!(string) raise_invalid_error!(UNESCAPED_DATABASE) if string =~ UNSAFE decode(string) if string.length > 0 end
# File lib/mongo/uri.rb, line 400 def parse_password!(string) if (string && pwd = string.partition(AUTH_USER_PWD_DELIM)[2]) if pwd.length > 0 raise_invalid_error!(UNESCAPED_USER_PWD) if pwd =~ UNSAFE pwd_decoded = decode(pwd) if pwd_decoded =~ PERCENT_CHAR && encode(pwd_decoded) != pwd raise_invalid_error!(UNESCAPED_USER_PWD) end pwd_decoded end end end
# File lib/mongo/uri.rb, line 424 def parse_servers!(string) raise_invalid_error!(INVALID_HOST) unless string.size > 0 string.split(HOST_DELIM).reduce([]) do |servers, host| if host[0] == '[' if host.index(']:') h, p = host.split(']:') validate_port_string!(p) end elsif host.index(HOST_PORT_DELIM) h, _, p = host.partition(HOST_PORT_DELIM) raise_invalid_error!(INVALID_HOST) unless h.size > 0 validate_port_string!(p) elsif host =~ UNIX_SOCKET raise_invalid_error!(UNESCAPED_UNIX_SOCKET) if host =~ UNSAFE host = decode(host) end servers << host end end
# File lib/mongo/uri.rb, line 370 def parse_uri_options!(string) return {} unless string string.split(INDIV_URI_OPTS_DELIM).reduce({}) do |uri_options, opt| key, value = opt.split('=', 2) if value.nil? raise_invalid_error!("Option #{key} has no value") end if value.index('=') raise_invalid_error!("Value for option #{key} contains the key/value delimiter (=): #{value}") end key = ::URI.decode(key) value = ::URI.decode(value) add_uri_option(key, value, uri_options) uri_options end end
# File lib/mongo/uri.rb, line 387 def parse_user!(string) if (string && user = string.partition(AUTH_USER_PWD_DELIM)[0]) if user.length > 0 raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE user_decoded = decode(user) if user_decoded =~ PERCENT_CHAR && encode(user_decoded) != user raise_invalid_error!(UNESCAPED_USER_PWD) end user_decoded end end end
# File lib/mongo/uri.rb, line 444 def raise_invalid_error!(details) raise Error::InvalidURI.new(@string, details, FORMAT) end
# File lib/mongo/uri.rb, line 448 def raise_invalid_error_no_fmt!(details) raise Error::InvalidURI.new(@string, details) end
Read preference mode transformation.
@param value [String] The read mode string value.
@return [Symbol] The read mode symbol.
# File lib/mongo/uri.rb, line 640 def read_mode(value) READ_MODE_MAP[value.downcase] end
Read preference tag set extractor.
@param value [String] The tag set string.
@return [Hash] The tag set hash.
# File lib/mongo/uri.rb, line 658 def read_set(value) hash_extractor('readPreferenceTags', value) end
Parses the retryReads value.
@param value [ String ] The retryReads value.
@return [ true | false | nil ] The boolean value parsed out, otherwise nil (and a warning
will be logged).
# File lib/mongo/uri.rb, line 795 def retry_reads(value) convert_bool('retryReads', value) end
Parses the retryWrites value.
@param value [ String ] The retryWrites value.
@return [ true | false | nil ] The boolean value parsed out, otherwise nil (and a warning
will be logged).
# File lib/mongo/uri.rb, line 805 def retry_writes(value) convert_bool('retryWrites', value) end
# File lib/mongo/uri.rb, line 323 def scheme MONGODB_SCHEME 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.
# File lib/mongo/uri.rb, line 553 def select_target(uri_options, group = nil) if group uri_options[group] ||= {} else uri_options end end
Parses the serverSelectionMS value.
@param value [ String ] The serverSelectionMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 960 def server_selection_timeout(value) ms_convert('serverSelectionTimeoutMS', value) end
Parses the socketTimeoutMS value.
@param value [ String ] The socketTimeoutMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 970 def socket_timeout(value) ms_convert('socketTimeoutMS', value) end
Parses the ssl value from the URI
.
@param value [ String ] The ssl value.
@return [ Array<true | false> ] The ssl value parsed out (stored in an array to facilitate
keeping track of all values).
# File lib/mongo/uri.rb, line 742 def ssl(value) [convert_bool('ssl', value)] end
Parses the ssl_verify
value from the tlsInsecure URI
value. Note that this will be the inverse of the value of tlsInsecure (if present).
@param value [ String ] The tlsInsecure value.
@return [ true | false | nil ] The ssl_verify
value parsed out, otherwise nil (and a warning
will be logged).
# File lib/mongo/uri.rb, line 763 def ssl_verify(value) inverse_bool('tlsAllowInvalidCertificates', value) end
Parses the ssl_verify_certificate
value from the tlsAllowInvalidCertificates URI
value. Note that this will be the inverse of the value of tlsInsecure (if present).
@param value [ String ] The tlsAllowInvalidCertificates value.
@return [ true | false | nil ] The ssl_verify_certificate
value parsed out, otherwise nil
(and a warning will be logged).
# File lib/mongo/uri.rb, line 774 def ssl_verify_certificate(value) inverse_bool('tlsAllowInvalidCertificates', value) end
Parses the ssl_verify_hostname
value from the tlsAllowInvalidHostnames URI
value. Note that this will be the inverse of the value of tlsAllowInvalidHostnames (if present).
@param value [ String ] The tlsAllowInvalidHostnames value.
@return [ true | false | nil ] The ssl_verify_hostname
value parsed out, otherwise nil
(and a warning will be logged).
# File lib/mongo/uri.rb, line 785 def ssl_verify_hostname(value) inverse_bool('tlsAllowInvalidHostnames', value) end
Parses the tls value from the URI
.
@param value [ String ] The tls value.
@return [ Array<true | false> ] The tls value parsed out (stored in an array to facilitate
keeping track of all values).
# File lib/mongo/uri.rb, line 752 def tls(value) [convert_bool('tls', value)] end
# File lib/mongo/uri.rb, line 418 def validate_port_string!(port) unless port.nil? || (port.length > 0 && port.to_i > 0 && port.to_i <= 65535) raise_invalid_error!(INVALID_PORT) end end
Parses the waitQueueTimeoutMS value.
@param value [ String ] The waitQueueTimeoutMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 980 def wait_queue_timeout(value) ms_convert('MS', value) end
Parses the wtimeoutMS value.
@param value [ String ] The wtimeoutMS value.
@return [ Integer | nil ] The integer parsed out, otherwise nil (and a warning will be
logged).
# File lib/mongo/uri.rb, line 990 def wtimeout(value) unless /\A\d+\z/ =~ value log_warn("Invalid wtimeoutMS value: #{value}") return nil end value.to_i 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).
# File lib/mongo/uri.rb, line 682 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