Skip to content

Commit 47dbb49

Browse files
committed
NODE-483 TypeError exception when server.lastIsMaster() returns null
1 parent 704b0d3 commit 47dbb49

File tree

6 files changed

+179
-176
lines changed

6 files changed

+179
-176
lines changed

lib/db.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,13 +282,13 @@ Db.prototype.close = function(force, callback) {
282282
// Fire close on all children
283283
for(var i = 0; i < this.s.children.length; i++) {
284284
this.s.children[i].emit('close');
285-
}
285+
}
286286
}
287287

288288
// Remove listeners after emit
289-
self.removeAllListeners('close');
289+
self.removeAllListeners('close');
290290
}
291-
291+
292292
// Close parent db if set
293293
if(this.s.parentDb) this.s.parentDb.close();
294294
// Callback after next event loop tick
@@ -823,17 +823,17 @@ Db.prototype.db = function(dbName) {
823823

824824
// Keep reference to the object
825825
// if(this.s.parentDb) {
826-
// this.s.parentDb.s.children.push(db);
827-
this.addChild(db);
828-
// }
826+
// this.s.parentDb.s.children.push(db);
827+
this.addChild(db);
828+
// }
829829
// this.s.children.push(db);
830830

831831
// // Add listeners to the parent database
832832
// this.once('error', createListener(this, 'error', db));
833833
// this.once('timeout', createListener(this, 'timeout', db));
834834
// this.once('close', createListener(this, 'close', db));
835835
// this.once('parseError', createListener(this, 'parseError', db));
836-
836+
837837
// Return the database
838838
return db;
839839
};

lib/mongos.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var EventEmitter = require('events').EventEmitter
1414
/**
1515
* @fileOverview The **Mongos** class is a class that represents a Mongos Proxy topology and is
1616
* used to construct connections.
17-
*
17+
*
1818
* **Mongos Should not be used, use MongoClient.connect**
1919
* @example
2020
* var Db = require('mongodb').Db,
@@ -39,17 +39,17 @@ var EventEmitter = require('events').EventEmitter
3939
* @param {booelan} [options.ha=true] Turn on high availability monitoring.
4040
* @param {number} [options.haInterval=5000] Time between each replicaset status check.
4141
* @param {number} [options.poolSize=5] Number of connections in the connection pool for each server instance, set to 5 as default for legacy reasons.
42-
* @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
42+
* @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
4343
* @param {object} [options.sslValidate=true] Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
4444
* @param {array} [options.sslCA=null] Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
4545
* @param {(Buffer|string)} [options.sslCert=null] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
4646
* @param {(Buffer|string)} [options.sslKey=null] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
4747
* @param {(Buffer|string)} [options.sslPass=null] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
48-
* @param {object} [options.socketOptions=null] Socket options
49-
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
50-
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.
51-
* @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
52-
* @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
48+
* @param {object} [options.socketOptions=null] Socket options
49+
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
50+
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.
51+
* @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
52+
* @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
5353
* @fires Mongos#connect
5454
* @fires Mongos#ha
5555
* @fires Mongos#joined
@@ -98,7 +98,7 @@ var Mongos = function(servers, options) {
9898
var finalOptions = shallowClone(options);
9999

100100
// Default values
101-
finalOptions.size = typeof options.poolSize == 'number' ? options.poolSize : 5;
101+
finalOptions.size = typeof options.poolSize == 'number' ? options.poolSize : 5;
102102
finalOptions.reconnect = typeof options.auto_reconnect == 'boolean' ? options.auto_reconnect : true;
103103
finalOptions.emitError = typeof options.emitError == 'boolean' ? options.emitError : true;
104104
finalOptions.cursorFactory = Cursor;
@@ -114,7 +114,7 @@ var Mongos = function(servers, options) {
114114
}
115115
if(options.socketOptions.socketTimeoutMS)
116116
finalOptions.socketTimeout = options.socketOptions.socketTimeoutMS;
117-
}
117+
}
118118

119119
// Are we running in debug mode
120120
var debug = typeof options.debug == 'boolean' ? options.debug : false;
@@ -189,9 +189,9 @@ var Mongos = function(servers, options) {
189189
});
190190

191191
// BSON property
192-
Object.defineProperty(this, 'bson', {
193-
enumerable: true, get: function() {
194-
return self.s.mongos.bson;
192+
Object.defineProperty(this, 'bson', {
193+
enumerable: true, get: function() {
194+
return self.s.mongos.bson;
195195
}
196196
});
197197

@@ -230,7 +230,7 @@ Mongos.prototype.connect = function(db, _options, callback) {
230230
// Try to callback
231231
try {
232232
callback(err);
233-
} catch(err) {
233+
} catch(err) {
234234
process.nextTick(function() { throw err; })
235235
}
236236
}
@@ -276,14 +276,14 @@ Mongos.prototype.connect = function(db, _options, callback) {
276276
self.s.mongos.on('fullsetup', relay('fullsetup'));
277277

278278
// Emit open event
279-
self.emit('open', null, self);
279+
self.emit('open', null, self);
280280

281281
// Return correctly
282282
try {
283283
callback(null, self);
284-
} catch(err) {
284+
} catch(err) {
285285
process.nextTick(function() { throw err; })
286-
}
286+
}
287287
}
288288

289289
// Set up listeners
@@ -305,6 +305,7 @@ Mongos.prototype.parserType = function() {
305305
// Server capabilities
306306
Mongos.prototype.capabilities = function() {
307307
if(this.s.sCapabilities) return this.s.sCapabilities;
308+
if(this.s.mongos.lastIsMaster() == null) throw new MongoError('cannot establish topology capabilities as driver is still in process of connecting');
308309
this.s.sCapabilities = new ServerCapabilities(this.s.mongos.lastIsMaster());
309310
return this.s.sCapabilities;
310311
}
@@ -344,7 +345,7 @@ Mongos.prototype.cursor = function(ns, cmd, options) {
344345

345346
Mongos.prototype.setBSONParserType = function(type) {
346347
return this.s.mongos.setBSONParserType(type);
347-
}
348+
}
348349

349350
Mongos.prototype.lastIsMaster = function() {
350351
return this.s.mongos.lastIsMaster();
@@ -371,7 +372,7 @@ Mongos.prototype.auth = function() {
371372
*/
372373
Mongos.prototype.connections = function() {
373374
return this.s.mongos.connections();
374-
}
375+
}
375376

