mongo 2.11.3 → 2.11.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo/address.rb +53 -37
- data/lib/mongo/server/connection.rb +1 -2
- data/lib/mongo/server/monitor/connection.rb +1 -2
- data/lib/mongo/socket.rb +10 -1
- data/lib/mongo/version.rb +1 -1
- data/spec/integration/bson_symbol_spec.rb +34 -0
- data/spec/integration/connection_spec.rb +57 -9
- data/spec/integration/retryable_errors_spec.rb +2 -2
- data/spec/mongo/address_spec.rb +19 -13
- data/spec/mongo/auth/scram/conversation_spec.rb +2 -0
- data/spec/mongo/collection_spec.rb +2 -2
- data/spec/mongo/server/connection_spec.rb +6 -6
- data/spec/mongo/server/monitor/connection_spec.rb +6 -6
- data/spec/mongo/socket/ssl_spec.rb +132 -98
- data/spec/mongo/socket/tcp_spec.rb +1 -9
- data/spec/support/cluster_tools.rb +5 -3
- data/spec/support/lite_constraints.rb +8 -0
- metadata +632 -630
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3363520c62689e2f649389acd84f68b46dba99a62d82c174c77414f66e055dd
|
4
|
+
data.tar.gz: 7b1a54bcc8657047ec15b61cdf39588965fc9328d0ac56aa294f7891ef7fb3e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 546bfab9a6042f6194f87efc8f19fa92ee9828040d1f34a98045e243cce6f056837e4144856a37803f964cf345724ccfdeb6eb2d407f265b4a02e7a14622f0a1
|
7
|
+
data.tar.gz: 9eb09ae2c44b3cee7cd9c082af7702b0acd4b56674a6c17b3959c05f654987b3b34f6d87c8d74d9863f312addc01a8de090899a879847d014fa0e7c9baa217d6
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/mongo/address.rb
CHANGED
@@ -66,6 +66,8 @@ module Mongo
|
|
66
66
|
# @param [ String ] seed The provided address.
|
67
67
|
# @param [ Hash ] options The address options.
|
68
68
|
#
|
69
|
+
# @option options [ Float ] :connect_timeout Connect timeout.
|
70
|
+
#
|
69
71
|
# @since 2.0.0
|
70
72
|
def initialize(seed, options = {})
|
71
73
|
if seed.nil?
|
@@ -85,6 +87,9 @@ module Mongo
|
|
85
87
|
# @return [ Integer ] port The port.
|
86
88
|
attr_reader :port
|
87
89
|
|
90
|
+
# @api private
|
91
|
+
attr_reader :options
|
92
|
+
|
88
93
|
# Check equality of the address to another.
|
89
94
|
#
|
90
95
|
# @example Check address equality.
|
@@ -138,13 +143,30 @@ module Mongo
|
|
138
143
|
"#<Mongo::Address:0x#{object_id} address=#{to_s}>"
|
139
144
|
end
|
140
145
|
|
141
|
-
# Get a socket for the
|
146
|
+
# Get a socket for the address stored in this object, given the options.
|
147
|
+
#
|
148
|
+
# If the address stored in this object looks like a Unix path, this method
|
149
|
+
# returns a Unix domain socket for this path.
|
142
150
|
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
151
|
+
# Otherwise, this method attempts to resolve the address stored in
|
152
|
+
# this object to IPv4 and IPv6 addresses using +Socket#getaddrinfo+, then
|
153
|
+
# connects to the resulting addresses and returns the socket of the first
|
154
|
+
# successful connection. The order in which address families (IPv4/IPV6)
|
155
|
+
# are tried is the same order in which the addresses are returned by
|
156
|
+
# +getaddrinfo+, and is determined by the host system.
|
157
|
+
#
|
158
|
+
# Name resolution is performed on each +socket+ call. This is done so that
|
159
|
+
# any changes to which addresses the host names used as seeds or in
|
160
|
+
# server configuration resolve to are immediately noticed by the driver,
|
161
|
+
# even if a socket has been connected to the affected host name/address
|
162
|
+
# before. However, note that DNS TTL values may still affect when a change
|
163
|
+
# to a host address is noticed by the driver.
|
164
|
+
#
|
165
|
+
# This method propagates any exceptions raised during DNS resolution and
|
166
|
+
# subsequent connection attempts. In case of a host name resolving to
|
167
|
+
# multiple IP addresses, the error raised by the last attempt is propagated
|
168
|
+
# to the caller. This method does not map exceptions to Mongo::Error
|
169
|
+
# subclasses, and may raise any subclass of Exception.
|
148
170
|
#
|
149
171
|
# @example Get a socket.
|
150
172
|
# address.socket(5, :ssl => true)
|
@@ -155,11 +177,34 @@ module Mongo
|
|
155
177
|
#
|
156
178
|
# @option options [ Float ] :connect_timeout Connect timeout.
|
157
179
|
#
|
158
|
-
# @return [ Mongo::Socket::SSL
|
180
|
+
# @return [ Mongo::Socket::SSL | Mongo::Socket::TCP | Mongo::Socket::Unix ]
|
181
|
+
# The socket.
|
182
|
+
#
|
183
|
+
# @raise [ Exception ] If network connection failed.
|
159
184
|
#
|
160
185
|
# @since 2.0.0
|
161
186
|
def socket(socket_timeout, ssl_options = {}, options = {})
|
162
|
-
|
187
|
+
if seed.downcase =~ Unix::MATCH
|
188
|
+
specific_address = Unix.new(seed.downcase)
|
189
|
+
return specific_address.socket(socket_timeout, ssl_options, options)
|
190
|
+
end
|
191
|
+
|
192
|
+
options = {
|
193
|
+
connect_timeout: Server::CONNECT_TIMEOUT,
|
194
|
+
}.update(options)
|
195
|
+
|
196
|
+
family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
|
197
|
+
error = nil
|
198
|
+
::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
|
199
|
+
begin
|
200
|
+
specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
|
201
|
+
socket = specific_address.socket(socket_timeout, ssl_options, options)
|
202
|
+
return socket
|
203
|
+
rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
|
204
|
+
error = e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
raise error
|
163
208
|
end
|
164
209
|
|
165
210
|
# Get the address as a string.
|
@@ -182,37 +227,8 @@ module Mongo
|
|
182
227
|
end
|
183
228
|
end
|
184
229
|
|
185
|
-
# @api private
|
186
|
-
def connect_timeout
|
187
|
-
@connect_timeout ||= @options[:connect_timeout] || Server::CONNECT_TIMEOUT
|
188
|
-
end
|
189
|
-
|
190
230
|
private
|
191
231
|
|
192
|
-
# To determine which address the socket will connect to, the driver will
|
193
|
-
# attempt to connect to each IP address returned by Socket::getaddrinfo in
|
194
|
-
# sequence. Once a successful connection is made, a resolver with that
|
195
|
-
# IP address specified is returned. If no successful connection is
|
196
|
-
# made, the error made by the last connection attempt is raised.
|
197
|
-
def create_resolver(ssl_options)
|
198
|
-
return Unix.new(seed.downcase) if seed.downcase =~ Unix::MATCH
|
199
|
-
|
200
|
-
family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
|
201
|
-
error = nil
|
202
|
-
::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
|
203
|
-
begin
|
204
|
-
specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
|
205
|
-
socket = specific_address.socket(
|
206
|
-
connect_timeout, ssl_options, connect_timeout: connect_timeout)
|
207
|
-
socket.close
|
208
|
-
return specific_address
|
209
|
-
rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
|
210
|
-
error = e
|
211
|
-
end
|
212
|
-
end
|
213
|
-
raise error
|
214
|
-
end
|
215
|
-
|
216
232
|
def parse_host_port
|
217
233
|
address = seed.downcase
|
218
234
|
case address
|
@@ -187,8 +187,7 @@ module Mongo
|
|
187
187
|
|
188
188
|
# Separate method to permit easier mocking in the test suite.
|
189
189
|
def do_connect
|
190
|
-
socket = address.socket(socket_timeout, ssl_options,
|
191
|
-
connect_timeout: address.connect_timeout)
|
190
|
+
socket = address.socket(socket_timeout, ssl_options, address.options)
|
192
191
|
|
193
192
|
begin
|
194
193
|
handshake!(socket)
|
@@ -162,8 +162,7 @@ module Mongo
|
|
162
162
|
# @since 2.0.0
|
163
163
|
def connect!
|
164
164
|
unless @socket
|
165
|
-
socket = address.socket(socket_timeout, ssl_options,
|
166
|
-
connect_timeout: address.connect_timeout)
|
165
|
+
socket = address.socket(socket_timeout, ssl_options, address.options)
|
167
166
|
handshake!(socket)
|
168
167
|
@socket = socket
|
169
168
|
end
|
data/lib/mongo/socket.rb
CHANGED
@@ -63,7 +63,16 @@ module Mongo
|
|
63
63
|
def alive?
|
64
64
|
sock_arr = [ @socket ]
|
65
65
|
if Kernel::select(sock_arr, nil, sock_arr, 0)
|
66
|
-
eof?
|
66
|
+
# The eof? call is supposed to return immediately since select
|
67
|
+
# indicated the socket is readable. However, if @socket is an SSL
|
68
|
+
# socket, eof? can block anyway - see RUBY-2140.
|
69
|
+
begin
|
70
|
+
Timeout.timeout(0.1) do
|
71
|
+
eof?
|
72
|
+
end
|
73
|
+
rescue ::Timeout::Error
|
74
|
+
true
|
75
|
+
end
|
67
76
|
else
|
68
77
|
true
|
69
78
|
end
|
data/lib/mongo/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Symbol encoding to BSON' do
|
4
|
+
let(:value) { :foo }
|
5
|
+
|
6
|
+
let(:hash) do
|
7
|
+
{'foo' => value}
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:serialized) do
|
11
|
+
hash.to_bson.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:expected) do
|
15
|
+
"\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'encodes symbol to BSON symbol' do
|
19
|
+
serialized.should == expected
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'round-trips symbol values' do
|
23
|
+
buffer = BSON::ByteBuffer.new(serialized)
|
24
|
+
Hash.from_bson(buffer).should == hash
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'round-trips symbol values using the same byte buffer' do
|
28
|
+
if BSON::Environment.jruby?
|
29
|
+
pending 'https://jira.mongodb.org/browse/RUBY-2128'
|
30
|
+
end
|
31
|
+
|
32
|
+
Hash.from_bson(hash.to_bson).should == hash
|
33
|
+
end
|
34
|
+
end
|
@@ -12,17 +12,18 @@ describe 'Connections' do
|
|
12
12
|
let(:server) { client.cluster.servers.first }
|
13
13
|
|
14
14
|
describe '#connect!' do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# RSpec::Expectations::ExpectationNotMetError: expected Mongo::Error::SocketError, got #<NameError: undefined method `write' for class `Mongo::Socket'>
|
20
|
-
fails_on_jruby
|
15
|
+
|
16
|
+
let(:connection) do
|
17
|
+
Mongo::Server::Connection.new(server, server.options)
|
18
|
+
end
|
21
19
|
|
22
20
|
context 'network error during handshake' do
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# On JRuby 9.2.7.0, this line:
|
22
|
+
# expect_any_instance_of(Mongo::Socket).to receive(:write).and_raise(exception)
|
23
|
+
# ... appears to produce a moment in which Mongo::Socket#write is undefined
|
24
|
+
# entirely, resulting in this failure:
|
25
|
+
# RSpec::Expectations::ExpectationNotMetError: expected Mongo::Error::SocketError, got #<NameError: undefined method `write' for class `Mongo::Socket'>
|
26
|
+
fails_on_jruby
|
26
27
|
|
27
28
|
let(:exception) { Mongo::Error::SocketError }
|
28
29
|
|
@@ -120,6 +121,53 @@ describe 'Connections' do
|
|
120
121
|
expect(client.cluster.topology).to be_a(Mongo::Cluster::Topology::ReplicaSetNoPrimary)
|
121
122
|
end
|
122
123
|
end
|
124
|
+
|
125
|
+
describe 'number of sockets created' do
|
126
|
+
|
127
|
+
before do
|
128
|
+
server
|
129
|
+
end
|
130
|
+
|
131
|
+
shared_examples_for 'is 1 per connection' do
|
132
|
+
it 'is 1 per connection' do
|
133
|
+
# Instantiating a connection object should not create any sockets
|
134
|
+
RSpec::Mocks.with_temporary_scope do
|
135
|
+
expect(socket_cls).not_to receive(:new)
|
136
|
+
|
137
|
+
connection
|
138
|
+
end
|
139
|
+
|
140
|
+
# When the connection connects, exactly one socket should be created
|
141
|
+
# (and subsequently connected)
|
142
|
+
RSpec::Mocks.with_temporary_scope do
|
143
|
+
expect(socket_cls).to receive(:new).and_call_original
|
144
|
+
|
145
|
+
connection.connect!
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:socket_cls) { ::Socket }
|
151
|
+
|
152
|
+
it_behaves_like 'is 1 per connection'
|
153
|
+
|
154
|
+
context 'connection to Unix domain socket' do
|
155
|
+
# Server does not allow Unix socket connections when TLS is enabled
|
156
|
+
require_no_tls
|
157
|
+
|
158
|
+
let(:port) { SpecConfig.instance.any_port }
|
159
|
+
|
160
|
+
let(:client) do
|
161
|
+
new_local_client(["/tmp/mongodb-#{port}.sock"], connect: :direct).tap do |client|
|
162
|
+
stop_monitoring(client)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
let(:socket_cls) { ::UNIXSocket }
|
167
|
+
|
168
|
+
it_behaves_like 'is 1 per connection'
|
169
|
+
end
|
170
|
+
end
|
123
171
|
end
|
124
172
|
|
125
173
|
describe 'wire protocol version range update' do
|
@@ -24,7 +24,7 @@ describe 'Failing retryable operations' do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
after do
|
27
|
-
ClusterTools.instance.
|
27
|
+
ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
|
28
28
|
client.use(:admin).database.command(clear_fail_point_command)
|
29
29
|
end
|
30
30
|
end
|
@@ -62,7 +62,7 @@ describe 'Failing retryable operations' do
|
|
62
62
|
server.monitor.stop!
|
63
63
|
end
|
64
64
|
|
65
|
-
ClusterTools.instance.
|
65
|
+
ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
|
66
66
|
client.use(:admin).database.command(fail_point_command)
|
67
67
|
end
|
68
68
|
end
|
data/spec/mongo/address_spec.rb
CHANGED
@@ -249,11 +249,7 @@ describe Mongo::Address do
|
|
249
249
|
end
|
250
250
|
end
|
251
251
|
|
252
|
-
context 'when creating a socket
|
253
|
-
|
254
|
-
before do
|
255
|
-
address.send(:create_resolver, SpecConfig.instance.ssl_options)
|
256
|
-
end
|
252
|
+
context 'when creating a socket' do
|
257
253
|
|
258
254
|
it 'uses the host, not the IP address' do
|
259
255
|
expect(address.socket(0.0).host).to eq(socket_address_or_host)
|
@@ -285,6 +281,24 @@ describe Mongo::Address do
|
|
285
281
|
end
|
286
282
|
end
|
287
283
|
end
|
284
|
+
|
285
|
+
describe ':connect_timeout option' do
|
286
|
+
clean_slate
|
287
|
+
|
288
|
+
let(:address) { Mongo::Address.new('127.0.0.1') }
|
289
|
+
|
290
|
+
it 'defaults to 10' do
|
291
|
+
RSpec::Mocks.with_temporary_scope do
|
292
|
+
resolved_address = double('address')
|
293
|
+
# This test's expectation
|
294
|
+
expect(resolved_address).to receive(:socket).with(0, {}, connect_timeout: 10)
|
295
|
+
|
296
|
+
expect(Mongo::Address::IPv4).to receive(:new).and_return(resolved_address)
|
297
|
+
|
298
|
+
address.socket(0)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
288
302
|
end
|
289
303
|
|
290
304
|
describe '#to_s' do
|
@@ -320,12 +334,4 @@ describe Mongo::Address do
|
|
320
334
|
end
|
321
335
|
end
|
322
336
|
end
|
323
|
-
|
324
|
-
describe '#connect_timeout' do
|
325
|
-
let(:address) { Mongo::Address.new('127.0.0.1') }
|
326
|
-
|
327
|
-
it 'defaults to 10' do
|
328
|
-
expect(address.send(:connect_timeout)).to eq(10)
|
329
|
-
end
|
330
|
-
end
|
331
337
|
end
|
@@ -278,9 +278,9 @@ describe Mongo::Collection do
|
|
278
278
|
require_topology :replica_set
|
279
279
|
|
280
280
|
let(:client_options) do
|
281
|
-
|
281
|
+
SpecConfig.instance.auth_options.merge(
|
282
282
|
read: { mode: :primary_preferred },
|
283
|
-
|
283
|
+
)
|
284
284
|
end
|
285
285
|
|
286
286
|
let(:subscriber) { EventSubscriber.new }
|
@@ -1080,7 +1080,7 @@ describe Mongo::Server::Connection, retry: 3 do
|
|
1080
1080
|
end
|
1081
1081
|
|
1082
1082
|
it 'uses the connect_timeout for the address' do
|
1083
|
-
expect(connection.address.
|
1083
|
+
expect(connection.address.options[:connect_timeout]).to eq(3)
|
1084
1084
|
end
|
1085
1085
|
|
1086
1086
|
it 'uses the socket_timeout as the socket_timeout' do
|
@@ -1099,7 +1099,7 @@ describe Mongo::Server::Connection, retry: 3 do
|
|
1099
1099
|
end
|
1100
1100
|
|
1101
1101
|
it 'uses the connect_timeout for the address' do
|
1102
|
-
expect(connection.address.
|
1102
|
+
expect(connection.address.options[:connect_timeout]).to eq(3)
|
1103
1103
|
end
|
1104
1104
|
|
1105
1105
|
it 'does not use a socket_timeout' do
|
@@ -1120,8 +1120,8 @@ describe Mongo::Server::Connection, retry: 3 do
|
|
1120
1120
|
connection.connect!
|
1121
1121
|
end
|
1122
1122
|
|
1123
|
-
it '
|
1124
|
-
expect(connection.address.
|
1123
|
+
it 'does not specify connect_timeout for the address' do
|
1124
|
+
expect(connection.address.options[:connect_timeout]).to be nil
|
1125
1125
|
end
|
1126
1126
|
|
1127
1127
|
it 'uses the socket_timeout' do
|
@@ -1139,8 +1139,8 @@ describe Mongo::Server::Connection, retry: 3 do
|
|
1139
1139
|
connection.connect!
|
1140
1140
|
end
|
1141
1141
|
|
1142
|
-
it '
|
1143
|
-
expect(connection.address.
|
1142
|
+
it 'does not specify connect_timeout for the address' do
|
1143
|
+
expect(connection.address.options[:connect_timeout]).to be nil
|
1144
1144
|
end
|
1145
1145
|
|
1146
1146
|
it 'does not use a socket_timeout' do
|
@@ -66,7 +66,7 @@ describe Mongo::Server::Monitor::Connection do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'uses the connect_timeout for the address' do
|
69
|
-
expect(connection.address.
|
69
|
+
expect(connection.address.options[:connect_timeout]).to eq(3)
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'uses the connect_timeout as the socket_timeout' do
|
@@ -81,7 +81,7 @@ describe Mongo::Server::Monitor::Connection do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'uses the connect_timeout for the address' do
|
84
|
-
expect(connection.address.
|
84
|
+
expect(connection.address.options[:connect_timeout]).to eq(3)
|
85
85
|
end
|
86
86
|
|
87
87
|
it 'uses the connect_timeout as the socket_timeout' do
|
@@ -98,8 +98,8 @@ describe Mongo::Server::Monitor::Connection do
|
|
98
98
|
SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: 5)
|
99
99
|
end
|
100
100
|
|
101
|
-
it '
|
102
|
-
expect(connection.address.
|
101
|
+
it 'does not specify connect_timeout for the address' do
|
102
|
+
expect(connection.address.options[:connect_timeout]).to be nil
|
103
103
|
end
|
104
104
|
|
105
105
|
it 'uses the connect_timeout as the socket_timeout' do
|
@@ -113,8 +113,8 @@ describe Mongo::Server::Monitor::Connection do
|
|
113
113
|
SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: nil)
|
114
114
|
end
|
115
115
|
|
116
|
-
it '
|
117
|
-
expect(connection.address.
|
116
|
+
it 'does not specify connect_timeout for the address' do
|
117
|
+
expect(connection.address.options[:connect_timeout]).to be nil
|
118
118
|
end
|
119
119
|
|
120
120
|
it 'uses the connect_timeout as the socket_timeout' do
|