Skip to content

Commit 2bf883e

Browse files
committed
JAVA-2824: Stop authenticating server monitor connections
This is a significant behavior change in the driver, as authentication errors will be thrown much faster to applications, as a MongoSecurityException rather than as a MongoTimeoutException after the server selection timeout (which defaults to 30 seconds) has expired.
1 parent 5db2353 commit 2bf883e

File tree

4 files changed

+75
-11
lines changed

4 files changed

+75
-11
lines changed

driver-core/src/main/com/mongodb/connection/DefaultClusterableServerFactory.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ public ClusterableServer create(final ServerAddress serverAddress, final ServerL
6464
ConnectionPool connectionPool = new DefaultConnectionPool(new ServerId(clusterId, serverAddress),
6565
new InternalStreamConnectionFactory(streamFactory, credentialList, applicationName,
6666
mongoDriverInformation, compressorList, commandListener), connectionPoolSettings);
67+
68+
// no credentials, compressor list, or command listener for the server monitor factory
6769
ServerMonitorFactory serverMonitorFactory =
6870
new DefaultServerMonitorFactory(new ServerId(clusterId, serverAddress), serverSettings, clusterClock,
69-
new InternalStreamConnectionFactory(heartbeatStreamFactory, credentialList, applicationName,
70-
mongoDriverInformation, Collections.<MongoCompressor>emptyList(), null), connectionPool);
71+
new InternalStreamConnectionFactory(heartbeatStreamFactory, Collections.<MongoCredentialWithCache>emptyList(),
72+
applicationName, mongoDriverInformation, Collections.<MongoCompressor>emptyList(), null), connectionPool);
7173

7274
return new DefaultServer(new ServerId(clusterId, serverAddress), clusterSettings.getMode(), connectionPool,
7375
new DefaultConnectionFactory(), serverMonitorFactory, serverListener, commandListener, clusterClock);

