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 @deprecated

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 @deprecated

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 the URI.

@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

options[R]

The uri parser object options.

@since 2.0.0

servers[R]

The servers specified in the uri.

@since 2.0.0

uri_options[R]

Mongo::Options::Redacted of the options specified in the uri.

@since 2.1.0

Public Class Methods

get(string, opts = {}) click to toggle source

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 221
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 '#{scheme}'. Scheme must be '#{MONGODB_SCHEME}' or '#{MONGODB_SRV_SCHEME}'")
  end
end
new(string, options = {}) click to toggle source

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 266
def initialize(string, options = {})
  @string = string
  @options = options
  parsed_scheme, _, remaining = string.partition(SCHEME_DELIM)
  unless parsed_scheme == scheme
    raise_invalid_error!("Invalid scheme '#{parsed_scheme}'. Scheme must be '#{MONGODB_SCHEME}'. Use URI#get to parse SRV URIs.")
  end
  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

uri_option(uri_key, name, extra = {}) click to toggle source

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 474
def self.uri_option(uri_key, name, extra = {})
  URI_OPTION_MAP[uri_key] = { :name => name }.merge(extra)
end

Public Instance Methods

client_options() click to toggle source

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 [ Mongo::Options::Redacted ] The options passed to the Mongo::Client

@since 2.0.0

# File lib/mongo/uri.rb, line 243
def client_options
  opts = uri_options.tap do |opts|
    opts[:database] = @database if @database
  end

  @user ? opts.merge(credentials) : opts
end
credentials() click to toggle source

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 317
def credentials
  { :user => @user, :password => @password }
end
database() click to toggle source

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 329
def database
  @database ? @database : Database::ADMIN
end
srv_records() click to toggle source
# File lib/mongo/uri.rb, line 251
def srv_records
  nil
end

Private Instance Methods

add_uri_option(key, value, uri_options) click to toggle source

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 597
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
apply_transform(key, value, type) click to toggle source

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 539
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
array(value) click to toggle source

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 845
def array(value)
  value.split(',')
end
auth_mech(value) click to toggle source

Authentication mechanism transformation.

@param value [String] The authentication mechanism.

@return [Symbol] The transformed authentication mechanism.

# File lib/mongo/uri.rb, line 614
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
auth_mech_props(value) click to toggle source

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 652
def auth_mech_props(value)
  properties = hash_extractor('authMechanismProperties', value)
  if properties[:canonicalize_host_name]
    properties.merge!(canonicalize_host_name:
      properties[:canonicalize_host_name].downcase == 'true')
  end
  properties
end
convert_bool(name, value) click to toggle source

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 747
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
convert_integer(name, value) click to toggle source

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 699
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
convert_inverse_bool(name, value) click to toggle source

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 765
def convert_inverse_bool(name, value)
  b = convert_bool(name, value)

  if b.nil?
    nil
  else
    !b
  end
end
convert_ms(name, value) click to toggle source

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 809
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
convert_repeated_bool(name, value) click to toggle source

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.
# File lib/mongo/uri.rb, line 687
def convert_repeated_bool(name, value)
  [convert_bool(name, value)]
end
convert_symbol(name, value) click to toggle source

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.

# File lib/mongo/uri.rb, line 714
def convert_symbol(name, value)
  value.to_sym
end
convert_w(name, value) click to toggle source

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 728
def convert_w(name, value)
  case value
  when 'majority'
    :majority
  when /\A[0-9]+\z/
    value.to_i
  else
    value
  end
end
decode(value) click to toggle source
# File lib/mongo/uri.rb, line 456
def decode(value)
  ::URI.decode(value)
end
encode(value) click to toggle source
# File lib/mongo/uri.rb, line 460
def encode(value)
  ::URI.encode(value)
end
extract_db_opts!(string) click to toggle source
# File lib/mongo/uri.rb, line 387
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
hash_extractor(name, value) click to toggle source

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 828
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(k.downcase.to_sym => v)
  end
end
max_staleness(value) click to toggle source

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 781
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
merge_uri_option(target, value, name) click to toggle source

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 576
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
parse!(remaining) click to toggle source
# File lib/mongo/uri.rb, line 339
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

  unless hosts.length > 0
    raise_invalid_error!("Missing host; at least one must be provided")
  end

  @servers = hosts.split(',').map do |host|
    if host.empty?
      raise_invalid_error!('Empty host given in the host list')
    end
    decode(host).tap do |host|
      validate_address_str!(host)
    end
  end

  @user = parse_user!(creds)
  @password = parse_password!(creds)
  @uri_options = Options::Redacted.new(parse_uri_options!(options))
  if db
    @database = parse_database!(db)
  end
rescue Error::InvalidAddress => e
  raise_invalid_error!(e.message)
end
parse_database!(string) click to toggle source
# File lib/mongo/uri.rb, line 443
def parse_database!(string)
  raise_invalid_error!(UNESCAPED_DATABASE) if string =~ UNSAFE
  decode(string) if string.length > 0
end
parse_password!(string) click to toggle source
# File lib/mongo/uri.rb, line 430
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
parse_uri_options!(string) click to toggle source
# File lib/mongo/uri.rb, line 396
def parse_uri_options!(string)
  uri_options = {}
  unless string
    return uri_options
  end
  string.split('&').each do |option_str|
    if option_str.empty?
      next
    end
    key, value = option_str.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 = decode(key)
    value = decode(value)
    add_uri_option(key, value, uri_options)
  end
  uri_options
end
parse_user!(string) click to toggle source
# File lib/mongo/uri.rb, line 419
def parse_user!(string)
  if (string && user = string.partition(AUTH_USER_PWD_DELIM)[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
raise_invalid_error!(details) click to toggle source
# File lib/mongo/uri.rb, line 448
def raise_invalid_error!(details)
  raise Error::InvalidURI.new(@string, details, FORMAT)
end
raise_invalid_error_no_fmt!(details) click to toggle source
# File lib/mongo/uri.rb, line 452
def raise_invalid_error_no_fmt!(details)
  raise Error::InvalidURI.new(@string, details)
end
read_mode(value) click to toggle source

Read preference mode transformation.

@param value [String] The read mode string value.

@return [Symbol] The read mode symbol.

# File lib/mongo/uri.rb, line 625
def read_mode(value)
  READ_MODE_MAP[value.downcase] || value
end
read_set(value) click to toggle source

Read preference tag set extractor.

@param value [String] The tag set string.

@return [Hash] The tag set hash.

# File lib/mongo/uri.rb, line 643
def read_set(value)
  hash_extractor('readPreferenceTags', value)
end
read_tags(value) click to toggle source

Read preference tags transformation.

@param value [String] The string representing tag set.

@return [Array<Hash>] Array with tag set.

# File lib/mongo/uri.rb, line 634
def read_tags(value)
  [read_set(value)]
end
scheme() click to toggle source
# File lib/mongo/uri.rb, line 335
def scheme
  MONGODB_SCHEME
end
select_target(uri_options, group = nil) click to toggle source

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 557
def select_target(uri_options, group = nil)
  if group
    uri_options[group] ||= {}
  else
    uri_options
  end
end
zlib_compression_level(value) click to toggle source

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 667
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