mongo 2.10.5 → 2.11.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +2 -0
  6. data/lib/mongo/address.rb +4 -0
  7. data/lib/mongo/address/validator.rb +99 -0
  8. data/lib/mongo/auth.rb +7 -2
  9. data/lib/mongo/auth/user.rb +1 -7
  10. data/lib/mongo/background_thread.rb +135 -0
  11. data/lib/mongo/bulk_write/transformable.rb +3 -3
  12. data/lib/mongo/client.rb +74 -16
  13. data/lib/mongo/cluster.rb +193 -41
  14. data/lib/mongo/cluster/periodic_executor.rb +31 -43
  15. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  16. data/lib/mongo/cluster/srv_monitor.rb +127 -0
  17. data/lib/mongo/collection/view/readable.rb +3 -5
  18. data/lib/mongo/collection/view/writable.rb +3 -3
  19. data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
  20. data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
  21. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  22. data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
  23. data/lib/mongo/error.rb +1 -0
  24. data/lib/mongo/error/auth_error.rb +1 -1
  25. data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
  26. data/lib/mongo/error/invalid_address.rb +24 -0
  27. data/lib/mongo/error/notable.rb +2 -2
  28. data/lib/mongo/error/operation_failure.rb +3 -3
  29. data/lib/mongo/error/pool_closed_error.rb +11 -4
  30. data/lib/mongo/event.rb +1 -1
  31. data/lib/mongo/grid/file.rb +0 -5
  32. data/lib/mongo/grid/file/chunk.rb +0 -2
  33. data/lib/mongo/grid/fs_bucket.rb +13 -15
  34. data/lib/mongo/grid/stream/write.rb +3 -9
  35. data/lib/mongo/loggable.rb +5 -1
  36. data/lib/mongo/monitoring.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
  38. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
  39. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
  40. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
  41. data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
  42. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
  43. data/lib/mongo/operation/shared/executable.rb +5 -10
  44. data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
  45. data/lib/mongo/protocol/get_more.rb +1 -2
  46. data/lib/mongo/protocol/kill_cursors.rb +13 -6
  47. data/lib/mongo/protocol/serializers.rb +4 -20
  48. data/lib/mongo/retryable.rb +9 -34
  49. data/lib/mongo/semaphore.rb +1 -1
  50. data/lib/mongo/server.rb +113 -42
  51. data/lib/mongo/server/connection.rb +12 -5
  52. data/lib/mongo/server/connection_pool.rb +250 -40
  53. data/lib/mongo/server/connection_pool/populator.rb +58 -0
  54. data/lib/mongo/server/description.rb +9 -2
  55. data/lib/mongo/server/monitor.rb +68 -93
  56. data/lib/mongo/server/monitor/connection.rb +2 -0
  57. data/lib/mongo/server_selector/selectable.rb +13 -5
  58. data/lib/mongo/session.rb +0 -13
  59. data/lib/mongo/srv.rb +17 -0
  60. data/lib/mongo/srv/monitor.rb +96 -0
  61. data/lib/mongo/srv/resolver.rb +130 -0
  62. data/lib/mongo/srv/result.rb +126 -0
  63. data/lib/mongo/srv/warning_result.rb +35 -0
  64. data/lib/mongo/uri.rb +45 -55
  65. data/lib/mongo/uri/srv_protocol.rb +89 -42
  66. data/lib/mongo/version.rb +1 -1
  67. data/mongo.gemspec +3 -4
  68. data/spec/README.md +6 -1
  69. data/spec/enterprise_auth/kerberos_spec.rb +7 -6
  70. data/spec/integration/change_stream_examples_spec.rb +0 -4
  71. data/spec/integration/client_construction_spec.rb +14 -2
  72. data/spec/integration/connect_single_rs_name_spec.rb +2 -2
  73. data/spec/integration/connection_pool_populator_spec.rb +296 -0
  74. data/spec/integration/connection_spec.rb +31 -22
  75. data/spec/integration/cursor_reaping_spec.rb +1 -2
  76. data/spec/integration/docs_examples_spec.rb +0 -4
  77. data/spec/integration/heartbeat_events_spec.rb +17 -15
  78. data/spec/integration/reconnect_spec.rb +144 -1
  79. data/spec/integration/retryable_writes_errors_spec.rb +0 -4
  80. data/spec/integration/retryable_writes_spec.rb +36 -36
  81. data/spec/integration/sdam_error_handling_spec.rb +31 -25
  82. data/spec/integration/sdam_events_spec.rb +2 -6
  83. data/spec/integration/server_monitor_spec.rb +28 -0
  84. data/spec/integration/server_selector_spec.rb +7 -5
  85. data/spec/integration/srv_monitoring_spec.rb +360 -0
  86. data/spec/integration/step_down_spec.rb +4 -6
  87. data/spec/lite_spec_helper.rb +22 -0
  88. data/spec/mongo/address/validator_spec.rb +51 -0
  89. data/spec/mongo/auth/cr_spec.rb +1 -29
  90. data/spec/mongo/auth/ldap_spec.rb +1 -29
  91. data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
  92. data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
  93. data/spec/mongo/auth/scram_spec.rb +1 -29
  94. data/spec/mongo/auth/user/view_spec.rb +1 -36
  95. data/spec/mongo/auth/user_spec.rb +0 -12
  96. data/spec/mongo/auth/x509_spec.rb +1 -29
  97. data/spec/mongo/bulk_write_spec.rb +2 -2
  98. data/spec/mongo/client_construction_spec.rb +56 -15
  99. data/spec/mongo/client_spec.rb +31 -27
  100. data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
  101. data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
  102. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
  103. data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
  104. data/spec/mongo/cluster/topology/single_spec.rb +20 -11
  105. data/spec/mongo/cluster_spec.rb +45 -29
  106. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
  107. data/spec/mongo/collection/view/readable_spec.rb +0 -16
  108. data/spec/mongo/collection_spec.rb +0 -44
  109. data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
  110. data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
  111. data/spec/mongo/cursor_spec.rb +27 -7
  112. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
  113. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
  114. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
  115. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
  116. data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
  117. data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
  118. data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
  119. data/spec/mongo/retryable_spec.rb +52 -31
  120. data/spec/mongo/server/app_metadata_spec.rb +0 -8
  121. data/spec/mongo/server/connection_auth_spec.rb +5 -2
  122. data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
  123. data/spec/mongo/server/connection_pool_spec.rb +256 -107
  124. data/spec/mongo/server/connection_spec.rb +22 -33
  125. data/spec/mongo/server/description_spec.rb +42 -4
  126. data/spec/mongo/server/monitor/connection_spec.rb +22 -11
  127. data/spec/mongo/server/monitor_spec.rb +66 -107
  128. data/spec/mongo/server_spec.rb +82 -60
  129. data/spec/mongo/session/session_pool_spec.rb +1 -5
  130. data/spec/mongo/session_spec.rb +0 -4
  131. data/spec/mongo/socket/ssl_spec.rb +2 -2
  132. data/spec/mongo/srv/monitor_spec.rb +211 -0
  133. data/spec/mongo/srv/result_spec.rb +54 -0
  134. data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
  135. data/spec/mongo/uri_spec.rb +125 -4
  136. data/spec/spec_helper.rb +6 -0
  137. data/spec/spec_tests/auth_spec.rb +39 -0
  138. data/spec/spec_tests/cmap_spec.rb +55 -8
  139. data/spec/spec_tests/connection_string_spec.rb +6 -31
  140. data/spec/spec_tests/data/auth/connection-string.yml +297 -0
  141. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
  142. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
  143. data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
  144. data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
  145. data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
  146. data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
  147. data/spec/spec_tests/data/transactions/abort.yml +3 -3
  148. data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
  149. data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
  150. data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
  151. data/spec/spec_tests/max_staleness_spec.rb +7 -2
  152. data/spec/spec_tests/retryable_reads_spec.rb +0 -31
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
  154. data/spec/spec_tests/sdam_spec.rb +4 -7
  155. data/spec/spec_tests/server_selection_spec.rb +6 -2
  156. data/spec/spec_tests/transactions_spec.rb +0 -2
  157. data/spec/spec_tests/uri_options_spec.rb +4 -2
  158. data/spec/stress/connection_pool_stress_spec.rb +203 -0
  159. data/spec/stress/connection_pool_timing_spec.rb +181 -0
  160. data/spec/support/auth.rb +113 -0
  161. data/spec/support/background_thread_registry.rb +63 -0
  162. data/spec/support/client_registry.rb +11 -2
  163. data/spec/support/cluster_config.rb +65 -46
  164. data/spec/support/cluster_tools.rb +2 -2
  165. data/spec/support/cmap.rb +13 -14
  166. data/spec/support/cmap/verifier.rb +4 -5
  167. data/spec/support/command_monitoring.rb +0 -5
  168. data/spec/support/common_shortcuts.rb +101 -1
  169. data/spec/support/constraints.rb +25 -0
  170. data/spec/support/dns.rb +13 -0
  171. data/spec/support/event_subscriber.rb +0 -7
  172. data/spec/support/json_ext_formatter.rb +5 -1
  173. data/spec/support/lite_constraints.rb +22 -6
  174. data/spec/support/local_resource_registry.rb +34 -0
  175. data/spec/support/sdam_monitoring.rb +115 -0
  176. data/spec/support/spec_config.rb +20 -6
  177. data/spec/support/spec_setup.rb +2 -2
  178. data/spec/support/transactions.rb +1 -1
  179. data/spec/support/transactions/test.rb +1 -1
  180. data/spec/support/utils.rb +1 -16
  181. metadata +685 -659
  182. metadata.gz.sig +0 -0
  183. data/lib/mongo/event/description_changed.rb +0 -52
  184. data/spec/integration/bson_symbol_spec.rb +0 -34
  185. data/spec/integration/crud_spec.rb +0 -45
  186. data/spec/integration/get_more_spec.rb +0 -32
  187. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  188. data/spec/integration/retryable_errors_spec.rb +0 -265
  189. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
  190. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
  191. 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
- # The options specified in the uri.
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, INVALID_SCHEME)
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 [ Hash ] The options passed to the Mongo::Client
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 = uri_options.merge(:database => database)
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
- raise_invalid_error!(INVALID_SCHEME) unless parsed_scheme == scheme
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
- validate_host!(host)
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.length > 0
409
- raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE
410
- user_decoded = decode(user)
411
- if user_decoded =~ PERCENT_CHAR && encode(user_decoded) != user
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 validate_port_string!(port)
438
- unless port.nil? || (port.length > 0 && port.to_i > 0 && port.to_i <= 65535)
439
- raise_invalid_error!(INVALID_PORT)
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
- # Takes a host in ipv4/ipv6/hostname/socket path format and validates
444
- # its format.
445
- def validate_host!(host)
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
- if port && port.empty?
477
- raise_invalid_error!("Port is empty: #{port}")
478
- end
456
+ opts
457
+ end
458
+
459
+ def default_auth_mech_properties
460
+ { service_name: 'mongodb' }
461
+ end
479
462
 
480
- validate_port_string!(port)
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::DEFAULT_PARSER.unescape(value)
483
+ ::URI.decode(value)
494
484
  end
495
485
 
496
486
  def encode(value)