driver-core/src/main/com/mongodb/connection/DefaultServer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public Connection getConnection() {
7979
try {
8080
return connectionFactory.create(connectionPool.get(), new DefaultServerProtocolExecutor(), clusterConnectionMode);
8181
} catch (MongoSecurityException e) {
82-
invalidate();
82+
connectionPool.invalidate();
8383
throw e;
8484
} catch (MongoSocketException e) {
8585
invalidate();
@@ -93,7 +93,9 @@ public void getConnectionAsync(final SingleResultCallback<AsyncConnection> callb
9393
connectionPool.getAsync(new SingleResultCallback<InternalConnection>() {
9494
@Override
9595
public void onResult(final InternalConnection result, final Throwable t) {
96-
if (t instanceof MongoSecurityException || t instanceof MongoSocketException) {
96+
if (t instanceof MongoSecurityException) {
97+
connectionPool.invalidate();
98+
} else if (t instanceof MongoSocketException) {
9799
invalidate();
98100
}
99101
if (t != null) {

driver-core/src/test/functional/com/mongodb/operation/UserOperationsSpecification.groovy

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ package com.mongodb.operation
1919
import category.Slow
2020
import com.mongodb.MongoCredential
2121
import com.mongodb.MongoNamespace
22+
import com.mongodb.MongoSecurityException
2223
import com.mongodb.MongoServerException
23-
import com.mongodb.MongoTimeoutException
2424
import com.mongodb.MongoWriteConcernException
2525
import com.mongodb.OperationFunctionalSpecification
2626
import com.mongodb.ReadPreference
@@ -135,12 +135,13 @@ class UserOperationsSpecification extends OperationFunctionalSpecification {
135135
given:
136136
def credential = createCredential('user', databaseName, 'pencil' as char[])
137137
def cluster = getCluster(credential, ClusterSettings.builder().serverSelectionTimeout(1, TimeUnit.SECONDS))
138+
def server = cluster.selectServer(new WritableServerSelector())
138139

139140
when:
140-
cluster.selectServer(new WritableServerSelector())
141+
server.getConnection()
141142

142143
then:
143-
thrown(MongoTimeoutException)
144+
thrown(MongoSecurityException)
144145

145146
cleanup:
146147
cluster?.close()
@@ -155,12 +156,13 @@ class UserOperationsSpecification extends OperationFunctionalSpecification {
155156
execute(new CreateUserOperation(credential, true), async)
156157
execute(new DropUserOperation(databaseName, credential.userName), async)
157158
def cluster = getCluster(ClusterSettings.builder().serverSelectionTimeout(1, TimeUnit.SECONDS))
159+
def server = cluster.selectServer(new WritableServerSelector())
158160

159161
when:
160-
cluster.selectServer(new WritableServerSelector())
162+
server.getConnection()
161163

162164
then:
163-
thrown(MongoTimeoutException)
165+
thrown(MongoSecurityException)
164166

165167
cleanup:
166168
cluster?.close()

driver-core/src/test/unit/com/mongodb/connection/DefaultServerSpecification.groovy

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,41 @@ class DefaultServerSpecification extends Specification {
156156

157157
where:
158158
exceptionToThrow << [
159-
new MongoSecurityException(createCredential('jeff', 'admin', '123'.toCharArray()), 'Auth failed'),
160159
new MongoSocketOpenException('open failed', new ServerAddress(), new IOException()),
161160
new MongoSocketWriteException('Write failed', new ServerAddress(), new IOException()),
162161
new MongoSocketReadException('Read failed', new ServerAddress(), new IOException()),
163162
new MongoSocketReadTimeoutException('Read timed out', new ServerAddress(), new IOException()),
164163
]
165164
}
166165

166+
def 'failed authentication should invalidate the connection pool'() {
167+
given:
168+
def clusterTime = new ClusterClock()
169+
def connectionPool = Mock(ConnectionPool)
170+
def connectionFactory = Mock(ConnectionFactory)
171+
def serverMonitorFactory = Stub(ServerMonitorFactory)
172+
def serverMonitor = Mock(ServerMonitor)
173+
connectionPool.get() >> { throw exceptionToThrow }
174+
serverMonitorFactory.create(_) >> { serverMonitor }
175+
176+
def server = new DefaultServer(serverId, SINGLE, connectionPool, connectionFactory, serverMonitorFactory,
177+
NO_OP_SERVER_LISTENER, null, clusterTime)
178+
179+
when:
180+
server.getConnection()
181+
182+
then:
183+
def e = thrown(MongoSecurityException)
184+
e.is(exceptionToThrow)
185+
1 * connectionPool.invalidate()
186+
0 * serverMonitor.connect()
187+
188+
where:
189+
exceptionToThrow << [
190+
new MongoSecurityException(createCredential('jeff', 'admin', '123'.toCharArray()), 'Auth failed'),
191+
]
192+
}
193+
167194
def 'failed open should invalidate the server asynchronously'() {
168195
given:
169196
def clusterTime = new ClusterClock()
@@ -192,14 +219,45 @@ class DefaultServerSpecification extends Specification {
192219

193220
where:
194221
exceptionToThrow << [
195-
new MongoSecurityException(createCredential('jeff', 'admin', '123'.toCharArray()), 'Auth failed'),
196222
new MongoSocketOpenException('open failed', new ServerAddress(), new IOException()),
197223
new MongoSocketWriteException('Write failed', new ServerAddress(), new IOException()),
198224
new MongoSocketReadException('Read failed', new ServerAddress(), new IOException()),
199225
new MongoSocketReadTimeoutException('Read timed out', new ServerAddress(), new IOException()),
200226
]
201227
}
202228

229+
def 'failed auth should invalidate the connection pool asynchronously'() {
230+
given:
231+
def clusterTime = new ClusterClock()
232+
def connectionPool = Mock(ConnectionPool)
233+
def connectionFactory = Mock(ConnectionFactory)
234+
def serverMonitorFactory = Stub(ServerMonitorFactory)
235+
def serverMonitor = Mock(ServerMonitor)
236+
connectionPool.getAsync(_) >> { it[0].onResult(null, exceptionToThrow) }
237+
serverMonitorFactory.create(_) >> { serverMonitor }
238+
def server = new DefaultServer(serverId, SINGLE, connectionPool, connectionFactory,
239+
serverMonitorFactory, NO_OP_SERVER_LISTENER, null, clusterTime)
240+
241+
when:
242+
def latch = new CountDownLatch(1)
243+
def receivedConnection = null
244+
def receivedThrowable = null
245+
server.getConnectionAsync { result, throwable -> receivedConnection = result; receivedThrowable = throwable; latch.countDown() }
246+
latch.await()
247+
248+
then:
249+
!receivedConnection
250+
receivedThrowable.is(exceptionToThrow)
251+
1 * connectionPool.invalidate()
252+
0 * serverMonitor.connect()
253+
254+
255+
where:
256+
exceptionToThrow << [
257+
new MongoSecurityException(createCredential('jeff', 'admin', '123'.toCharArray()), 'Auth failed'),
258+
]
259+
}
260+
203261
def 'should invalidate on MongoNotPrimaryException'() {
204262
given:
205263
def clusterTime = new ClusterClock()

0 commit comments

Comments
 (0)