mongo 2.10.5 → 2.11.0.rc0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CONTRIBUTING.md +1 -1
- data/lib/mongo.rb +2 -0
- data/lib/mongo/address.rb +4 -0
- data/lib/mongo/address/validator.rb +99 -0
- data/lib/mongo/auth.rb +7 -2
- data/lib/mongo/auth/user.rb +1 -7
- data/lib/mongo/background_thread.rb +135 -0
- data/lib/mongo/bulk_write/transformable.rb +3 -3
- data/lib/mongo/client.rb +74 -16
- data/lib/mongo/cluster.rb +193 -41
- data/lib/mongo/cluster/periodic_executor.rb +31 -43
- data/lib/mongo/cluster/sdam_flow.rb +26 -3
- data/lib/mongo/cluster/srv_monitor.rb +127 -0
- data/lib/mongo/collection/view/readable.rb +3 -5
- data/lib/mongo/collection/view/writable.rb +3 -3
- data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
- data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
- data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
- data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/error/auth_error.rb +1 -1
- data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
- data/lib/mongo/error/invalid_address.rb +24 -0
- data/lib/mongo/error/notable.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +3 -3
- data/lib/mongo/error/pool_closed_error.rb +11 -4
- data/lib/mongo/event.rb +1 -1
- data/lib/mongo/grid/file.rb +0 -5
- data/lib/mongo/grid/file/chunk.rb +0 -2
- data/lib/mongo/grid/fs_bucket.rb +13 -15
- data/lib/mongo/grid/stream/write.rb +3 -9
- data/lib/mongo/loggable.rb +5 -1
- data/lib/mongo/monitoring.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
- data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
- data/lib/mongo/operation/shared/executable.rb +5 -10
- data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
- data/lib/mongo/protocol/get_more.rb +1 -2
- data/lib/mongo/protocol/kill_cursors.rb +13 -6
- data/lib/mongo/protocol/serializers.rb +4 -20
- data/lib/mongo/retryable.rb +9 -34
- data/lib/mongo/semaphore.rb +1 -1
- data/lib/mongo/server.rb +113 -42
- data/lib/mongo/server/connection.rb +12 -5
- data/lib/mongo/server/connection_pool.rb +250 -40
- data/lib/mongo/server/connection_pool/populator.rb +58 -0
- data/lib/mongo/server/description.rb +9 -2
- data/lib/mongo/server/monitor.rb +68 -93
- data/lib/mongo/server/monitor/connection.rb +2 -0
- data/lib/mongo/server_selector/selectable.rb +13 -5
- data/lib/mongo/session.rb +0 -13
- data/lib/mongo/srv.rb +17 -0
- data/lib/mongo/srv/monitor.rb +96 -0
- data/lib/mongo/srv/resolver.rb +130 -0
- data/lib/mongo/srv/result.rb +126 -0
- data/lib/mongo/srv/warning_result.rb +35 -0
- data/lib/mongo/uri.rb +45 -55
- data/lib/mongo/uri/srv_protocol.rb +89 -42
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +3 -4
- data/spec/README.md +6 -1
- data/spec/enterprise_auth/kerberos_spec.rb +7 -6
- data/spec/integration/change_stream_examples_spec.rb +0 -4
- data/spec/integration/client_construction_spec.rb +14 -2
- data/spec/integration/connect_single_rs_name_spec.rb +2 -2
- data/spec/integration/connection_pool_populator_spec.rb +296 -0
- data/spec/integration/connection_spec.rb +31 -22
- data/spec/integration/cursor_reaping_spec.rb +1 -2
- data/spec/integration/docs_examples_spec.rb +0 -4
- data/spec/integration/heartbeat_events_spec.rb +17 -15
- data/spec/integration/reconnect_spec.rb +144 -1
- data/spec/integration/retryable_writes_errors_spec.rb +0 -4
- data/spec/integration/retryable_writes_spec.rb +36 -36
- data/spec/integration/sdam_error_handling_spec.rb +31 -25
- data/spec/integration/sdam_events_spec.rb +2 -6
- data/spec/integration/server_monitor_spec.rb +28 -0
- data/spec/integration/server_selector_spec.rb +7 -5
- data/spec/integration/srv_monitoring_spec.rb +360 -0
- data/spec/integration/step_down_spec.rb +4 -6
- data/spec/lite_spec_helper.rb +22 -0
- data/spec/mongo/address/validator_spec.rb +51 -0
- data/spec/mongo/auth/cr_spec.rb +1 -29
- data/spec/mongo/auth/ldap_spec.rb +1 -29
- data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
- data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
- data/spec/mongo/auth/scram_spec.rb +1 -29
- data/spec/mongo/auth/user/view_spec.rb +1 -36
- data/spec/mongo/auth/user_spec.rb +0 -12
- data/spec/mongo/auth/x509_spec.rb +1 -29
- data/spec/mongo/bulk_write_spec.rb +2 -2
- data/spec/mongo/client_construction_spec.rb +56 -15
- data/spec/mongo/client_spec.rb +31 -27
- data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
- data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
- data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
- data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
- data/spec/mongo/cluster/topology/single_spec.rb +20 -11
- data/spec/mongo/cluster_spec.rb +45 -29
- data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
- data/spec/mongo/collection/view/readable_spec.rb +0 -16
- data/spec/mongo/collection_spec.rb +0 -44
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
- data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
- data/spec/mongo/cursor_spec.rb +27 -7
- data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
- data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
- data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
- data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
- data/spec/mongo/retryable_spec.rb +52 -31
- data/spec/mongo/server/app_metadata_spec.rb +0 -8
- data/spec/mongo/server/connection_auth_spec.rb +5 -2
- data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
- data/spec/mongo/server/connection_pool_spec.rb +256 -107
- data/spec/mongo/server/connection_spec.rb +22 -33
- data/spec/mongo/server/description_spec.rb +42 -4
- data/spec/mongo/server/monitor/connection_spec.rb +22 -11
- data/spec/mongo/server/monitor_spec.rb +66 -107
- data/spec/mongo/server_spec.rb +82 -60
- data/spec/mongo/session/session_pool_spec.rb +1 -5
- data/spec/mongo/session_spec.rb +0 -4
- data/spec/mongo/socket/ssl_spec.rb +2 -2
- data/spec/mongo/srv/monitor_spec.rb +211 -0
- data/spec/mongo/srv/result_spec.rb +54 -0
- data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
- data/spec/mongo/uri_spec.rb +125 -4
- data/spec/spec_helper.rb +6 -0
- data/spec/spec_tests/auth_spec.rb +39 -0
- data/spec/spec_tests/cmap_spec.rb +55 -8
- data/spec/spec_tests/connection_string_spec.rb +6 -31
- data/spec/spec_tests/data/auth/connection-string.yml +297 -0
- data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
- data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
- data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
- data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
- data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
- data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
- data/spec/spec_tests/data/transactions/abort.yml +3 -3
- data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
- data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
- data/spec/spec_tests/max_staleness_spec.rb +7 -2
- data/spec/spec_tests/retryable_reads_spec.rb +0 -31
- data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
- data/spec/spec_tests/sdam_spec.rb +4 -7
- data/spec/spec_tests/server_selection_spec.rb +6 -2
- data/spec/spec_tests/transactions_spec.rb +0 -2
- data/spec/spec_tests/uri_options_spec.rb +4 -2
- data/spec/stress/connection_pool_stress_spec.rb +203 -0
- data/spec/stress/connection_pool_timing_spec.rb +181 -0
- data/spec/support/auth.rb +113 -0
- data/spec/support/background_thread_registry.rb +63 -0
- data/spec/support/client_registry.rb +11 -2
- data/spec/support/cluster_config.rb +65 -46
- data/spec/support/cluster_tools.rb +2 -2
- data/spec/support/cmap.rb +13 -14
- data/spec/support/cmap/verifier.rb +4 -5
- data/spec/support/command_monitoring.rb +0 -5
- data/spec/support/common_shortcuts.rb +101 -1
- data/spec/support/constraints.rb +25 -0
- data/spec/support/dns.rb +13 -0
- data/spec/support/event_subscriber.rb +0 -7
- data/spec/support/json_ext_formatter.rb +5 -1
- data/spec/support/lite_constraints.rb +22 -6
- data/spec/support/local_resource_registry.rb +34 -0
- data/spec/support/sdam_monitoring.rb +115 -0
- data/spec/support/spec_config.rb +20 -6
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +1 -1
- data/spec/support/transactions/test.rb +1 -1
- data/spec/support/utils.rb +1 -16
- metadata +685 -659
- metadata.gz.sig +0 -0
- data/lib/mongo/event/description_changed.rb +0 -52
- data/spec/integration/bson_symbol_spec.rb +0 -34
- data/spec/integration/crud_spec.rb +0 -45
- data/spec/integration/get_more_spec.rb +0 -32
- data/spec/integration/grid_fs_bucket_spec.rb +0 -48
- data/spec/integration/retryable_errors_spec.rb +0 -265
- data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
- data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
- data/spec/runners/sdam/verifier.rb +0 -88
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright (C) 2014-2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module SRV
|
17
|
+
|
18
|
+
# Polls SRV records for the URI that a cluster was created for and
|
19
|
+
# updates the list of servers in the cluster when records change.
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
class Monitor
|
23
|
+
include Loggable
|
24
|
+
|
25
|
+
MIN_RESCAN_FREQUENCY = 60
|
26
|
+
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
def initialize(cluster, resolver, srv_records, options = nil)
|
30
|
+
@options = options || {}
|
31
|
+
@cluster = cluster
|
32
|
+
@resolver = resolver
|
33
|
+
@records = srv_records
|
34
|
+
@no_records_found = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_monitor!
|
38
|
+
@thread = Thread.new do
|
39
|
+
loop do
|
40
|
+
sleep(rescan_frequency)
|
41
|
+
scan!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@thread))
|
46
|
+
end
|
47
|
+
|
48
|
+
def scan!
|
49
|
+
@old_hosts = @records.hosts
|
50
|
+
|
51
|
+
begin
|
52
|
+
@records = @resolver.get_records(@records.hostname)
|
53
|
+
rescue Resolv::ResolvTimeout => e
|
54
|
+
log_warn("Timed out trying to resolve hostname #{@records.hostname}")
|
55
|
+
return
|
56
|
+
rescue Resolv::ResolvError => e
|
57
|
+
log_warn("Unable to resolve hostname #{@records.hostname}")
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
if @records.empty?
|
62
|
+
@no_records_found = true
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
@no_records_found = false
|
67
|
+
|
68
|
+
(@old_hosts - @records.hosts).each do |host|
|
69
|
+
@cluster.remove(host)
|
70
|
+
end
|
71
|
+
|
72
|
+
(@records.hosts - @old_hosts).each do |host|
|
73
|
+
@cluster.add(host)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.finalize(thread)
|
78
|
+
Proc.new do
|
79
|
+
thread.kill
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def rescan_frequency
|
86
|
+
if @no_records_found
|
87
|
+
Server:: Monitor::HEARTBEAT_FREQUENCY
|
88
|
+
elsif @records.min_ttl.nil?
|
89
|
+
MIN_RESCAN_FREQUENCY
|
90
|
+
else
|
91
|
+
[@records.min_ttl, MIN_RESCAN_FREQUENCY].max
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Copyright (C) 2017-2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the 'License');
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an 'AS IS' BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module Srv
|
17
|
+
|
18
|
+
# Encapsulates the necessary behavior for querying SRV records as
|
19
|
+
# required by the driver.
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
class Resolver
|
23
|
+
include Loggable
|
24
|
+
|
25
|
+
# @return [ String ] RECORD_PREFIX The prefix prepended to each hostname
|
26
|
+
# before querying SRV records.
|
27
|
+
RECORD_PREFIX = '_mongodb._tcp.'.freeze
|
28
|
+
|
29
|
+
# Creates a new Resolver.
|
30
|
+
#
|
31
|
+
# @param [ Hash ] options The options for the resolver.
|
32
|
+
#
|
33
|
+
# @option options [ Boolean ] :raise_on_invalid Whether or not to raise
|
34
|
+
# an exception if either a record with a mismatched domain is found
|
35
|
+
# or if no records are found. Defaults to true.
|
36
|
+
# @option options [ Hash ] :resolv_options For internal driver use only.
|
37
|
+
# Options to pass through to Resolv::DNS constructor for SRV lookups.
|
38
|
+
def initialize(options = nil)
|
39
|
+
@options = if options
|
40
|
+
options.dup
|
41
|
+
else
|
42
|
+
{}
|
43
|
+
end.freeze
|
44
|
+
@resolver = Resolv::DNS.new(@options[:resolv_options])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Obtains all of the SRV records for a given hostname.
|
48
|
+
#
|
49
|
+
# In the event that a record with a mismatched domain is found or no
|
50
|
+
# records are found, if the :raise_on_invalid option is true,
|
51
|
+
# an exception will be raised, otherwise a warning will be logged.
|
52
|
+
#
|
53
|
+
# @param [ String ] hostname The hostname whose records should be obtained.
|
54
|
+
#
|
55
|
+
# @raise [ Mongo::Error::MismatchedDomain ] If the :raise_in_invalid
|
56
|
+
# Resolver option is true and a record with a domain name that does
|
57
|
+
# not match the hostname's is found.
|
58
|
+
# @raise [ Mongo::Error::NoSRVRecords ] If the :raise_in_invalid Resolver
|
59
|
+
# option is true and no records are found.
|
60
|
+
#
|
61
|
+
# @return [ Mongo::Srv::Result ] SRV lookup result.
|
62
|
+
def get_records(hostname)
|
63
|
+
query_name = RECORD_PREFIX + hostname
|
64
|
+
resources = @resolver.getresources(query_name, Resolv::DNS::Resource::IN::SRV)
|
65
|
+
|
66
|
+
# Collect all of the records into a Result object, raising an error
|
67
|
+
# or logging a warning if a record with a mismatched domain is found.
|
68
|
+
# Note that in the case a warning is raised, the record is _not_
|
69
|
+
# added to the Result object.
|
70
|
+
result = Srv::Result.new(hostname)
|
71
|
+
resources.each do |record|
|
72
|
+
begin
|
73
|
+
result.add_record(record)
|
74
|
+
rescue Error::MismatchedDomain => e
|
75
|
+
if raise_on_invalid?
|
76
|
+
raise
|
77
|
+
else
|
78
|
+
log_warn(e.message)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# If no records are found, either raise an error or log a warning
|
84
|
+
# based on the Resolver's :raise_on_invalid option.
|
85
|
+
if result.empty?
|
86
|
+
if raise_on_invalid?
|
87
|
+
raise Error::NoSRVRecords.new(URI::SRVProtocol::NO_SRV_RECORDS % hostname)
|
88
|
+
else
|
89
|
+
log_warn(URI::SRVProtocol::NO_SRV_RECORDS % hostname)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# Obtains the TXT records of a host.
|
97
|
+
#
|
98
|
+
# @param [ String ] hostname The host whose TXT records should be obtained.
|
99
|
+
#
|
100
|
+
# @return [ nil | String ] URI options string from TXT record
|
101
|
+
# associated with the hostname, or nil if there is no such record.
|
102
|
+
#
|
103
|
+
# @raise [ Mongo::Error::InvalidTXTRecord ] If more than one TXT record is found.
|
104
|
+
def get_txt_options_string(hostname)
|
105
|
+
records = @resolver.getresources(hostname, Resolv::DNS::Resource::IN::TXT)
|
106
|
+
if records.empty?
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
if records.length > 1
|
111
|
+
msg = "Only one TXT record is allowed: querying hostname #{hostname} returned #{records.length} records"
|
112
|
+
|
113
|
+
raise Error::InvalidTXTRecord, msg
|
114
|
+
end
|
115
|
+
|
116
|
+
records[0].strings.join
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Checks whether an error should be raised due to either a record with
|
122
|
+
# a mismatched domain being found or no records being found.
|
123
|
+
#
|
124
|
+
# @return [ Boolean ] Whether an error should be raised.
|
125
|
+
def raise_on_invalid?
|
126
|
+
@raise_on_invalid ||= @options[:raise_on_invalid] || true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright (C) 2017-2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the 'License');
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an 'AS IS' BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
|
17
|
+
module Srv
|
18
|
+
|
19
|
+
# SRV record lookup result.
|
20
|
+
#
|
21
|
+
# Contains server addresses that the query resolved to, and minimum TTL
|
22
|
+
# of the DNS records.
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
class Result
|
26
|
+
include Address::Validator
|
27
|
+
|
28
|
+
# @return [ String ] MISMATCHED_DOMAINNAME Error message format string indicating that an SRV
|
29
|
+
# record found does not match the domain of a hostname.
|
30
|
+
MISMATCHED_DOMAINNAME = "Parent domain name in SRV record result (%s) does not match " +
|
31
|
+
"that of the hostname (%s)".freeze
|
32
|
+
|
33
|
+
# @return [ String ] query_hostname The hostname pointing to the DNS records.
|
34
|
+
attr_reader :query_hostname
|
35
|
+
|
36
|
+
# @return [ Array<String> ] address_strs The host strings of the SRV records
|
37
|
+
# for the query hostname.
|
38
|
+
attr_reader :address_strs
|
39
|
+
|
40
|
+
# @return [ Integer | nil ] min_ttl The smallest TTL found among the
|
41
|
+
# records (or nil if no records have been added).
|
42
|
+
attr_accessor :min_ttl
|
43
|
+
|
44
|
+
# Create a new object to keep track of the SRV records of the hostname.
|
45
|
+
#
|
46
|
+
# @param [ String ] hostname The hostname pointing to the DNS records.
|
47
|
+
def initialize(hostname)
|
48
|
+
@query_hostname = hostname
|
49
|
+
@address_strs = []
|
50
|
+
@min_ttl = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks whether there are any records.
|
54
|
+
#
|
55
|
+
# @return [ Boolean ] Whether or not there are any records.
|
56
|
+
def empty?
|
57
|
+
@address_strs.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Adds a new record.
|
61
|
+
#
|
62
|
+
# @param [ Resolv::DNS::Resource ] record An SRV record found for the hostname.
|
63
|
+
def add_record(record)
|
64
|
+
record_host = normalize_hostname(record.target.to_s)
|
65
|
+
port = record.port
|
66
|
+
validate_hostname!(record_host)
|
67
|
+
validate_same_origin!(record_host)
|
68
|
+
address_str = if record_host.index(':')
|
69
|
+
# IPV6 address
|
70
|
+
"[#{record_host}]:#{port}"
|
71
|
+
else
|
72
|
+
"#{record_host}:#{port}"
|
73
|
+
end
|
74
|
+
@address_strs << address_str
|
75
|
+
|
76
|
+
if @min_ttl.nil?
|
77
|
+
@min_ttl = record.ttl
|
78
|
+
else
|
79
|
+
@min_ttl = [@min_ttl, record.ttl].min
|
80
|
+
end
|
81
|
+
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Transforms the provided hostname to simplify its validation later on.
|
88
|
+
#
|
89
|
+
# This method is safe to call during both initial DNS seed list discovery
|
90
|
+
# and during SRV monitoring, in that it does not convert invalid hostnames
|
91
|
+
# into valid ones.
|
92
|
+
#
|
93
|
+
# - Converts the hostname to lower case.
|
94
|
+
# - Removes one trailing dot, if there is exactly one. If the hostname
|
95
|
+
# has multiple trailing dots, it is unchanged.
|
96
|
+
#
|
97
|
+
# @param [ String ] host Hostname to transform.
|
98
|
+
def normalize_hostname(host)
|
99
|
+
host = host.downcase
|
100
|
+
unless host.end_with?('..')
|
101
|
+
host = host.sub(/\.\z/, '')
|
102
|
+
end
|
103
|
+
host
|
104
|
+
end
|
105
|
+
|
106
|
+
# Ensures that a record's domain name matches that of the hostname.
|
107
|
+
#
|
108
|
+
# A hostname's domain name consists of each of the '.' delineated
|
109
|
+
# parts after the first. For example, the hostname 'foo.bar.baz'
|
110
|
+
# has the domain name 'bar.baz'.
|
111
|
+
#
|
112
|
+
# @param [ String ] record_host The host of the SRV record.
|
113
|
+
#
|
114
|
+
# @raise [ Mongo::Error::MismatchedDomain ] If the record's domain name doesn't match that of
|
115
|
+
# the hostname.
|
116
|
+
def validate_same_origin!(record_host)
|
117
|
+
domain_name ||= query_hostname.split('.')[1..-1]
|
118
|
+
host_parts = record_host.split('.')
|
119
|
+
|
120
|
+
unless (host_parts.size > domain_name.size) && (domain_name == host_parts[-domain_name.length..-1])
|
121
|
+
raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, domain_name])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (C) 2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the 'License');
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an 'AS IS' BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
|
17
|
+
module Srv
|
18
|
+
|
19
|
+
# SRV record lookup result which warns on errors rather than raising
|
20
|
+
# exceptions.
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
class WarningResult < Result
|
24
|
+
|
25
|
+
# Adds a new record.
|
26
|
+
#
|
27
|
+
# @param [ Resolv::DNS::Resource ] record An SRV record found for the hostname.
|
28
|
+
def add_record(record)
|
29
|
+
super
|
30
|
+
rescue Error::InvalidAddress, Error::MismatchedDomain => e
|
31
|
+
log_warn(e.message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/mongo/uri.rb
CHANGED
@@ -28,13 +28,14 @@ module Mongo
|
|
28
28
|
# @since 2.0.0
|
29
29
|
class URI
|
30
30
|
include Loggable
|
31
|
+
include Address::Validator
|
31
32
|
|
32
33
|
# The uri parser object options.
|
33
34
|
#
|
34
35
|
# @since 2.0.0
|
35
36
|
attr_reader :options
|
36
37
|
|
37
|
-
#
|
38
|
+
# Mongo::Options::Redacted of the options specified in the uri.
|
38
39
|
#
|
39
40
|
# @since 2.1.0
|
40
41
|
attr_reader :uri_options
|
@@ -64,6 +65,7 @@ module Mongo
|
|
64
65
|
# Error details for an invalid scheme.
|
65
66
|
#
|
66
67
|
# @since 2.1.0
|
68
|
+
# @deprecated
|
67
69
|
INVALID_SCHEME = "Invalid scheme. Scheme must be '#{MONGODB_SCHEME}' or '#{MONGODB_SRV_SCHEME}'".freeze
|
68
70
|
|
69
71
|
# MongoDB URI format specification.
|
@@ -80,7 +82,7 @@ module Mongo
|
|
80
82
|
# Unsafe characters that must be urlencoded.
|
81
83
|
#
|
82
84
|
# @since 2.1.0
|
83
|
-
UNSAFE = /[
|
85
|
+
UNSAFE = /[\:\/\@]/
|
84
86
|
|
85
87
|
# Percent sign that must be encoded in user creds.
|
86
88
|
#
|
@@ -224,7 +226,7 @@ module Mongo
|
|
224
226
|
when MONGODB_SRV_SCHEME
|
225
227
|
SRVProtocol.new(string, opts)
|
226
228
|
else
|
227
|
-
raise Error::InvalidURI.new(string,
|
229
|
+
raise Error::InvalidURI.new(string, "Invalid scheme '#{scheme}'. Scheme must be '#{MONGODB_SCHEME}' or '#{MONGODB_SRV_SCHEME}'")
|
228
230
|
end
|
229
231
|
end
|
230
232
|
|
@@ -235,14 +237,18 @@ module Mongo
|
|
235
237
|
# @example Get the client options.
|
236
238
|
# uri.client_options
|
237
239
|
#
|
238
|
-
# @return [
|
240
|
+
# @return [ Mongo::Options::Redacted ] The options passed to the Mongo::Client
|
239
241
|
#
|
240
242
|
# @since 2.0.0
|
241
243
|
def client_options
|
242
|
-
opts =
|
244
|
+
opts = default_client_options.merge(uri_options)
|
243
245
|
@user ? opts.merge(credentials) : opts
|
244
246
|
end
|
245
247
|
|
248
|
+
def srv_records
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
|
246
252
|
# Create the new uri from the provided string.
|
247
253
|
#
|
248
254
|
# @example Create the new URI.
|
@@ -258,7 +264,9 @@ module Mongo
|
|
258
264
|
@string = string
|
259
265
|
@options = options
|
260
266
|
parsed_scheme, _, remaining = string.partition(SCHEME_DELIM)
|
261
|
-
|
267
|
+
unless parsed_scheme == scheme
|
268
|
+
raise_invalid_error!("Invalid scheme '#{parsed_scheme}'. Scheme must be '#{MONGODB_SCHEME}'. Use URI#get to parse SRV URIs.")
|
269
|
+
end
|
262
270
|
if remaining.empty?
|
263
271
|
raise_invalid_error!('No hosts in the URI')
|
264
272
|
end
|
@@ -359,7 +367,7 @@ module Mongo
|
|
359
367
|
raise_invalid_error!('Empty host given in the host list')
|
360
368
|
end
|
361
369
|
decode(host).tap do |host|
|
362
|
-
|
370
|
+
validate_address_str!(host)
|
363
371
|
end
|
364
372
|
end
|
365
373
|
|
@@ -369,6 +377,8 @@ module Mongo
|
|
369
377
|
if db
|
370
378
|
@database = parse_database!(db)
|
371
379
|
end
|
380
|
+
rescue Error::InvalidAddress => e
|
381
|
+
raise_invalid_error!(e.message)
|
372
382
|
end
|
373
383
|
|
374
384
|
def extract_db_opts!(string)
|
@@ -405,14 +415,12 @@ module Mongo
|
|
405
415
|
|
406
416
|
def parse_user!(string)
|
407
417
|
if (string && user = string.partition(AUTH_USER_PWD_DELIM)[0])
|
408
|
-
if user
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
raise_invalid_error!(UNESCAPED_USER_PWD)
|
413
|
-
end
|
414
|
-
user_decoded
|
418
|
+
raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE
|
419
|
+
user_decoded = decode(user)
|
420
|
+
if user_decoded =~ PERCENT_CHAR && encode(user_decoded) != user
|
421
|
+
raise_invalid_error!(UNESCAPED_USER_PWD)
|
415
422
|
end
|
423
|
+
user_decoded
|
416
424
|
end
|
417
425
|
end
|
418
426
|
|
@@ -434,50 +442,32 @@ module Mongo
|
|
434
442
|
decode(string) if string.length > 0
|
435
443
|
end
|
436
444
|
|
437
|
-
def
|
438
|
-
|
439
|
-
|
445
|
+
def default_client_options
|
446
|
+
opts = Options::Redacted.new(database: database)
|
447
|
+
|
448
|
+
if @uri_options[:auth_mech] || @user
|
449
|
+
opts[:auth_source] = default_auth_source
|
440
450
|
end
|
441
|
-
end
|
442
451
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
case host
|
447
|
-
when /\A\[[\d:]+\](?::(\d+))?\z/
|
448
|
-
# ipv6 with optional port
|
449
|
-
if port_str = $1
|
450
|
-
validate_port_string!(port_str)
|
451
|
-
end
|
452
|
-
when /\A\//, /\.sock\z/
|
453
|
-
# Unix socket path.
|
454
|
-
# Spec requires us to validate that the path has no unescaped
|
455
|
-
# slashes, but if this were to be the case, parsing would have
|
456
|
-
# already failed elsewhere because the URI would've been split in
|
457
|
-
# a weird place.
|
458
|
-
# The spec also allows relative socket paths and requires that
|
459
|
-
# socket paths end in ".sock". We accept all paths but special case
|
460
|
-
# the .sock extension to avoid relative paths falling into the
|
461
|
-
# host:port case below.
|
462
|
-
when /[\/\[\]]/
|
463
|
-
# Not a host:port nor an ipv4 address with optional port.
|
464
|
-
# Possibly botched ipv6 address with e.g. port delimiter present and
|
465
|
-
# port missing, or extra junk before or after.
|
466
|
-
raise_invalid_error!("Invalid hostname: #{host}")
|
467
|
-
when /:.*:/m
|
468
|
-
raise_invalid_error!("Multiple port delimiters are not allowed: #{host}")
|
469
|
-
else
|
470
|
-
# host:port or ipv4 address with optional port number
|
471
|
-
host, port = host.split(':')
|
472
|
-
if host.empty?
|
473
|
-
raise_invalid_error!("Host is empty: #{host}")
|
474
|
-
end
|
452
|
+
if @uri_options[:auth_mech] == :gssapi
|
453
|
+
opts[:auth_mech_properties] = default_auth_mech_properties
|
454
|
+
end
|
475
455
|
|
476
|
-
|
477
|
-
|
478
|
-
|
456
|
+
opts
|
457
|
+
end
|
458
|
+
|
459
|
+
def default_auth_mech_properties
|
460
|
+
{ service_name: 'mongodb' }
|
461
|
+
end
|
479
462
|
|
480
|
-
|
463
|
+
def default_auth_source
|
464
|
+
case @uri_options[:auth_mech]
|
465
|
+
when :gssapi, :mongodb_x509
|
466
|
+
:external
|
467
|
+
when :plain
|
468
|
+
@database || :external
|
469
|
+
else
|
470
|
+
@database || Database::ADMIN
|
481
471
|
end
|
482
472
|
end
|
483
473
|
|
@@ -490,7 +480,7 @@ module Mongo
|
|
490
480
|
end
|
491
481
|
|
492
482
|
def decode(value)
|
493
|
-
::URI
|
483
|
+
::URI.decode(value)
|
494
484
|
end
|
495
485
|
|
496
486
|
def encode(value)
|