376377
/**
377378
* A mongos connect event, used to verify that the connection is up and running

lib/replset.js

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var EventEmitter = require('events').EventEmitter
1919
/**
2020
* @fileOverview The **ReplSet** class is a class that represents a Replicaset topology and is
2121
* used to construct connections.
22-
*
22+
*
2323
* **ReplSet Should not be used, use MongoClient.connect**
2424
* @example
2525
* var Db = require('mongodb').Db,
@@ -44,20 +44,20 @@ var EventEmitter = require('events').EventEmitter
4444
* @param {booelan} [options.ha=true] Turn on high availability monitoring.
4545
* @param {number} [options.haInterval=5000] Time between each replicaset status check.
4646
* @param {string} options.replicaSet The name of the replicaset to connect to.
47-
* @param {number} [options.secondaryAcceptableLatencyMS=15] Sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms)
47+
* @param {number} [options.secondaryAcceptableLatencyMS=15] Sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms)
4848
* @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
4949
* @param {number} [options.poolSize=5] Number of connections in the connection pool for each server instance, set to 5 as default for legacy reasons.
50-
* @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
50+
* @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
5151
* @param {object} [options.sslValidate=true] Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
5252
* @param {array} [options.sslCA=null] Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
5353
* @param {(Buffer|string)} [options.sslCert=null] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
5454
* @param {(Buffer|string)} [options.sslKey=null] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
5555
* @param {(Buffer|string)} [options.sslPass=null] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
56-
* @param {object} [options.socketOptions=null] Socket options
57-
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
58-
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.
59-
* @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
60-
* @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
56+
* @param {object} [options.socketOptions=null] Socket options
57+
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
58+
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.
59+
* @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
60+
* @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
6161
* @fires ReplSet#connect
6262
* @fires ReplSet#ha
6363
* @fires ReplSet#joined
@@ -70,7 +70,7 @@ var EventEmitter = require('events').EventEmitter
7070
* @fires ReplSet#parseError
7171
* @return {ReplSet} a ReplSet instance.
7272
*/
73-
var ReplSet = function(servers, options) {
73+
var ReplSet = function(servers, options) {
7474
if(!(this instanceof ReplSet)) return new ReplSet(servers, options);
7575
options = options || {};
7676
var self = this;
@@ -105,7 +105,7 @@ var ReplSet = function(servers, options) {
105105
// Final options
106106
var finalOptions = shallowClone(options);
107107

108-
// Default values
108+
// Default values
109109
finalOptions.size = typeof options.poolSize == 'number' ? options.poolSize : 5;
110110
finalOptions.reconnect = typeof options.auto_reconnect == 'boolean' ? options.auto_reconnect : true;
111111
finalOptions.emitError = typeof options.emitError == 'boolean' ? options.emitError : true;
@@ -120,11 +120,11 @@ var ReplSet = function(servers, options) {
120120
this.connectTimeoutMS = options.socketOptions.connectTimeoutMS;
121121
finalOptions.connectionTimeout = options.socketOptions.connectTimeoutMS;
122122
}
123-
123+
124124
if(options.socketOptions.socketTimeoutMS) {
125125
finalOptions.socketTimeout = options.socketOptions.socketTimeoutMS;
126126
}
127-
}
127+
}
128128

129129
// Get the name
130130
var replicaSet = options.replicaSet || options.rs_name;
@@ -209,13 +209,13 @@ var ReplSet = function(servers, options) {
209209
// Options
210210
, options: options
211211
}
212-
212+
213213
// Debug
214214
if(debug) {
215215
// Last ismaster
216216
Object.defineProperty(this, 'replset', {
217217
enumerable:true, get: function() { return replset; }
218-
});
218+
});
219219
}
220220

