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
@deprecated
- 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 46 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 136 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 156 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
Mongo::URI#parse!
# File lib/mongo/uri/srv_protocol.rb, line 95 def parse!(remaining) super if @servers.length != 1 raise_invalid_error!(INVALID_HOST) end hostname = @servers.first validate_hostname(hostname) records = get_records(hostname) @txt_options = get_txt_opts(hostname) || {} @servers = parse_servers!(records.join(',')) end
# File lib/mongo/uri/srv_protocol.rb, line 167 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) add_uri_option(key, value, txt_options) txt_options end end
# File lib/mongo/uri/srv_protocol.rb, line 87 def raise_invalid_error!(details) raise Error::InvalidURI.new(@string, details, FORMAT) end
# File lib/mongo/uri/srv_protocol.rb, line 91 def resolver @resolver ||= Resolv::DNS.new end
# File lib/mongo/uri/srv_protocol.rb, line 83 def scheme MONGODB_SRV_SCHEME end
Validates the hostname used in an SRV URI
.
The hostname cannot include a port.
The hostname must not begin with a dot, end with a dot, or have consecutive dots. The hostname must have a minimum of 3 total components (foo.bar.tld).
Raises Error::InvalidURI
if validation fails.
# File lib/mongo/uri/srv_protocol.rb, line 118 def validate_hostname(hostname) raise_invalid_error!(INVALID_PORT) if hostname.include?(HOST_PORT_DELIM) if hostname.start_with?('.') raise_invalid_error!("Hostname cannot start with a dot: #{hostname}") end if hostname.end_with?('.') raise_invalid_error!("Hostname cannot end with a dot: #{hostname}") end parts = hostname.split('.') if parts.any?(&:empty?) raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}") end if parts.length < 3 raise_invalid_error!("Hostname must have a minimum of 3 components (foo.bar.tld): #{hostname}") end end
# File lib/mongo/uri/srv_protocol.rb, line 148 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