class Mongo::URI::SRVProtocol
Parser for a URI using the mongodb+srv protocol, which specifies a DNS to query for SRV records. The driver will query the DNS server for SRV records on {hostname}.{domainname}, prefixed with _mongodb._tcp The SRV records can then be used as the seedlist for a Mongo::Client. The driver also queries for a TXT record providing default connection string options. Only one TXT record is allowed, and only a subset of Mongo::Client options is allowed.
Please refer to the Initial DNS Seedlist Discovery spec for details.
github.com/mongodb/specifications/blob/master/source/initial-dns-seedlist-discovery
@example Use the uri string to make a client connection.
client = Mongo::Client.new('mongodb+srv://test6.test.build.10gen.cc/')
@since 2.5.0
Constants
- DOT_PARTITION
- FORMAT
- INVALID_DOMAIN
- INVALID_HOST
- INVALID_PORT
- INVALID_TXT_RECORD_OPTION
- MISMATCHED_DOMAINNAME
- MORE_THAN_ONE_TXT_RECORD_FOUND
- NO_SRV_RECORDS
- RECORD_PREFIX
- VALID_TXT_OPTIONS
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 txt record options, 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.5.0
# File lib/mongo/uri/srv_protocol.rb, line 48 def client_options opts = @txt_options.merge(ssl: true) opts = opts.merge(uri_options).merge(:database => database) @user ? opts.merge(credentials) : opts end
Private Instance Methods
# File lib/mongo/uri/srv_protocol.rb, line 114 def get_records(hostname) query_name = RECORD_PREFIX + hostname records = resolver.getresources(query_name, Resolv::DNS::Resource::IN::SRV).collect do |record| record_host = record.target.to_s port = record.port validate_record!(record_host, hostname) "#{record_host}#{HOST_PORT_DELIM}#{port}" end raise Error::NoSRVRecords.new(NO_SRV_RECORDS % hostname) if records.empty? records end
# File lib/mongo/uri/srv_protocol.rb, line 134 def get_txt_opts(host) records = resolver.getresources(host, Resolv::DNS::Resource::IN::TXT) unless records.empty? if records.size > 1 raise Error::InvalidTXTRecord.new(MORE_THAN_ONE_TXT_RECORD_FOUND % host) end options_string = records[0].strings.join parse_txt_options!(options_string) end end
# File lib/mongo/uri/srv_protocol.rb, line 96 def parse_creds_hosts!(string) hostname, creds = split_creds_hosts(string) validate_hostname!(hostname) records = get_records(hostname) @txt_options = get_txt_opts(hostname) || {} @servers = parse_servers!(records.join(',')) @user = parse_user!(creds) @password = parse_password!(creds) end
# File lib/mongo/uri/srv_protocol.rb, line 145 def parse_txt_options!(string) return {} unless string string.split(INDIV_URI_OPTS_DELIM).reduce({}) do |txt_options, opt| raise Error::InvalidTXTRecord.new(INVALID_OPTS_VALUE_DELIM) unless opt.index(URI_OPTS_VALUE_DELIM) key, value = opt.split(URI_OPTS_VALUE_DELIM) raise Error::InvalidTXTRecord.new(INVALID_TXT_RECORD_OPTION) unless VALID_TXT_OPTIONS.include?(key.downcase) strategy = URI_OPTION_MAP[key.downcase] add_uri_option(strategy, value, txt_options) txt_options end end
# File lib/mongo/uri/srv_protocol.rb, line 88 def raise_invalid_error!(details) raise Error::InvalidURI.new(@string, details, FORMAT) end
# File lib/mongo/uri/srv_protocol.rb, line 92 def resolver @resolver ||= Resolv::DNS.new end
# File lib/mongo/uri/srv_protocol.rb, line 84 def scheme MONGODB_SRV_SCHEME end
# File lib/mongo/uri/srv_protocol.rb, line 106 def validate_hostname!(hostname) raise_invalid_error!(INVALID_HOST) if hostname.empty? raise_invalid_error!(INVALID_HOST) if hostname.include?(HOST_DELIM) raise_invalid_error!(INVALID_PORT) if hostname.include?(HOST_PORT_DELIM) _, _, domain = hostname.partition(DOT_PARTITION) raise_invalid_error!(INVALID_DOMAIN) unless domain.include?(DOT_PARTITION) end
# File lib/mongo/uri/srv_protocol.rb, line 126 def validate_record!(record_host, hostname) domainname = hostname.split(DOT_PARTITION)[1..-1] host_parts = record_host.split(DOT_PARTITION) unless (host_parts.size > domainname.size) && (domainname == host_parts[-domainname.length..-1]) raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, domainname]) end end