221221
// Last ismaster
@@ -224,9 +224,9 @@ var ReplSet = function(servers, options) {
224224
});
225225

226226
// BSON property
227-
Object.defineProperty(this, 'bson', {
228-
enumerable: true, get: function() {
229-
return replset.bson;
227+
Object.defineProperty(this, 'bson', {
228+
enumerable: true, get: function() {
229+
return replset.bson;
230230
}
231231
});
232232

@@ -249,7 +249,7 @@ var translateReadPreference = function(options) {
249249
, options.readPreference.tags);
250250
}
251251

252-
return options;
252+
return options;
253253
}
254254

255255
ReplSet.prototype.parserType = function() {
@@ -304,12 +304,12 @@ ReplSet.prototype.connect = function(db, _options, callback) {
304304

305305
// Relay ha
306306
var relayHa = function(t, state) {
307-
self.emit('ha', t, state);
307+
self.emit('ha', t, state);
308308

309309
if(t == 'start') {
310-
self.emit('ha_connect', t, state);
310+
self.emit('ha_connect', t, state);
311311
} else if(t == 'end') {
312-
self.emit('ha_ismaster', t, state);
312+
self.emit('ha_ismaster', t, state);
313313
}
314314
}
315315

@@ -318,24 +318,24 @@ ReplSet.prototype.connect = function(db, _options, callback) {
318318
self.s.replset.on('left', relay('left'));
319319
self.s.replset.on('ping', relay('ping'));
320320
self.s.replset.on('ha', relayHa);
321-
321+
322322
self.s.replset.on('fullsetup', function(topology) {
323323
self.emit('fullsetup', null, self);
324324
});
325-
325+
326326
self.s.replset.on('all', function(topology) {
327327
self.emit('all', null, self);
328328
});
329329

330330
// Emit open event
331-
self.emit('open', null, self);
331+
self.emit('open', null, self);
332332

333333
// Return correctly
334334
try {
335335
callback(null, self);
336-
} catch(err) {
336+
} catch(err) {
337337
process.nextTick(function() { throw err; })
338-
}
338+
}
339339
}
340340

341341
// Error handler
@@ -352,12 +352,12 @@ ReplSet.prototype.connect = function(db, _options, callback) {
352352
// Try to callback
353353
try {
354354
callback(err);
355-
} catch(err) {
355+
} catch(err) {
356356
if(!self.s.replset.isConnected())
357357
process.nextTick(function() { throw err; })
358358
}
359359
}
360-
}
360+
}
361361

362362
// Set up listeners
363363
self.s.replset.once('timeout', connectErrorHandler('timeout'));
@@ -367,11 +367,12 @@ ReplSet.prototype.connect = function(db, _options, callback) {
367367

368368
// Start connection
369369
self.s.replset.connect(_options);
370-
}
370+
}
371371

372372
// Server capabilities
373373
ReplSet.prototype.capabilities = function() {
374374
if(this.s.sCapabilities) return this.s.sCapabilities;
375+
if(this.s.replset.lastIsMaster() == null) throw new MongoError('cannot establish topology capabilities as driver is still in process of connecting');
375376
this.s.sCapabilities = new ServerCapabilities(this.s.replset.lastIsMaster());
376377
return this.s.sCapabilities;
377378
}
@@ -404,7 +405,7 @@ ReplSet.prototype.isConnected = function() {
404405

405406
ReplSet.prototype.setBSONParserType = function(type) {
406407
return this.s.replset.setBSONParserType(type);
407-
}
408+
}
408409

409410
// Insert
410411
ReplSet.prototype.cursor = function(ns, cmd, options) {
@@ -429,7 +430,7 @@ ReplSet.prototype.close = function(forceClosed) {
429430
var events = ['timeout', 'error', 'close', 'joined', 'left'];
430431
events.forEach(function(e) {
431432
self.removeAllListeners(e);
432-
});
433+
});
433434
}
434435

435436
ReplSet.prototype.auth = function() {
@@ -444,7 +445,7 @@ ReplSet.prototype.auth = function() {
444445
*/
445446
ReplSet.prototype.connections = function() {
446447
return this.s.replset.connections();
447-
}
448+
}
448449

449450
/**
450451
* A replset connect event, used to verify that the connection is up and running
@@ -524,4 +525,4 @@ ReplSet.prototype.connections = function() {
524525
* @type {object}
525526
*/
526527

527-
module.exports = ReplSet;
528+
module.exports = ReplSet;

lib/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Server.prototype.connect = function(db, _options, callback) {
297297
// Server capabilities
298298
Server.prototype.capabilities = function() {
299299
if(this.s.sCapabilities) return this.s.sCapabilities;
300+
if(this.s.server.lastIsMaster() == null) throw new MongoError('cannot establish topology capabilities as driver is still in process of connecting');
300301
this.s.sCapabilities = new ServerCapabilities(this.s.server.lastIsMaster());
301302
return this.s.sCapabilities;
302303
}

0 commit comments

Comments
 (0)