Socket
Socket
Sign inDemoInstall

mongodb-core

Package Overview
Dependencies
2
Maintainers
2
Versions
177
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.17 to 3.0.0-rc0

lib/sessions.js

68

HISTORY.md

@@ -1,5 +0,67 @@

2.1.17 2017-10-11
-----------------
* fix a typo that completely broke SCRAM-SHA1 authentication
<a name="3.0.0-rc0"></a>
# 3.0.0-rc0 (2017-12-05)
### Bug Fixes
* **auth-plain:** only use BSON -after- requiring it ([4934adf](https://github.com/christkv/mongodb-core/commit/4934adf))
* **auth-scram:** cache the ScramSHA1 salted passwords up to 200 entries ([31ef03a](https://github.com/christkv/mongodb-core/commit/31ef03a))
* **client-session:** don't report errors for endSessions commands ([c34eaf5](https://github.com/christkv/mongodb-core/commit/c34eaf5))
* **connection:** default `family` to undefined rather than 4 ([c1b5e04](https://github.com/christkv/mongodb-core/commit/c1b5e04))
* **connection:** fixing leak in 3.0.0 ([#235](https://github.com/christkv/mongodb-core/issues/235)) ([fc669c0](https://github.com/christkv/mongodb-core/commit/fc669c0))
* **cursor:** avoid waiting for reconnect if reconnect disabled ([43e9b23](https://github.com/christkv/mongodb-core/commit/43e9b23))
* **cursor:** callback with server response on `_find` ([6999459](https://github.com/christkv/mongodb-core/commit/6999459))
* **errors:** export MongoError and MongoNetworkError at top-level ([972064a](https://github.com/christkv/mongodb-core/commit/972064a))
* **errors:** throw MongoNetworkError from more places ([2cec239](https://github.com/christkv/mongodb-core/commit/2cec239))
* **errors:** use subclassing for MongoNetworkError ([a132830](https://github.com/christkv/mongodb-core/commit/a132830))
* **errors:** use util.inherits() and protect edge case ([c953246](https://github.com/christkv/mongodb-core/commit/c953246))
* **mocha_server_tests:** rename confusing variable to fix tests ([a9fbae2](https://github.com/christkv/mongodb-core/commit/a9fbae2))
* **mock-tests:** ensure all servers are properly cleaned up ([5dafc86](https://github.com/christkv/mongodb-core/commit/5dafc86))
* **package:** upgrade mongodb-test-runner with bug fix ([5b2e99e](https://github.com/christkv/mongodb-core/commit/5b2e99e))
* **pool:** check topology exists before verifying session support ([0aa146d](https://github.com/christkv/mongodb-core/commit/0aa146d))
* **pool:** ensure inUse and connecting queues are cleared on reauth ([aa2840d](https://github.com/christkv/mongodb-core/commit/aa2840d))
* **pool:** ensure that errors are propagated on force destroy ([8f8ad56](https://github.com/christkv/mongodb-core/commit/8f8ad56))
* **pool:** ensure workItem is not null before accessing properties ([2143963](https://github.com/christkv/mongodb-core/commit/2143963))
* **pool_tests:** remove .only ([8172137](https://github.com/christkv/mongodb-core/commit/8172137))
* **retryable-writes:** don't increment `txnNumber` on retries ([e7c2242](https://github.com/christkv/mongodb-core/commit/e7c2242))
* **retryable-writes:** network errors are retryable, inverted logic ([2727551](https://github.com/christkv/mongodb-core/commit/2727551))
* **scram:** cache salted data, not the original data ([0cbe95f](https://github.com/christkv/mongodb-core/commit/0cbe95f))
* **SDAM:** emit SDAM events on close and reconnect ([3451ff0](https://github.com/christkv/mongodb-core/commit/3451ff0))
* **server:** avoid waiting for reconnect if reconnect disabled ([611a352](https://github.com/christkv/mongodb-core/commit/611a352))
* **server:** correct minor typo in porting 2.x patch ([d92efec](https://github.com/christkv/mongodb-core/commit/d92efec))
* **server_tests:** change 'this' to 'self' in some server tests ([992d9e9](https://github.com/christkv/mongodb-core/commit/992d9e9))
* **server_tests:** fix errors in broken test ([1602e4d](https://github.com/christkv/mongodb-core/commit/1602e4d))
* **server-session-pool:** don't add expired sessions to the pool ([8f48b89](https://github.com/christkv/mongodb-core/commit/8f48b89))
* **server-session-pool:** ensure the queue is LIFO ([ac68e76](https://github.com/christkv/mongodb-core/commit/ac68e76))
* **utils:** don't throw if no snappy ([55bf2ad](https://github.com/christkv/mongodb-core/commit/55bf2ad))
* **wire-protocol:** 2.6 killCursor should not way for reply ([7337d91](https://github.com/christkv/mongodb-core/commit/7337d91))
### Features
* **cluster-time:** ensure clusterTime makes it to outgoing commands ([e700b79](https://github.com/christkv/mongodb-core/commit/e700b79))
* **cluster-time:** track incoming cluster time gossiping ([c910706](https://github.com/christkv/mongodb-core/commit/c910706))
* **compression:** implement wire protocol compression support ([2356ffb](https://github.com/christkv/mongodb-core/commit/2356ffb))
* **connection-spy:** add class for monitoring active connections ([6dd6db3](https://github.com/christkv/mongodb-core/commit/6dd6db3))
* **errors:** create MongoNetworkError ([df12740](https://github.com/christkv/mongodb-core/commit/df12740))
* **inital-cluster-time:** allow session to define initia value ([e3a1c8b](https://github.com/christkv/mongodb-core/commit/e3a1c8b))
* **mock:** support a means of consistently cleaning up mock servers ([ab3b70b](https://github.com/christkv/mongodb-core/commit/ab3b70b))
* **mock-server:** add the ability to set a message handler ([9a8b815](https://github.com/christkv/mongodb-core/commit/9a8b815))
* **operation-time:** track operationTime in relevant sessions ([8d512f1](https://github.com/christkv/mongodb-core/commit/8d512f1))
* **pool:** introduce the concept of a minimum pool size ([b01b1f8](https://github.com/christkv/mongodb-core/commit/b01b1f8))
* **replset:** more verbose replica set errors emission ([6d5eccd](https://github.com/christkv/mongodb-core/commit/6d5eccd))
* **retryable-writes:** add mongos support for retryable writes ([7778067](https://github.com/christkv/mongodb-core/commit/7778067))
* **retryable-writes:** initial support on replicasets ([73ac688](https://github.com/christkv/mongodb-core/commit/73ac688))
* **retryable-writes:** retry on "not master" stepdown errors ([028aec7](https://github.com/christkv/mongodb-core/commit/028aec7))
* **server-check:** reintroduce server version check ([486aace](https://github.com/christkv/mongodb-core/commit/486aace))
* **server-session-pool:** implement session pool per spect ([a1d5b22](https://github.com/christkv/mongodb-core/commit/a1d5b22))
* **session:** allow `session` options to be passed to write cmds ([5da75e4](https://github.com/christkv/mongodb-core/commit/5da75e4))
* **sessions:** add equality operator to ease readability ([6510d7d](https://github.com/christkv/mongodb-core/commit/6510d7d))
* **sessions:** export all sessions types on the top level ([35265b3](https://github.com/christkv/mongodb-core/commit/35265b3))
* **sessions:** support sessions with cursors with find/getMore ([a016602](https://github.com/christkv/mongodb-core/commit/a016602))
* **sessions:** track `logicalSessionTimeoutMinutes` for sessions ([11865bf](https://github.com/christkv/mongodb-core/commit/11865bf))
* **ssl:** adding ssl options ciphers and ecdhCurve ([c839d5c](https://github.com/christkv/mongodb-core/commit/c839d5c))
* **test/:** convert server_tests, undefined_tests, replset_tests, replset_state_tests, and repleset_server_selection_tests to run with mongodb-test-runner ([3a7c5fd](https://github.com/christkv/mongodb-core/commit/3a7c5fd))
2.1.16 2017-10-11

@@ -6,0 +68,0 @@ -----------------

42

index.js

@@ -0,1 +1,3 @@

'use strict';
var BSON = require('bson');

@@ -9,25 +11,27 @@ var require_optional = require('require_optional');

// Javascript one
if(BSONNative) {
BSON = BSONNative
if (BSONNative) {
BSON = BSONNative;
}
} catch(err) {}
} catch (err) {} // eslint-disable-line
module.exports = {
MongoError: require('./lib/error')
, Connection: require('./lib/connection/connection')
, Server: require('./lib/topologies/server')
, ReplSet: require('./lib/topologies/replset')
, Mongos: require('./lib/topologies/mongos')
, Logger: require('./lib/connection/logger')
, Cursor: require('./lib/cursor')
, ReadPreference: require('./lib/topologies/read_preference')
, BSON: BSON
MongoError: require('./lib/error').MongoError,
MongoNetworkError: require('./lib/error').MongoNetworkError,
Connection: require('./lib/connection/connection'),
Server: require('./lib/topologies/server'),
ReplSet: require('./lib/topologies/replset'),
Mongos: require('./lib/topologies/mongos'),
Logger: require('./lib/connection/logger'),
Cursor: require('./lib/cursor'),
ReadPreference: require('./lib/topologies/read_preference'),
Sessions: require('./lib/sessions'),
BSON: BSON,
// Raw operations
, Query: require('./lib/connection/commands').Query
Query: require('./lib/connection/commands').Query,
// Auth mechanisms
, MongoCR: require('./lib/auth/mongocr')
, X509: require('./lib/auth/x509')
, Plain: require('./lib/auth/plain')
, GSSAPI: require('./lib/auth/gssapi')
, ScramSHA1: require('./lib/auth/scram')
}
MongoCR: require('./lib/auth/mongocr'),
X509: require('./lib/auth/x509'),
Plain: require('./lib/auth/plain'),
GSSAPI: require('./lib/auth/gssapi'),
ScramSHA1: require('./lib/auth/scram')
};

@@ -1,7 +0,7 @@

"use strict";
'use strict';
var f = require('util').format
, require_optional = require('require_optional')
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var f = require('util').format,
require_optional = require('require_optional'),
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;

@@ -13,9 +13,11 @@ var AuthSession = function(db, username, password, options) {

this.options = options;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -31,4 +33,3 @@ // Kerberos class

MongoAuthProcess = require_optional('kerberos').processes.MongoAuthProcess;
} catch(err) {
}
} catch (err) {} // eslint-disable-line

@@ -43,3 +44,3 @@ /**

this.authStore = [];
}
};

@@ -60,7 +61,7 @@ /**

// We don't have the Kerberos library
if(Kerberos == null) return callback(new Error("Kerberos library is not installed"));
if (Kerberos == null) return callback(new Error('Kerberos library is not installed'));
var gssapiServiceName = options['gssapiServiceName'] || 'mongodb';
// Total connections
var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -72,33 +73,45 @@ // Valid connections

// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
// Execute MongoCR
var execute = function(connection) {
// Start Auth process for a connection
GSSAPIInitialize(self, db, username, password, db, gssapiServiceName, server, connection, options, function(err, r) {
// Adjust count
count = count - 1;
GSSAPIInitialize(
self,
db,
username,
password,
db,
gssapiServiceName,
server,
connection,
options,
function(err, r) {
// Adjust count
count = count - 1;
// If we have an error
if(err) {
errorObject = err;
} else if(r.result['$err']) {
errorObject = r.result;
} else if(r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// If we have an error
if (err) {
errorObject = err;
} else if (r.result['$err']) {
errorObject = r.result;
} else if (r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// We have authenticated all connections
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password, options));
// Return correct authentication
callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
callback(errorObject, false);
// We have authenticated all connections
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password, options));
// Return correct authentication
callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using mongocr'));
callback(errorObject, false);
}
}
});
}
);
};

@@ -109,63 +122,130 @@ var _execute = function(_connection) {

});
}
};
_execute(connections.shift());
}
}
};
//
// Initialize step
var GSSAPIInitialize = function(self, db, username, password, authdb, gssapiServiceName, server, connection, options, callback) {
var GSSAPIInitialize = function(
self,
db,
username,
password,
authdb,
gssapiServiceName,
server,
connection,
options,
callback
) {
// Create authenticator
var mongo_auth_process = new MongoAuthProcess(connection.host, connection.port, gssapiServiceName, options);
var mongo_auth_process = new MongoAuthProcess(
connection.host,
connection.port,
gssapiServiceName,
options
);
// Perform initialization
mongo_auth_process.init(username, password, function(err) {
if(err) return callback(err, false);
if (err) return callback(err, false);
// Perform the first step
mongo_auth_process.transition('', function(err, payload) {
if(err) return callback(err, false);
if (err) return callback(err, false);
// Call the next db step
MongoDBGSSAPIFirstStep(self, mongo_auth_process, payload, db, username, password, authdb, server, connection, callback);
MongoDBGSSAPIFirstStep(
self,
mongo_auth_process,
payload,
db,
username,
password,
authdb,
server,
connection,
callback
);
});
});
}
};
//
// Perform first step against mongodb
var MongoDBGSSAPIFirstStep = function(self, mongo_auth_process, payload, db, username, password, authdb, server, connection, callback) {
var MongoDBGSSAPIFirstStep = function(
self,
mongo_auth_process,
payload,
db,
username,
password,
authdb,
server,
connection,
callback
) {
// Build the sasl start command
var command = {
saslStart: 1
, mechanism: 'GSSAPI'
, payload: payload
, autoAuthorize: 1
saslStart: 1,
mechanism: 'GSSAPI',
payload: payload,
autoAuthorize: 1
};
// Write the commmand on the connection
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
var doc = r.result;
// Execute mongodb transition
mongo_auth_process.transition(r.result.payload, function(err, payload) {
if(err) return callback(err, false);
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
// Execute mongodb transition
mongo_auth_process.transition(r.result.payload, function(err, payload) {
if (err) return callback(err, false);
// MongoDB API Second Step
MongoDBGSSAPISecondStep(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
});
});
}
// MongoDB API Second Step
MongoDBGSSAPISecondStep(
self,
mongo_auth_process,
payload,
doc,
db,
username,
password,
authdb,
server,
connection,
callback
);
});
}
);
};
//
// Perform first step against mongodb
var MongoDBGSSAPISecondStep = function(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
var MongoDBGSSAPISecondStep = function(
self,
mongo_auth_process,
payload,
doc,
db,
username,
password,
authdb,
server,
connection,
callback
) {
// Build Authentication command to send to MongoDB
var command = {
saslContinue: 1
, conversationId: doc.conversationId
, payload: payload
saslContinue: 1,
conversationId: doc.conversationId,
payload: payload
};

@@ -175,36 +255,70 @@

// Write the commmand on the connection
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
var doc = r.result;
// Call next transition for kerberos
mongo_auth_process.transition(doc.payload, function(err, payload) {
if(err) return callback(err, false);
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
// Call next transition for kerberos
mongo_auth_process.transition(doc.payload, function(err, payload) {
if (err) return callback(err, false);
// Call the last and third step
MongoDBGSSAPIThirdStep(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
});
});
}
// Call the last and third step
MongoDBGSSAPIThirdStep(
self,
mongo_auth_process,
payload,
doc,
db,
username,
password,
authdb,
server,
connection,
callback
);
});
}
);
};
var MongoDBGSSAPIThirdStep = function(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
var MongoDBGSSAPIThirdStep = function(
self,
mongo_auth_process,
payload,
doc,
db,
username,
password,
authdb,
server,
connection,
callback
) {
// Build final command
var command = {
saslContinue: 1
, conversationId: doc.conversationId
, payload: payload
saslContinue: 1,
conversationId: doc.conversationId,
payload: payload
};
// Execute the command
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
mongo_auth_process.transition(null, function(err) {
if(err) return callback(err, null);
callback(null, r);
});
});
}
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
mongo_auth_process.transition(null, function(err) {
if (err) return callback(err, null);
callback(null, r);
});
}
);
};

@@ -215,4 +329,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -223,4 +337,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -235,5 +349,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -251,14 +365,22 @@ /**

var count = authStore.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, authStore[i].options, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
authStore[i].options,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};

@@ -265,0 +387,0 @@ /**

@@ -1,7 +0,7 @@

"use strict";
'use strict';
var f = require('util').format
, crypto = require('crypto')
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var f = require('util').format,
crypto = require('crypto'),
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;

@@ -12,9 +12,11 @@ var AuthSession = function(db, username, password) {

this.password = password;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -29,3 +31,3 @@ /**

this.authStore = [];
}
};

@@ -36,4 +38,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -44,4 +46,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -63,3 +65,3 @@ /**

var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -71,62 +73,86 @@ // Valid connections

// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
// Execute MongoCR
var executeMongoCR = function(connection) {
// Write the commmand on the connection
server(connection, new Query(self.bson, f("%s.$cmd", db), {
getnonce:1
}, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
var nonce = null;
var key = null;
server(
connection,
new Query(
self.bson,
f('%s.$cmd', db),
{
getnonce: 1
},
{
numberToSkip: 0,
numberToReturn: 1
}
),
function(err, r) {
var nonce = null;
var key = null;
// Adjust the number of connections left
// Get nonce
if(err == null) {
nonce = r.result.nonce;
// Use node md5 generator
var md5 = crypto.createHash('md5');
// Generate keys used for authentication
md5.update(username + ":mongo:" + password, 'utf8');
var hash_password = md5.digest('hex');
// Final key
md5 = crypto.createHash('md5');
md5.update(nonce + username + hash_password, 'utf8');
key = md5.digest('hex');
}
// Adjust the number of connections left
// Get nonce
if (err == null) {
nonce = r.result.nonce;
// Use node md5 generator
var md5 = crypto.createHash('md5');
// Generate keys used for authentication
md5.update(username + ':mongo:' + password, 'utf8');
var hash_password = md5.digest('hex');
// Final key
md5 = crypto.createHash('md5');
md5.update(nonce + username + hash_password, 'utf8');
key = md5.digest('hex');
}
// Execute command
// Write the commmand on the connection
server(connection, new Query(self.bson, f("%s.$cmd", db), {
authenticate: 1, user: username, nonce: nonce, key:key
}, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
count = count - 1;
// Execute command
// Write the commmand on the connection
server(
connection,
new Query(
self.bson,
f('%s.$cmd', db),
{
authenticate: 1,
user: username,
nonce: nonce,
key: key
},
{
numberToSkip: 0,
numberToReturn: 1
}
),
function(err, r) {
count = count - 1;
// If we have an error
if(err) {
errorObject = err;
} else if(r.result['$err']) {
errorObject = r.result;
} else if(r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// If we have an error
if (err) {
errorObject = err;
} else if (r.result['$err']) {
errorObject = r.result;
} else if (r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// We have authenticated all connections
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
callback(errorObject, false);
}
});
});
}
// We have authenticated all connections
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using mongocr'));
callback(errorObject, false);
}
}
);
}
);
};

@@ -137,7 +163,7 @@ var _execute = function(_connection) {

});
}
};
_execute(connections.shift());
}
}
};

@@ -152,5 +178,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -168,14 +194,21 @@ /**

var count = authStore.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};

@@ -182,0 +215,0 @@ /**

@@ -1,13 +0,11 @@

"use strict";
'use strict';
var BSON = require('bson');
var f = require('util').format,
retrieveBSON = require('../connection/utils').retrieveBSON,
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;
var f = require('util').format
, Binary = BSON.Binary
, retrieveBSON = require('../connection/utils').retrieveBSON
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var BSON = retrieveBSON(),
Binary = BSON.Binary;
var BSON = retrieveBSON();
var AuthSession = function(db, username, password) {

@@ -17,9 +15,11 @@ this.db = db;

this.password = password;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -34,3 +34,3 @@ /**

this.authStore = [];
}
};

@@ -52,3 +52,3 @@ /**

var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -60,46 +60,52 @@ // Valid connections

// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
// Execute MongoCR
var execute = function(connection) {
// Create payload
var payload = new Binary(f("\x00%s\x00%s", username, password));
var payload = new Binary(f('\x00%s\x00%s', username, password));
// Let's start the sasl process
var command = {
saslStart: 1
, mechanism: 'PLAIN'
, payload: payload
, autoAuthorize: 1
saslStart: 1,
mechanism: 'PLAIN',
payload: payload,
autoAuthorize: 1
};
// Let's start the process
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
// Adjust count
count = count - 1;
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
// Adjust count
count = count - 1;
// If we have an error
if(err) {
errorObject = err;
} else if(r.result['$err']) {
errorObject = r.result;
} else if(r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// If we have an error
if (err) {
errorObject = err;
} else if (r.result['$err']) {
errorObject = r.result;
} else if (r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// We have authenticated all connections
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
callback(errorObject, false);
// We have authenticated all connections
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using mongocr'));
callback(errorObject, false);
}
}
});
}
);
};

@@ -110,7 +116,7 @@ var _execute = function(_connection) {

});
}
};
_execute(connections.shift());
}
}
};

@@ -121,4 +127,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -129,4 +135,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -141,5 +147,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -157,14 +163,21 @@ /**

var count = authStore.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};

@@ -171,0 +184,0 @@ /**

@@ -1,8 +0,8 @@

"use strict";
'use strict';
var f = require('util').format
, crypto = require('crypto')
, retrieveBSON = require('../connection/utils').retrieveBSON
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var f = require('util').format,
crypto = require('crypto'),
retrieveBSON = require('../connection/utils').retrieveBSON,
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;

@@ -16,9 +16,11 @@ var BSON = retrieveBSON(),

this.password = password;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -36,3 +38,3 @@ var id = 0;

this.id = id++;
}
};

@@ -43,3 +45,3 @@ var parsePayload = function(payload) {

for(var i = 0; i < parts.length; i++) {
for (var i = 0; i < parts.length; i++) {
var valueParts = parts[i].split('=');

@@ -50,31 +52,31 @@ dict[valueParts[0]] = valueParts[1];

return dict;
}
};
var passwordDigest = function(username, password) {
if(typeof username != 'string') throw new MongoError("username must be a string");
if(typeof password != 'string') throw new MongoError("password must be a string");
if(password.length == 0) throw new MongoError("password cannot be empty");
if (typeof username !== 'string') throw new MongoError('username must be a string');
if (typeof password !== 'string') throw new MongoError('password must be a string');
if (password.length === 0) throw new MongoError('password cannot be empty');
// Use node md5 generator
var md5 = crypto.createHash('md5');
// Generate keys used for authentication
md5.update(username + ":mongo:" + password, 'utf8');
md5.update(username + ':mongo:' + password, 'utf8');
return md5.digest('hex');
}
};
// XOR two buffers
var xor = function(a, b) {
if (!Buffer.isBuffer(a)) a = new Buffer(a)
if (!Buffer.isBuffer(b)) b = new Buffer(b)
var res = []
if (!Buffer.isBuffer(a)) a = new Buffer(a);
if (!Buffer.isBuffer(b)) b = new Buffer(b);
var res = [];
if (a.length > b.length) {
for (var i = 0; i < b.length; i++) {
res.push(a[i] ^ b[i])
res.push(a[i] ^ b[i]);
}
} else {
for (i = 0; i < a.length; i++) {
res.push(a[i] ^ b[i])
res.push(a[i] ^ b[i]);
}
}
return new Buffer(res);
}
};

@@ -96,3 +98,3 @@ var _hiCache = {};

// generate the salt
var saltedData = crypto.pbkdf2Sync(data, salt, iterations, 20, "sha1");
var saltedData = crypto.pbkdf2Sync(data, salt, iterations, 20, 'sha1');

@@ -124,3 +126,3 @@ // cache a copy to speed up the next lookup, but prevent unbounded cache growth

var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -134,3 +136,3 @@ // Valid connections

// Clean up the user
username = username.replace('=', "=3D").replace(',', '=2C');
username = username.replace('=', '=3D').replace(',', '=2C');

@@ -140,21 +142,24 @@ // Create a random nonce

// var nonce = 'MsQUY9iw0T9fx2MUEz6LZPwGuhVvWAhc'
var firstBare = f("n=%s,r=%s", username, nonce);
var firstBare = f('n=%s,r=%s', username, nonce);
// Build command structure
var cmd = {
saslStart: 1
, mechanism: 'SCRAM-SHA-1'
, payload: new Binary(f("n,,%s", firstBare))
, autoAuthorize: 1
}
saslStart: 1,
mechanism: 'SCRAM-SHA-1',
payload: new Binary(f('n,,%s', firstBare)),
autoAuthorize: 1
};
// Handle the error
var handleError = function(err, r) {
if(err) {
if (err) {
numberOfValidConnections = numberOfValidConnections - 1;
errorObject = err; return false;
} else if(r.result['$err']) {
errorObject = r.result; return false;
} else if(r.result['errmsg']) {
errorObject = r.result; return false;
errorObject = err;
return false;
} else if (r.result['$err']) {
errorObject = r.result;
return false;
} else if (r.result['errmsg']) {
errorObject = r.result;
return false;
} else {

@@ -164,8 +169,8 @@ numberOfValidConnections = numberOfValidConnections + 1;

return true
}
return true;
};
// Finish up
var finish = function(_count, _numberOfValidConnections) {
if(_count == 0 && _numberOfValidConnections > 0) {
if (_count === 0 && _numberOfValidConnections > 0) {
// Store the auth details

@@ -175,11 +180,12 @@ addAuthSession(self.authStore, new AuthSession(db, username, password));

return callback(null, true);
} else if(_count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using scram"));
} else if (_count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using scram'));
return callback(errorObject, false);
}
}
};
var handleEnd = function(_err, _r) {
// Handle any error
handleError(_err, _r)
handleError(_err, _r);
// Adjust the number of connections

@@ -189,98 +195,114 @@ count = count - 1;

finish(count, numberOfValidConnections);
}
};
// Write the commmand on the connection
server(connection, new Query(self.bson, f("%s.$cmd", db), cmd, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
// Do we have an error, handle it
if(handleError(err, r) == false) {
count = count - 1;
server(
connection,
new Query(self.bson, f('%s.$cmd', db), cmd, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
// Do we have an error, handle it
if (handleError(err, r) === false) {
count = count - 1;
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
return callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using scram"));
return callback(errorObject, false);
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
return callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using scram'));
return callback(errorObject, false);
}
return;
}
return;
}
// Get the dictionary
var dict = parsePayload(r.result.payload.value());
// Get the dictionary
var dict = parsePayload(r.result.payload.value())
// Unpack dictionary
var iterations = parseInt(dict.i, 10);
var salt = dict.s;
var rnonce = dict.r;
// Unpack dictionary
var iterations = parseInt(dict.i, 10);
var salt = dict.s;
var rnonce = dict.r;
// Set up start of proof
var withoutProof = f('c=biws,r=%s', rnonce);
var passwordDig = passwordDigest(username, password);
var saltedPassword = hi(passwordDig, new Buffer(salt, 'base64'), iterations);
// Set up start of proof
var withoutProof = f("c=biws,r=%s", rnonce);
var passwordDig = passwordDigest(username, password);
var saltedPassword = hi(passwordDig
, new Buffer(salt, 'base64')
, iterations);
// Create the client key
var hmac = crypto.createHmac('sha1', saltedPassword);
hmac.update(new Buffer('Client Key'));
var clientKey = new Buffer(hmac.digest('base64'), 'base64');
// Create the client key
var hmac = crypto.createHmac('sha1', saltedPassword);
hmac.update(new Buffer("Client Key"));
var clientKey = new Buffer(hmac.digest('base64'), 'base64');
// Create the stored key
var hash = crypto.createHash('sha1');
hash.update(clientKey);
var storedKey = new Buffer(hash.digest('base64'), 'base64');
// Create the stored key
var hash = crypto.createHash('sha1');
hash.update(clientKey);
var storedKey = new Buffer(hash.digest('base64'), 'base64');
// Create the authentication message
var authMsg = [firstBare, r.result.payload.value().toString('base64'), withoutProof].join(
','
);
// Create the authentication message
var authMsg = [firstBare, r.result.payload.value().toString('base64'), withoutProof].join(',');
// Create client signature
hmac = crypto.createHmac('sha1', storedKey);
hmac.update(new Buffer(authMsg));
var clientSig = new Buffer(hmac.digest('base64'), 'base64');
// Create client signature
hmac = crypto.createHmac('sha1', storedKey);
hmac.update(new Buffer(authMsg));
var clientSig = new Buffer(hmac.digest('base64'), 'base64');
// Create client proof
var clientProof = f('p=%s', new Buffer(xor(clientKey, clientSig)).toString('base64'));
// Create client proof
var clientProof = f("p=%s", new Buffer(xor(clientKey, clientSig)).toString('base64'));
// Create client final
var clientFinal = [withoutProof, clientProof].join(',');
// Create client final
var clientFinal = [withoutProof, clientProof].join(',');
//
// Create continue message
var cmd = {
saslContinue: 1,
conversationId: r.result.conversationId,
payload: new Binary(new Buffer(clientFinal))
};
//
// Create continue message
var cmd = {
saslContinue: 1
, conversationId: r.result.conversationId
, payload: new Binary(new Buffer(clientFinal))
}
//
// Execute sasl continue
// Write the commmand on the connection
server(
connection,
new Query(self.bson, f('%s.$cmd', db), cmd, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (r && r.result.done === false) {
var cmd = {
saslContinue: 1,
conversationId: r.result.conversationId,
payload: new Buffer(0)
};
//
// Execute sasl continue
// Write the commmand on the connection
server(connection, new Query(self.bson, f("%s.$cmd", db), cmd, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(r && r.result.done == false) {
var cmd = {
saslContinue: 1
, conversationId: r.result.conversationId
, payload: new Buffer(0)
// Write the commmand on the connection
server(
connection,
new Query(self.bson, f('%s.$cmd', db), cmd, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
handleEnd(err, r);
}
);
} else {
handleEnd(err, r);
}
}
);
}
);
};
// Write the commmand on the connection
server(connection, new Query(self.bson, f("%s.$cmd", db), cmd, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
handleEnd(err, r);
});
} else {
handleEnd(err, r);
}
});
});
}
var _execute = function(_connection) {

@@ -290,9 +312,9 @@ process.nextTick(function() {

});
}
};
// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
_execute(connections.shift());
}
}
};

@@ -303,4 +325,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -311,4 +333,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -323,5 +345,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -340,16 +362,22 @@ /**

// No connections
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};
module.exports = ScramSHA1;

@@ -1,7 +0,7 @@

"use strict";
'use strict';
var f = require('util').format
, require_optional = require('require_optional')
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var f = require('util').format,
require_optional = require('require_optional'),
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;

@@ -13,9 +13,11 @@ var AuthSession = function(db, username, password, options) {

this.options = options;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -28,6 +30,6 @@ // Kerberos class

try {
Kerberos = require_optional('kerberos').Kerberos
Kerberos = require_optional('kerberos').Kerberos;
// Authentication process for Mongo
MongoAuthProcess = require_optional('kerberos').processes.MongoAuthProcess
} catch(err) {}
MongoAuthProcess = require_optional('kerberos').processes.MongoAuthProcess;
} catch (err) {} // eslint-disable-line

@@ -42,3 +44,3 @@ /**

this.authStore = [];
}
};

@@ -59,7 +61,7 @@ /**

// We don't have the Kerberos library
if(Kerberos == null) return callback(new Error("Kerberos library is not installed"));
if (Kerberos == null) return callback(new Error('Kerberos library is not installed'));
var gssapiServiceName = options['gssapiServiceName'] || 'mongodb';
// Total connections
var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -71,33 +73,43 @@ // Valid connections

// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
// Execute MongoCR
var execute = function(connection) {
// Start Auth process for a connection
SSIPAuthenticate(self, username, password, gssapiServiceName, server, connection, options, function(err, r) {
// Adjust count
count = count - 1;
SSIPAuthenticate(
self,
username,
password,
gssapiServiceName,
server,
connection,
options,
function(err, r) {
// Adjust count
count = count - 1;
// If we have an error
if(err) {
errorObject = err;
} else if(r && typeof r == 'object' && r.result['$err']) {
errorObject = r.result;
} else if(r && typeof r == 'object' && r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// If we have an error
if (err) {
errorObject = err;
} else if (r && typeof r === 'object' && r.result['$err']) {
errorObject = r.result;
} else if (r && typeof r === 'object' && r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// We have authenticated all connections
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password, options));
// Return correct authentication
callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
callback(errorObject, false);
// We have authenticated all connections
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password, options));
// Return correct authentication
callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using mongocr'));
callback(errorObject, false);
}
}
});
}
);
};

@@ -108,90 +120,124 @@ var _execute = function(_connection) {

});
}
};
_execute(connections.shift());
}
}
};
var SSIPAuthenticate = function(self, username, password, gssapiServiceName, server, connection, options, callback) {
var SSIPAuthenticate = function(
self,
username,
password,
gssapiServiceName,
server,
connection,
options,
callback
) {
// Build Authentication command to send to MongoDB
var command = {
saslStart: 1
, mechanism: 'GSSAPI'
, payload: ''
, autoAuthorize: 1
saslStart: 1,
mechanism: 'GSSAPI',
payload: '',
autoAuthorize: 1
};
// Create authenticator
var mongo_auth_process = new MongoAuthProcess(connection.host, connection.port, gssapiServiceName, options);
var mongo_auth_process = new MongoAuthProcess(
connection.host,
connection.port,
gssapiServiceName,
options
);
// Execute first sasl step
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
var doc = r.result;
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
mongo_auth_process.init(username, password, function(err) {
if(err) return callback(err);
mongo_auth_process.init(username, password, function(err) {
if (err) return callback(err);
mongo_auth_process.transition(doc.payload, function(err, payload) {
if(err) return callback(err);
mongo_auth_process.transition(doc.payload, function(err, payload) {
if (err) return callback(err);
// Perform the next step against mongod
var command = {
saslContinue: 1
, conversationId: doc.conversationId
, payload: payload
};
// Perform the next step against mongod
var command = {
saslContinue: 1,
conversationId: doc.conversationId,
payload: payload
};
// Execute the command
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
var doc = r.result;
mongo_auth_process.transition(doc.payload, function(err, payload) {
if(err) return callback(err);
// Perform the next step against mongod
var command = {
saslContinue: 1
, conversationId: doc.conversationId
, payload: payload
};
// Execute the command
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
// Execute the command
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
mongo_auth_process.transition(doc.payload, function(err, payload) {
if (err) return callback(err);
// Perform the next step against mongod
var command = {
saslContinue: 1
, conversationId: doc.conversationId
, payload: payload
saslContinue: 1,
conversationId: doc.conversationId,
payload: payload
};
// Execute the command
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
if(err) return callback(err, false);
var doc = r.result;
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
if(doc.done) return callback(null, true);
callback(new Error("Authentication failed"), false);
});
mongo_auth_process.transition(doc.payload, function(err, payload) {
// Perform the next step against mongod
var command = {
saslContinue: 1,
conversationId: doc.conversationId,
payload: payload
};
// Execute the command
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
if (err) return callback(err, false);
var doc = r.result;
if (doc.done) return callback(null, true);
callback(new Error('Authentication failed'), false);
}
);
});
}
);
});
});
});
}
);
});
});
});
});
}
}
);
};

@@ -202,4 +248,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -210,4 +256,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -222,5 +268,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -238,14 +284,22 @@ /**

var count = authStore.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, authStore[i].options, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
authStore[i].options,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};

@@ -252,0 +306,0 @@ /**

@@ -1,6 +0,6 @@

"use strict";
'use strict';
var f = require('util').format
, Query = require('../connection/commands').Query
, MongoError = require('../error');
var f = require('util').format,
Query = require('../connection/commands').Query,
MongoError = require('../error').MongoError;

@@ -11,9 +11,11 @@ var AuthSession = function(db, username, password) {

this.password = password;
}
};
AuthSession.prototype.equal = function(session) {
return session.db == this.db
&& session.username == this.username
&& session.password == this.password;
}
return (
session.db === this.db &&
session.username === this.username &&
session.password === this.password
);
};

@@ -28,3 +30,3 @@ /**

this.authStore = [];
}
};

@@ -46,3 +48,3 @@ /**

var count = connections.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);

@@ -54,3 +56,3 @@ // Valid connections

// For each connection we need to authenticate
while(connections.length > 0) {
while (connections.length > 0) {
// Execute MongoCR

@@ -60,8 +62,8 @@ var execute = function(connection) {

var command = {
authenticate: 1
, mechanism: 'MONGODB-X509'
authenticate: 1,
mechanism: 'MONGODB-X509'
};
// Add username if specified
if(username) {
if (username) {
command.user = username;

@@ -71,31 +73,37 @@ }

// Let's start the process
server(connection, new Query(self.bson, "$external.$cmd", command, {
numberToSkip: 0, numberToReturn: 1
}), function(err, r) {
// Adjust count
count = count - 1;
server(
connection,
new Query(self.bson, '$external.$cmd', command, {
numberToSkip: 0,
numberToReturn: 1
}),
function(err, r) {
// Adjust count
count = count - 1;
// If we have an error
if(err) {
errorObject = err;
} else if(r.result['$err']) {
errorObject = r.result;
} else if(r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// If we have an error
if (err) {
errorObject = err;
} else if (r.result['$err']) {
errorObject = r.result;
} else if (r.result['errmsg']) {
errorObject = r.result;
} else {
numberOfValidConnections = numberOfValidConnections + 1;
}
// We have authenticated all connections
if(count == 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if(count == 0) {
if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
callback(errorObject, false);
// We have authenticated all connections
if (count === 0 && numberOfValidConnections > 0) {
// Store the auth details
addAuthSession(self.authStore, new AuthSession(db, username, password));
// Return correct authentication
callback(null, true);
} else if (count === 0) {
if (errorObject == null)
errorObject = new MongoError(f('failed to authenticate using mongocr'));
callback(errorObject, false);
}
}
});
}
);
};

@@ -106,7 +114,7 @@ var _execute = function(_connection) {

});
}
};
_execute(connections.shift());
}
}
};

@@ -117,4 +125,4 @@ // Add to store only if it does not exist

for(var i = 0; i < authStore.length; i++) {
if(authStore[i].equal(session)) {
for (var i = 0; i < authStore.length; i++) {
if (authStore[i].equal(session)) {
found = true;

@@ -125,4 +133,4 @@ break;

if(!found) authStore.push(session);
}
if (!found) authStore.push(session);
};

@@ -137,5 +145,5 @@ /**

this.authStore = this.authStore.filter(function(x) {
return x.db != dbName;
return x.db !== dbName;
});
}
};

@@ -153,14 +161,21 @@ /**

var count = authStore.length;
if(count == 0) return callback(null, null);
if (count === 0) return callback(null, null);
// Iterate over all the auth details stored
for(var i = 0; i < authStore.length; i++) {
this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, function(err) {
count = count - 1;
// Done re-authenticating
if(count == 0) {
callback(err, null);
for (var i = 0; i < authStore.length; i++) {
this.auth(
server,
connections,
authStore[i].db,
authStore[i].username,
authStore[i].password,
function(err) {
count = count - 1;
// Done re-authenticating
if (count === 0) {
callback(err, null);
}
}
});
);
}
}
};

@@ -167,0 +182,0 @@ /**

@@ -1,2 +0,2 @@

"use strict";
'use strict';

@@ -14,3 +14,3 @@ /**

this.message = message;
}
};

@@ -24,3 +24,3 @@ /**

return this.result;
}
};

@@ -34,4 +34,4 @@ /**

return JSON.stringify(this.toJSON());
}
};
module.exports = CommandResult;

@@ -1,4 +0,4 @@

"use strict";
'use strict';
var retrieveBSON = require('../connection/utils').retrieveBSON;
var retrieveBSON = require('./utils').retrieveBSON;
var BSON = retrieveBSON();

@@ -11,5 +11,3 @@ var Long = BSON.Long;

// Wire command operation ids
var OP_QUERY = 2004;
var OP_GETMORE = 2005;
var OP_KILL_CURSORS = 2007;
var opcodes = require('../wireprotocol/shared').opcodes;

@@ -37,8 +35,8 @@ // Query flags

// Basic options needed to be passed in
if(ns == null) throw new Error("ns must be specified for query");
if(query == null) throw new Error("query must be specified for query");
if (ns == null) throw new Error('ns must be specified for query');
if (query == null) throw new Error('query must be specified for query');
// Validate that we are not passing 0x00 in the collection name
if(!!~ns.indexOf("\x00")) {
throw new Error("namespace cannot contain a null character");
if (ns.indexOf('\x00') !== -1) {
throw new Error('namespace cannot contain a null character');
}

@@ -61,6 +59,8 @@

// Serialization option
this.serializeFunctions = typeof options.serializeFunctions == 'boolean' ? options.serializeFunctions : false;
this.ignoreUndefined = typeof options.ignoreUndefined == 'boolean' ? options.ignoreUndefined : false;
this.serializeFunctions =
typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
this.ignoreUndefined =
typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
this.checkKeys = typeof options.checkKeys == 'boolean' ? options.checkKeys : true;
this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
this.batchSize = self.numberToReturn;

@@ -70,3 +70,3 @@

this.tailable = false;
this.slaveOk = typeof options.slaveOk == 'boolean'? options.slaveOk : false;
this.slaveOk = typeof options.slaveOk === 'boolean' ? options.slaveOk : false;
this.oplogReplay = false;

@@ -77,3 +77,3 @@ this.noCursorTimeout = false;

this.partial = false;
}
};

@@ -84,3 +84,3 @@ //

this.requestId = _requestId++;
}
};

@@ -91,3 +91,3 @@ //

return _requestId + 1;
}
};

@@ -103,27 +103,27 @@ //

var flags = 0;
if(this.tailable) {
if (this.tailable) {
flags |= OPTS_TAILABLE_CURSOR;
}
if(this.slaveOk) {
if (this.slaveOk) {
flags |= OPTS_SLAVE;
}
if(this.oplogReplay) {
if (this.oplogReplay) {
flags |= OPTS_OPLOG_REPLAY;
}
if(this.noCursorTimeout) {
if (this.noCursorTimeout) {
flags |= OPTS_NO_CURSOR_TIMEOUT;
}
if(this.awaitData) {
if (this.awaitData) {
flags |= OPTS_AWAIT_DATA;
}
if(this.exhaust) {
if (this.exhaust) {
flags |= OPTS_EXHAUST;
}
if(this.partial) {
if (this.partial) {
flags |= OPTS_PARTIAL;

@@ -133,11 +133,12 @@ }

// If batchSize is different to self.numberToReturn
if(self.batchSize != self.numberToReturn) self.numberToReturn = self.batchSize;
if (self.batchSize !== self.numberToReturn) self.numberToReturn = self.batchSize;
// Allocate write protocol header buffer
var header = new Buffer(
4 * 4 // Header
+ 4 // Flags
+ Buffer.byteLength(self.ns) + 1 // namespace
+ 4 // numberToSkip
+ 4 // numberToReturn
4 * 4 + // Header
4 + // Flags
Buffer.byteLength(self.ns) +
1 + // namespace
4 + // numberToSkip
4 // numberToReturn
);

@@ -152,3 +153,3 @@

serializeFunctions: this.serializeFunctions,
ignoreUndefined: this.ignoreUndefined,
ignoreUndefined: this.ignoreUndefined
});

@@ -159,3 +160,3 @@

if(self.returnFieldSelector && Object.keys(self.returnFieldSelector).length > 0) {
if (self.returnFieldSelector && Object.keys(self.returnFieldSelector).length > 0) {
// Serialize the projection document

@@ -165,3 +166,3 @@ projection = self.bson.serialize(this.returnFieldSelector, {

serializeFunctions: this.serializeFunctions,
ignoreUndefined: this.ignoreUndefined,
ignoreUndefined: this.ignoreUndefined
});

@@ -182,3 +183,3 @@ // Add projection document

header[1] = (totalLength >> 8) & 0xff;
header[0] = (totalLength) & 0xff;
header[0] = totalLength & 0xff;

@@ -189,3 +190,3 @@ // Write header information requestId

header[index + 1] = (this.requestId >> 8) & 0xff;
header[index] = (this.requestId) & 0xff;
header[index] = this.requestId & 0xff;
index = index + 4;

@@ -197,10 +198,10 @@

header[index + 1] = (0 >> 8) & 0xff;
header[index] = (0) & 0xff;
header[index] = 0 & 0xff;
index = index + 4;
// Write header information OP_QUERY
header[index + 3] = (OP_QUERY >> 24) & 0xff;
header[index + 2] = (OP_QUERY >> 16) & 0xff;
header[index + 1] = (OP_QUERY >> 8) & 0xff;
header[index] = (OP_QUERY) & 0xff;
header[index + 3] = (opcodes.OP_QUERY >> 24) & 0xff;
header[index + 2] = (opcodes.OP_QUERY >> 16) & 0xff;
header[index + 1] = (opcodes.OP_QUERY >> 8) & 0xff;
header[index] = opcodes.OP_QUERY & 0xff;
index = index + 4;

@@ -212,3 +213,3 @@

header[index + 1] = (flags >> 8) & 0xff;
header[index] = (flags) & 0xff;
header[index] = flags & 0xff;
index = index + 4;

@@ -224,3 +225,3 @@

header[index + 1] = (this.numberToSkip >> 8) & 0xff;
header[index] = (this.numberToSkip) & 0xff;
header[index] = this.numberToSkip & 0xff;
index = index + 4;

@@ -232,3 +233,3 @@

header[index + 1] = (this.numberToReturn >> 8) & 0xff;
header[index] = (this.numberToReturn) & 0xff;
header[index] = this.numberToReturn & 0xff;
index = index + 4;

@@ -238,7 +239,7 @@

return buffers;
}
};
Query.getRequestId = function() {
return ++_requestId;
}
};

@@ -255,3 +256,3 @@ /**************************************************************

this.cursorId = cursorId;
}
};

@@ -261,3 +262,3 @@ //

GetMore.prototype.toBin = function() {
var length = 4 + Buffer.byteLength(this.ns) + 1 + 4 + 8 + (4 * 4);
var length = 4 + Buffer.byteLength(this.ns) + 1 + 4 + 8 + 4 * 4;
// Create command buffer

@@ -273,3 +274,3 @@ var index = 0;

_buffer[index + 1] = (length >> 8) & 0xff;
_buffer[index] = (length) & 0xff;
_buffer[index] = length & 0xff;
index = index + 4;

@@ -281,3 +282,3 @@

_buffer[index + 1] = (this.requestId >> 8) & 0xff;
_buffer[index] = (this.requestId) & 0xff;
_buffer[index] = this.requestId & 0xff;
index = index + 4;

@@ -289,10 +290,10 @@

_buffer[index + 1] = (0 >> 8) & 0xff;
_buffer[index] = (0) & 0xff;
_buffer[index] = 0 & 0xff;
index = index + 4;
// index = write32bit(index, _buffer, OP_GETMORE);
_buffer[index + 3] = (OP_GETMORE >> 24) & 0xff;
_buffer[index + 2] = (OP_GETMORE >> 16) & 0xff;
_buffer[index + 1] = (OP_GETMORE >> 8) & 0xff;
_buffer[index] = (OP_GETMORE) & 0xff;
_buffer[index + 3] = (opcodes.OP_GETMORE >> 24) & 0xff;
_buffer[index + 2] = (opcodes.OP_GETMORE >> 16) & 0xff;
_buffer[index + 1] = (opcodes.OP_GETMORE >> 8) & 0xff;
_buffer[index] = opcodes.OP_GETMORE & 0xff;
index = index + 4;

@@ -304,3 +305,3 @@

_buffer[index + 1] = (0 >> 8) & 0xff;
_buffer[index] = (0) & 0xff;
_buffer[index] = 0 & 0xff;
index = index + 4;

@@ -317,3 +318,3 @@

_buffer[index + 1] = (this.numberToReturn >> 8) & 0xff;
_buffer[index] = (this.numberToReturn) & 0xff;
_buffer[index] = this.numberToReturn & 0xff;
index = index + 4;

@@ -326,3 +327,3 @@

_buffer[index + 1] = (this.cursorId.getLowBits() >> 8) & 0xff;
_buffer[index] = (this.cursorId.getLowBits()) & 0xff;
_buffer[index] = this.cursorId.getLowBits() & 0xff;
index = index + 4;

@@ -334,3 +335,3 @@

_buffer[index + 1] = (this.cursorId.getHighBits() >> 8) & 0xff;
_buffer[index] = (this.cursorId.getHighBits()) & 0xff;
_buffer[index] = this.cursorId.getHighBits() & 0xff;
index = index + 4;

@@ -340,3 +341,3 @@

return _buffer;
}
};

@@ -349,3 +350,3 @@ /**************************************************************

this.cursorIds = cursorIds;
}
};

@@ -355,3 +356,3 @@ //

KillCursor.prototype.toBin = function() {
var length = 4 + 4 + (4 * 4) + (this.cursorIds.length * 8);
var length = 4 + 4 + 4 * 4 + this.cursorIds.length * 8;

@@ -367,3 +368,3 @@ // Create command buffer

_buffer[index + 1] = (length >> 8) & 0xff;
_buffer[index] = (length) & 0xff;
_buffer[index] = length & 0xff;
index = index + 4;

@@ -375,3 +376,3 @@

_buffer[index + 1] = (this.requestId >> 8) & 0xff;
_buffer[index] = (this.requestId) & 0xff;
_buffer[index] = this.requestId & 0xff;
index = index + 4;

@@ -383,10 +384,10 @@

_buffer[index + 1] = (0 >> 8) & 0xff;
_buffer[index] = (0) & 0xff;
_buffer[index] = 0 & 0xff;
index = index + 4;
// index = write32bit(index, _buffer, OP_KILL_CURSORS);
_buffer[index + 3] = (OP_KILL_CURSORS >> 24) & 0xff;
_buffer[index + 2] = (OP_KILL_CURSORS >> 16) & 0xff;
_buffer[index + 1] = (OP_KILL_CURSORS >> 8) & 0xff;
_buffer[index] = (OP_KILL_CURSORS) & 0xff;
_buffer[index + 3] = (opcodes.OP_KILL_CURSORS >> 24) & 0xff;
_buffer[index + 2] = (opcodes.OP_KILL_CURSORS >> 16) & 0xff;
_buffer[index + 1] = (opcodes.OP_KILL_CURSORS >> 8) & 0xff;
_buffer[index] = opcodes.OP_KILL_CURSORS & 0xff;
index = index + 4;

@@ -398,3 +399,3 @@

_buffer[index + 1] = (0 >> 8) & 0xff;
_buffer[index] = (0) & 0xff;
_buffer[index] = 0 & 0xff;
index = index + 4;

@@ -407,7 +408,7 @@

_buffer[index + 1] = (this.cursorIds.length >> 8) & 0xff;
_buffer[index] = (this.cursorIds.length) & 0xff;
_buffer[index] = this.cursorIds.length & 0xff;
index = index + 4;
// Write all the cursor ids into the array
for(var i = 0; i < this.cursorIds.length; i++) {
for (var i = 0; i < this.cursorIds.length; i++) {
// Write cursor id

@@ -418,3 +419,3 @@ // index = write32bit(index, _buffer, cursorIds[i].getLowBits());

_buffer[index + 1] = (this.cursorIds[i].getLowBits() >> 8) & 0xff;
_buffer[index] = (this.cursorIds[i].getLowBits()) & 0xff;
_buffer[index] = this.cursorIds[i].getLowBits() & 0xff;
index = index + 4;

@@ -426,3 +427,3 @@

_buffer[index + 1] = (this.cursorIds[i].getHighBits() >> 8) & 0xff;
_buffer[index] = (this.cursorIds[i].getHighBits()) & 0xff;
_buffer[index] = this.cursorIds[i].getHighBits() & 0xff;
index = index + 4;

@@ -433,52 +434,25 @@ }

return _buffer;
}
};
var Response = function(bson, data, opts) {
opts = opts || {promoteLongs: true, promoteValues: true, promoteBuffers: false};
var Response = function(bson, message, msgHeader, msgBody, opts) {
opts = opts || { promoteLongs: true, promoteValues: true, promoteBuffers: false };
this.parsed = false;
//
// Parse Header
//
this.index = 0;
this.raw = data;
this.data = data;
this.raw = message;
this.data = msgBody;
this.bson = bson;
this.opts = opts;
// Read the message length
this.length = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Read the message header
this.length = msgHeader.length;
this.requestId = msgHeader.requestId;
this.responseTo = msgHeader.responseTo;
this.opCode = msgHeader.opCode;
this.fromCompressed = msgHeader.fromCompressed;
// Fetch the request id for this reply
this.requestId = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Read the message body
this.responseFlags = msgBody.readInt32LE(0);
this.cursorId = new Long(msgBody.readInt32LE(4), msgBody.readInt32LE(8));
this.startingFrom = msgBody.readInt32LE(12);
this.numberReturned = msgBody.readInt32LE(16);
// Fetch the id of the request that triggered the response
this.responseTo = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Skip op-code field
this.index = this.index + 4;
// Unpack flags
this.responseFlags = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Unpack the cursor
var lowBits = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
var highBits = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Create long object
this.cursorId = new Long(lowBits, highBits);
// Unpack the starting from
this.startingFrom = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Unpack the number of objects returned
this.numberReturned = data[this.index] | data[this.index + 1] << 8 | data[this.index + 2] << 16 | data[this.index + 3] << 24;
this.index = this.index + 4;
// Preallocate document array

@@ -488,18 +462,18 @@ this.documents = new Array(this.numberReturned);

// Flag values
this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) != 0;
this.queryFailure = (this.responseFlags & QUERY_FAILURE) != 0;
this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) != 0;
this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) != 0;
this.promoteLongs = typeof opts.promoteLongs == 'boolean' ? opts.promoteLongs : true;
this.promoteValues = typeof opts.promoteValues == 'boolean' ? opts.promoteValues : true;
this.promoteBuffers = typeof opts.promoteBuffers == 'boolean' ? opts.promoteBuffers : false;
}
this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
this.promoteLongs = typeof opts.promoteLongs === 'boolean' ? opts.promoteLongs : true;
this.promoteValues = typeof opts.promoteValues === 'boolean' ? opts.promoteValues : true;
this.promoteBuffers = typeof opts.promoteBuffers === 'boolean' ? opts.promoteBuffers : false;
};
Response.prototype.isParsed = function() {
return this.parsed;
}
};
Response.prototype.parse = function(options) {
// Don't parse again if not needed
if(this.parsed) return;
if (this.parsed) return;
options = options || {};

@@ -510,11 +484,8 @@

var documentsReturnedIn = options.documentsReturnedIn || null;
var promoteLongs = typeof options.promoteLongs == 'boolean'
? options.promoteLongs
: this.opts.promoteLongs;
var promoteValues = typeof options.promoteValues == 'boolean'
? options.promoteValues
: this.opts.promoteValues;
var promoteBuffers = typeof options.promoteBuffers == 'boolean'
? options.promoteBuffers
: this.opts.promoteBuffers
var promoteLongs =
typeof options.promoteLongs === 'boolean' ? options.promoteLongs : this.opts.promoteLongs;
var promoteValues =
typeof options.promoteValues === 'boolean' ? options.promoteValues : this.opts.promoteValues;
var promoteBuffers =
typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : this.opts.promoteBuffers;
var bsonSize, _options;

@@ -529,12 +500,20 @@

// Position within OP_REPLY at which documents start
// (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
this.index = 20;
//
// Single document and documentsReturnedIn set
//
if(this.numberReturned == 1 && documentsReturnedIn != null && raw) {
if (this.numberReturned === 1 && documentsReturnedIn != null && raw) {
// Calculate the bson size
bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24;
bsonSize =
this.data[this.index] |
(this.data[this.index + 1] << 8) |
(this.data[this.index + 2] << 16) |
(this.data[this.index + 3] << 24);
// Slice out the buffer containing the command result document
var document = this.data.slice(this.index, this.index + bsonSize);
// Set up field we wish to keep as raw
var fieldsAsRaw = {}
var fieldsAsRaw = {};
fieldsAsRaw[documentsReturnedIn] = true;

@@ -550,5 +529,4 @@ _options.fieldsAsRaw = fieldsAsRaw;

// Ensure we have a Long valie cursor id
this.cursorId = typeof doc.cursor.id == 'number'
? Long.fromNumber(doc.cursor.id)
: doc.cursor.id;
this.cursorId =
typeof doc.cursor.id === 'number' ? Long.fromNumber(doc.cursor.id) : doc.cursor.id;

@@ -559,3 +537,3 @@ // Adjust the index

// Set as parsed
this.parsed = true
this.parsed = true;
return;

@@ -567,10 +545,17 @@ }

//
for(var i = 0; i < this.numberReturned; i++) {
bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24;
for (var i = 0; i < this.numberReturned; i++) {
bsonSize =
this.data[this.index] |
(this.data[this.index + 1] << 8) |
(this.data[this.index + 2] << 16) |
(this.data[this.index + 3] << 24);
// If we have raw results specified slice the return document
if(raw) {
if (raw) {
this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
} else {
this.documents[i] = this.bson.deserialize(this.data.slice(this.index, this.index + bsonSize), _options);
this.documents[i] = this.bson.deserialize(
this.data.slice(this.index, this.index + bsonSize),
_options
);
}

@@ -584,9 +569,9 @@

this.parsed = true;
}
};
module.exports = {
Query: Query
, GetMore: GetMore
, Response: Response
, KillCursor: KillCursor
}
Query: Query,
GetMore: GetMore,
Response: Response,
KillCursor: KillCursor
};

@@ -1,18 +0,41 @@

"use strict";
'use strict';
var inherits = require('util').inherits
, EventEmitter = require('events').EventEmitter
, net = require('net')
, tls = require('tls')
, crypto = require('crypto')
, f = require('util').format
, debugOptions = require('./utils').debugOptions
, Response = require('./commands').Response
, MongoError = require('../error')
, Logger = require('./logger');
var inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter,
net = require('net'),
tls = require('tls'),
crypto = require('crypto'),
f = require('util').format,
debugOptions = require('./utils').debugOptions,
parseHeader = require('../wireprotocol/shared').parseHeader,
decompress = require('../wireprotocol/compression').decompress,
Response = require('./commands').Response,
MongoNetworkError = require('../error').MongoNetworkError,
Logger = require('./logger'),
OP_COMPRESSED = require('../wireprotocol/shared').opcodes.OP_COMPRESSED,
MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
var _id = 0;
var debugFields = ['host', 'port', 'size', 'keepAlive', 'keepAliveInitialDelay', 'noDelay'
, 'connectionTimeout', 'socketTimeout', 'singleBufferSerializtion', 'ssl', 'ca', 'crl', 'cert'
, 'rejectUnauthorized', 'promoteLongs', 'promoteValues', 'promoteBuffers', 'checkServerIdentity'];
var debugFields = [
'host',
'port',
'size',
'keepAlive',
'keepAliveInitialDelay',
'noDelay',
'connectionTimeout',
'socketTimeout',
'singleBufferSerializtion',
'ssl',
'ca',
'crl',
'cert',
'rejectUnauthorized',
'promoteLongs',
'promoteValues',
'promoteBuffers',
'checkServerIdentity'
];
var connectionAccountingSpy = undefined;
var connectionAccounting = false;

@@ -26,3 +49,3 @@ var connections = {};

* @param {number} options.port The server port
* @param {number} [options.family=4] Version of IP stack. Defaults to 4.
* @param {number} [options.family=null] IP version for DNS lookup, passed down to Node's [`dns.lookup()` function](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback). If set to `6`, will only look for ipv6 addresses.
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled

@@ -62,3 +85,3 @@ * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled

// No bson parser passed in
if(!options.bson) throw new Error("must pass in valid bson parser");
if (!options.bson) throw new Error('must pass in valid bson parser');
// Get bson parser

@@ -72,5 +95,12 @@ this.bson = options.bson;

// Max BSON message size
this.maxBsonMessageSize = options.maxBsonMessageSize || (1024 * 1024 * 16 * 4);
this.maxBsonMessageSize = options.maxBsonMessageSize || 1024 * 1024 * 16 * 4;
// Debug information
if(this.logger.isDebug()) this.logger.debug(f('creating connection %s with options [%s]', this.id, JSON.stringify(debugOptions(debugFields, options))));
if (this.logger.isDebug())
this.logger.debug(
f(
'creating connection %s with options [%s]',
this.id,
JSON.stringify(debugOptions(debugFields, options))
)
);

@@ -80,15 +110,14 @@ // Default options

this.host = options.host || 'localhost';
this.family = typeof options.family == 'number' ? options.family : 4;
this.keepAlive = typeof options.keepAlive == 'boolean' ? options.keepAlive : true;
this.keepAliveInitialDelay = typeof options.keepAliveInitialDelay == 'number'
? options.keepAliveInitialDelay : 300000;
this.noDelay = typeof options.noDelay == 'boolean' ? options.noDelay : true;
this.connectionTimeout = typeof options.connectionTimeout == 'number'
? options.connectionTimeout : 30000;
this.socketTimeout = typeof options.socketTimeout == 'number'
? options.socketTimeout : 360000;
this.family = typeof options.family === 'number' ? options.family : void 0;
this.keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
this.keepAliveInitialDelay =
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
this.noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
this.connectionTimeout =
typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
this.socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
// Is the keepAliveInitialDelay > socketTimeout set it to half of socketTimeout
if(this.keepAliveInitialDelay > this.socketTimeout) {
this.keepAliveInitialDelay = Math.round(this.socketTimeout/2);
if (this.keepAliveInitialDelay > this.socketTimeout) {
this.keepAliveInitialDelay = Math.round(this.socketTimeout / 2);
}

@@ -100,6 +129,7 @@

// Check if we have a domain socket
this.domainSocket = this.host.indexOf('\/') != -1;
this.domainSocket = this.host.indexOf('/') !== -1;
// Serialize commands using function
this.singleBufferSerializtion = typeof options.singleBufferSerializtion == 'boolean' ? options.singleBufferSerializtion : true;
this.singleBufferSerializtion =
typeof options.singleBufferSerializtion === 'boolean' ? options.singleBufferSerializtion : true;
this.serializationFunction = this.singleBufferSerializtion ? 'toBinUnified' : 'toBin';

@@ -113,16 +143,22 @@

this.passphrase = options.passphrase || null;
this.ssl = typeof options.ssl == 'boolean' ? options.ssl : false;
this.rejectUnauthorized = typeof options.rejectUnauthorized == 'boolean' ? options.rejectUnauthorized : true;
this.checkServerIdentity = typeof options.checkServerIdentity == 'boolean'
|| typeof options.checkServerIdentity == 'function' ? options.checkServerIdentity : true;
this.ciphers = options.ciphers || null;
this.ecdhCurve = options.ecdhCurve || null;
this.ssl = typeof options.ssl === 'boolean' ? options.ssl : false;
this.rejectUnauthorized =
typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
this.checkServerIdentity =
typeof options.checkServerIdentity === 'boolean' ||
typeof options.checkServerIdentity === 'function'
? options.checkServerIdentity
: true;
// If ssl not enabled
if(!this.ssl) this.rejectUnauthorized = false;
if (!this.ssl) this.rejectUnauthorized = false;
// Response options
this.responseOptions = {
promoteLongs: typeof options.promoteLongs == 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues == 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers == 'boolean' ? options.promoteBuffers: false
}
promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false
};

@@ -146,3 +182,3 @@ // Flushing

this.workItems = [];
}
};

@@ -152,25 +188,30 @@ inherits(Connection, EventEmitter);

Connection.prototype.setSocketTimeout = function(value) {
if(this.connection) {
if (this.connection) {
this.connection.setTimeout(value);
}
}
};
Connection.prototype.resetSocketTimeout = function() {
if(this.connection) {
if (this.connection) {
this.connection.setTimeout(this.socketTimeout);
}
}
};
Connection.enableConnectionAccounting = function() {
Connection.enableConnectionAccounting = function(spy) {
if (spy) {
connectionAccountingSpy = spy;
}
connectionAccounting = true;
connections = {};
}
};
Connection.disableConnectionAccounting = function() {
connectionAccounting = false;
}
connectionAccountingSpy = undefined;
};
Connection.connections = function() {
return connections;
}
};

@@ -180,2 +221,6 @@ function deleteConnection(id) {

delete connections[id];
if (connectionAccountingSpy) {
connectionAccountingSpy.deleteConnection(id);
}
}

@@ -186,2 +231,6 @@

connections[id] = connection;
if (connectionAccountingSpy) {
connectionAccountingSpy.addConnection(id, connection);
}
}

@@ -193,36 +242,91 @@

return function(err) {
if(connectionAccounting) deleteConnection(self.id);
if (connectionAccounting) deleteConnection(self.id);
// Debug information
if(self.logger.isDebug()) self.logger.debug(f('connection %s for [%s:%s] errored out with [%s]', self.id, self.host, self.port, JSON.stringify(err)));
if (self.logger.isDebug())
self.logger.debug(
f(
'connection %s for [%s:%s] errored out with [%s]',
self.id,
self.host,
self.port,
JSON.stringify(err)
)
);
// Emit the error
if(self.listeners('error').length > 0) self.emit("error", MongoError.create(err), self);
}
}
if (self.listeners('error').length > 0) self.emit('error', new MongoNetworkError(err), self);
};
};
var timeoutHandler = function(self) {
return function() {
if(connectionAccounting) deleteConnection(self.id);
if (connectionAccounting) deleteConnection(self.id);
// Debug information
if(self.logger.isDebug()) self.logger.debug(f('connection %s for [%s:%s] timed out', self.id, self.host, self.port));
if (self.logger.isDebug())
self.logger.debug(f('connection %s for [%s:%s] timed out', self.id, self.host, self.port));
// Emit timeout error
self.emit("timeout"
, MongoError.create(f("connection %s to %s:%s timed out", self.id, self.host, self.port))
, self);
}
}
self.emit(
'timeout',
new MongoNetworkError(f('connection %s to %s:%s timed out', self.id, self.host, self.port)),
self
);
};
};
var closeHandler = function(self) {
return function(hadError) {
if(connectionAccounting) deleteConnection(self.id);
if (connectionAccounting) deleteConnection(self.id);
// Debug information
if(self.logger.isDebug()) self.logger.debug(f('connection %s with for [%s:%s] closed', self.id, self.host, self.port));
if (self.logger.isDebug())
self.logger.debug(f('connection %s with for [%s:%s] closed', self.id, self.host, self.port));
// Emit close event
if(!hadError) {
self.emit("close"
, MongoError.create(f("connection %s to %s:%s closed", self.id, self.host, self.port))
, self);
if (!hadError) {
self.emit(
'close',
new MongoNetworkError(f('connection %s to %s:%s closed', self.id, self.host, self.port)),
self
);
}
};
};
// Handle a message once it is recieved
var emitMessageHandler = function(self, message) {
var msgHeader = parseHeader(message);
if (msgHeader.opCode === OP_COMPRESSED) {
msgHeader.fromCompressed = true;
var index = MESSAGE_HEADER_SIZE;
msgHeader.opCode = message.readInt32LE(index);
index += 4;
msgHeader.length = message.readInt32LE(index);
index += 4;
var compressorID = message[index];
index++;
decompress(compressorID, message.slice(index), function(err, decompressedMsgBody) {
if (err) {
throw err;
}
if (decompressedMsgBody.length !== msgHeader.length) {
throw new Error(
'Decompressing a compressed message from the server failed. The message is corrupt.'
);
}
self.messageHandler(
new Response(self.bson, message, msgHeader, decompressedMsgBody, self.responseOptions),
self
);
});
} else {
self.messageHandler(
new Response(
self.bson,
message,
msgHeader,
message.slice(MESSAGE_HEADER_SIZE),
self.responseOptions
),
self
);
}
}
};

@@ -232,9 +336,9 @@ var dataHandler = function(self) {

// Parse until we are done with the data
while(data.length > 0) {
while (data.length > 0) {
// If we still have bytes to read on the current message
if(self.bytesRead > 0 && self.sizeOfMessage > 0) {
if (self.bytesRead > 0 && self.sizeOfMessage > 0) {
// Calculate the amount of remaining bytes
var remainingBytesToRead = self.sizeOfMessage - self.bytesRead;
// Check if the current chunk contains the rest of the message
if(remainingBytesToRead > data.length) {
if (remainingBytesToRead > data.length) {
// Copy the new data into the exiting buffer (should have been allocated when we know the message size)

@@ -261,11 +365,17 @@ data.copy(self.buffer, self.bytesRead);

self.stubBuffer = null;
// Emit the buffer
self.messageHandler(new Response(self.bson, emitBuffer, self.responseOptions), self);
} catch(err) {
var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{
sizeOfMessage:self.sizeOfMessage,
bytesRead:self.bytesRead,
stubBuffer:self.stubBuffer}};
emitMessageHandler(self, emitBuffer);
} catch (err) {
var errorObject = {
err: 'socketHandler',
trace: err,
bin: self.buffer,
parseState: {
sizeOfMessage: self.sizeOfMessage,
bytesRead: self.bytesRead,
stubBuffer: self.stubBuffer
}
};
// We got a parse Error fire it off then keep going
self.emit("parseError", errorObject, self);
self.emit('parseError', errorObject, self);
}

@@ -276,5 +386,5 @@ }

// size of the message (< 4 bytes)
if(self.stubBuffer != null && self.stubBuffer.length > 0) {
if (self.stubBuffer != null && self.stubBuffer.length > 0) {
// If we have enough bytes to determine the message size let's do it
if(self.stubBuffer.length + data.length > 4) {
if (self.stubBuffer.length + data.length > 4) {
// Prepad the data

@@ -292,5 +402,3 @@ var newData = new Buffer(self.stubBuffer.length + data.length);

self.stubBuffer = null;
} else {
// Add the the bytes to the stub buffer

@@ -306,14 +414,20 @@ var newStubBuffer = new Buffer(self.stubBuffer.length + data.length);

} else {
if(data.length > 4) {
if (data.length > 4) {
// Retrieve the message size
// var sizeOfMessage = data.readUInt32LE(0);
var sizeOfMessage = data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24;
var sizeOfMessage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
// If we have a negative sizeOfMessage emit error and return
if(sizeOfMessage < 0 || sizeOfMessage > self.maxBsonMessageSize) {
errorObject = {err:"socketHandler", trace:'', bin:self.buffer, parseState:{
sizeOfMessage: sizeOfMessage,
bytesRead: self.bytesRead,
stubBuffer: self.stubBuffer}};
if (sizeOfMessage < 0 || sizeOfMessage > self.maxBsonMessageSize) {
errorObject = {
err: 'socketHandler',
trace: '',
bin: self.buffer,
parseState: {
sizeOfMessage: sizeOfMessage,
bytesRead: self.bytesRead,
stubBuffer: self.stubBuffer
}
};
// We got a parse Error fire it off then keep going
self.emit("parseError", errorObject, self);
self.emit('parseError', errorObject, self);
return;

@@ -323,3 +437,7 @@ }

// Ensure that the size of message is larger than 0 and less than the max allowed
if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonMessageSize && sizeOfMessage > data.length) {
if (
sizeOfMessage > 4 &&
sizeOfMessage < self.maxBsonMessageSize &&
sizeOfMessage > data.length
) {
self.buffer = new Buffer(sizeOfMessage);

@@ -336,4 +454,7 @@ // Copy all the data into the buffer

data = new Buffer(0);
} else if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonMessageSize && sizeOfMessage == data.length) {
} else if (
sizeOfMessage > 4 &&
sizeOfMessage < self.maxBsonMessageSize &&
sizeOfMessage === data.length
) {
try {

@@ -349,14 +470,20 @@ emitBuffer = data;

// Emit the message
self.messageHandler(new Response(self.bson, emitBuffer, self.responseOptions), self);
emitMessageHandler(self, emitBuffer);
} catch (err) {
self.emit("parseError", err, self);
self.emit('parseError', err, self);
}
} else if(sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonMessageSize) {
errorObject = {err:"socketHandler", trace:null, bin:data, parseState:{
sizeOfMessage:sizeOfMessage,
bytesRead:0,
buffer:null,
stubBuffer:null}};
} else if (sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonMessageSize) {
errorObject = {
err: 'socketHandler',
trace: null,
bin: data,
parseState: {
sizeOfMessage: sizeOfMessage,
bytesRead: 0,
buffer: null,
stubBuffer: null
}
};
// We got a parse Error fire it off then keep going
self.emit("parseError", errorObject, self);
self.emit('parseError', errorObject, self);

@@ -380,7 +507,7 @@ // Clear out the state of the parser

// Emit the message
self.messageHandler(new Response(self.bson, emitBuffer, self.responseOptions), self);
emitMessageHandler(self, emitBuffer);
}
} else {
// Create a buffer that contains the space for the non-complete message
self.stubBuffer = new Buffer(data.length)
self.stubBuffer = new Buffer(data.length);
// Copy the data to the stub buffer

@@ -394,15 +521,27 @@ data.copy(self.stubBuffer, 0);

}
}
}
};
};
// List of socket level valid ssl options
var legalSslSocketOptions = ['pfx', 'key', 'passphrase', 'cert', 'ca', 'ciphers'
, 'NPNProtocols', 'ALPNProtocols', 'servername'
, 'secureProtocol', 'secureContext', 'session'
, 'minDHSize'];
var legalSslSocketOptions = [
'pfx',
'key',
'passphrase',
'cert',
'ca',
'ciphers',
'NPNProtocols',
'ALPNProtocols',
'servername',
'ecdhCurve',
'secureProtocol',
'secureContext',
'session',
'minDHSize'
];
function merge(options1, options2) {
// Merge in any allowed ssl options
for(var name in options2) {
if(options2[name] != null && legalSslSocketOptions.indexOf(name) != -1) {
for (var name in options2) {
if (options2[name] != null && legalSslSocketOptions.indexOf(name) !== -1) {
options1[name] = options2[name];

@@ -421,5 +560,5 @@ }

// Set the connections
if(connectionAccounting) addConnection(this.id, this);
if (connectionAccounting) addConnection(this.id, this);
// Check if we are overriding the promoteLongs
if(typeof _options.promoteLongs == 'boolean') {
if (typeof _options.promoteLongs === 'boolean') {
self.responseOptions.promoteLongs = _options.promoteLongs;

@@ -431,5 +570,11 @@ self.responseOptions.promoteValues = _options.promoteValues;

// Create new connection instance
var connection_options = self.domainSocket
? {path: self.host}
: {port: self.port, host: self.host, family: self.family};
var connection_options;
if (self.domainSocket) {
connection_options = { path: self.host };
} else {
connection_options = { port: self.port, host: self.host };
if (self.family !== void 0) {
connection_options.family = self.family;
}
}
self.connection = net.createConnection(connection_options);

@@ -443,7 +588,7 @@

// If we have ssl enabled
if(self.ssl) {
if (self.ssl) {
var sslOptions = {
socket: self.connection
, rejectUnauthorized: self.rejectUnauthorized
}
socket: self.connection,
rejectUnauthorized: self.rejectUnauthorized
};

@@ -455,10 +600,10 @@ // Merge in options

// Set options for ssl
if(self.ca) sslOptions.ca = self.ca;
if(self.crl) sslOptions.crl = self.crl;
if(self.cert) sslOptions.cert = self.cert;
if(self.key) sslOptions.key = self.key;
if(self.passphrase) sslOptions.passphrase = self.passphrase;
if (self.ca) sslOptions.ca = self.ca;
if (self.crl) sslOptions.crl = self.crl;
if (self.cert) sslOptions.cert = self.cert;
if (self.key) sslOptions.key = self.key;
if (self.passphrase) sslOptions.passphrase = self.passphrase;
// Override checkServerIdentity behavior
if(self.checkServerIdentity == false) {
if (self.checkServerIdentity === false) {
// Skip the identiy check by retuning undefined as per node documents

@@ -468,4 +613,4 @@ // https://nodejs.org/api/tls.html#tls_tls_connect_options_callback

return undefined;
}
} else if(typeof self.checkServerIdentity == 'function') {
};
} else if (typeof self.checkServerIdentity === 'function') {
sslOptions.checkServerIdentity = self.checkServerIdentity;

@@ -475,3 +620,3 @@ }

// Set default sni servername to be the same as host
if(sslOptions.servername == null) {
if (sslOptions.servername == null) {
sslOptions.servername = self.host;

@@ -483,4 +628,4 @@ }

// Error on auth or skip
if(self.connection.authorizationError && self.rejectUnauthorized) {
return self.emit("error", self.connection.authorizationError, self, {ssl:true});
if (self.connection.authorizationError && self.rejectUnauthorized) {
return self.emit('error', self.connection.authorizationError, self, { ssl: true });
}

@@ -495,3 +640,3 @@

} else {
self.connection.on('connect', function() {
self.connection.once('connect', function() {
// Set socket timeout instead of connection timeout

@@ -509,3 +654,3 @@ self.connection.setTimeout(self.socketTimeout);

self.connection.on('data', dataHandler(self));
}
};

@@ -525,3 +670,3 @@ /**

}
}
};

@@ -534,6 +679,8 @@ /**

// Set the connections
if(connectionAccounting) deleteConnection(this.id);
if(this.connection) {
if (connectionAccounting) deleteConnection(this.id);
if (this.connection) {
// Catch posssible exception thrown by node 0.10.x
try { this.connection.end(); } catch (err) {}
try {
this.connection.end();
} catch (err) {} // eslint-disable-line
// Destroy connection

@@ -544,3 +691,3 @@ this.connection.destroy();

this.destroyed = true;
}
};

@@ -555,8 +702,12 @@ /**

// Debug Log
if(this.logger.isDebug()) {
if(!Array.isArray(buffer)) {
this.logger.debug(f('writing buffer [%s] to %s:%s', buffer.toString('hex'), this.host, this.port));
if (this.logger.isDebug()) {
if (!Array.isArray(buffer)) {
this.logger.debug(
f('writing buffer [%s] to %s:%s', buffer.toString('hex'), this.host, this.port)
);
} else {
for(i = 0; i < buffer.length; i++)
this.logger.debug(f('writing buffer [%s] to %s:%s', buffer[i].toString('hex'), this.host, this.port));
for (i = 0; i < buffer.length; i++)
this.logger.debug(
f('writing buffer [%s] to %s:%s', buffer[i].toString('hex'), this.host, this.port)
);
}

@@ -566,5 +717,5 @@ }

// Double check that the connection is not destroyed
if(this.connection.destroyed === false) {
if (this.connection.destroyed === false) {
// Write out the command
if(!Array.isArray(buffer)) {
if (!Array.isArray(buffer)) {
this.connection.write(buffer, 'binary');

@@ -575,9 +726,9 @@ return true;

// Iterate over all buffers and write them in order to the socket
for(i = 0; i < buffer.length; i++) this.connection.write(buffer[i], 'binary');
for (i = 0; i < buffer.length; i++) this.connection.write(buffer[i], 'binary');
return true;
}
}
// Connection is destroyed return write failed
return false;
}
};

@@ -590,4 +741,4 @@ /**

Connection.prototype.toString = function() {
return "" + this.id;
}
return '' + this.id;
};

@@ -600,4 +751,4 @@ /**

Connection.prototype.toJSON = function() {
return {id: this.id, host: this.host, port: this.port};
}
return { id: this.id, host: this.host, port: this.port };
};

@@ -610,5 +761,5 @@ /**

Connection.prototype.isConnected = function() {
if(this.destroyed) return false;
if (this.destroyed) return false;
return !this.connection.destroyed && this.connection.writable;
}
};

@@ -615,0 +766,0 @@ /**

@@ -1,5 +0,5 @@

"use strict";
'use strict';
var f = require('util').format
, MongoError = require('../error');
var f = require('util').format,
MongoError = require('../error').MongoError;

@@ -25,3 +25,3 @@ // Filters for classes

var Logger = function(className, options) {
if(!(this instanceof Logger)) return new Logger(className, options);
if (!(this instanceof Logger)) return new Logger(className, options);
options = options || {};

@@ -33,5 +33,5 @@

// Current logger
if(options.logger) {
if (options.logger) {
currentLogger = options.logger;
} else if(currentLogger == null) {
} else if (currentLogger == null) {
currentLogger = console.log;

@@ -41,3 +41,3 @@ }

// Set level of logging, default is error
if(options.loggerLevel) {
if (options.loggerLevel) {
level = options.loggerLevel || 'error';

@@ -47,4 +47,4 @@ }

// Add all class names
if(filteredClasses[this.className] == null) classFilters[this.className] = true;
}
if (filteredClasses[this.className] == null) classFilters[this.className] = true;
};

@@ -59,14 +59,20 @@ /**

Logger.prototype.debug = function(message, object) {
if(this.isDebug()
&& ((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className])
|| (Object.keys(filteredClasses).length == 0 && classFilters[this.className]))) {
if (
this.isDebug() &&
((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className]) ||
(Object.keys(filteredClasses).length === 0 && classFilters[this.className]))
) {
var dateTime = new Date().getTime();
var msg = f("[%s-%s:%s] %s %s", 'DEBUG', this.className, pid, dateTime, message);
var msg = f('[%s-%s:%s] %s %s', 'DEBUG', this.className, pid, dateTime, message);
var state = {
type: 'debug', message: message, className: this.className, pid: pid, date: dateTime
type: 'debug',
message: message,
className: this.className,
pid: pid,
date: dateTime
};
if(object) state.meta = object;
if (object) state.meta = object;
currentLogger(msg, state);
}
}
};

@@ -80,95 +86,107 @@ /**

*/
Logger.prototype.warn = function(message, object) {
if(this.isWarn()
&& ((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className])
|| (Object.keys(filteredClasses).length == 0 && classFilters[this.className]))) {
(Logger.prototype.warn = function(message, object) {
if (
this.isWarn() &&
((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className]) ||
(Object.keys(filteredClasses).length === 0 && classFilters[this.className]))
) {
var dateTime = new Date().getTime();
var msg = f("[%s-%s:%s] %s %s", 'WARN', this.className, pid, dateTime, message);
var msg = f('[%s-%s:%s] %s %s', 'WARN', this.className, pid, dateTime, message);
var state = {
type: 'warn', message: message, className: this.className, pid: pid, date: dateTime
type: 'warn',
message: message,
className: this.className,
pid: pid,
date: dateTime
};
if(object) state.meta = object;
if (object) state.meta = object;
currentLogger(msg, state);
}
},
}),
/**
* Log a message at the info level
* @method
* @param {string} message The message to log
* @param {object} object additional meta data to log
* @return {null}
*/
(Logger.prototype.info = function(message, object) {
if (
this.isInfo() &&
((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className]) ||
(Object.keys(filteredClasses).length === 0 && classFilters[this.className]))
) {
var dateTime = new Date().getTime();
var msg = f('[%s-%s:%s] %s %s', 'INFO', this.className, pid, dateTime, message);
var state = {
type: 'info',
message: message,
className: this.className,
pid: pid,
date: dateTime
};
if (object) state.meta = object;
currentLogger(msg, state);
}
}),
/**
* Log a message at the error level
* @method
* @param {string} message The message to log
* @param {object} object additional meta data to log
* @return {null}
*/
(Logger.prototype.error = function(message, object) {
if (
this.isError() &&
((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className]) ||
(Object.keys(filteredClasses).length === 0 && classFilters[this.className]))
) {
var dateTime = new Date().getTime();
var msg = f('[%s-%s:%s] %s %s', 'ERROR', this.className, pid, dateTime, message);
var state = {
type: 'error',
message: message,
className: this.className,
pid: pid,
date: dateTime
};
if (object) state.meta = object;
currentLogger(msg, state);
}
}),
/**
* Is the logger set at info level
* @method
* @return {boolean}
*/
(Logger.prototype.isInfo = function() {
return level === 'info' || level === 'debug';
}),
/**
* Is the logger set at error level
* @method
* @return {boolean}
*/
(Logger.prototype.isError = function() {
return level === 'error' || level === 'info' || level === 'debug';
}),
/**
* Is the logger set at error level
* @method
* @return {boolean}
*/
(Logger.prototype.isWarn = function() {
return level === 'error' || level === 'warn' || level === 'info' || level === 'debug';
}),
/**
* Is the logger set at debug level
* @method
* @return {boolean}
*/
(Logger.prototype.isDebug = function() {
return level === 'debug';
});
/**
* Log a message at the info level
* @method
* @param {string} message The message to log
* @param {object} object additional meta data to log
* @return {null}
*/
Logger.prototype.info = function(message, object) {
if(this.isInfo()
&& ((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className])
|| (Object.keys(filteredClasses).length == 0 && classFilters[this.className]))) {
var dateTime = new Date().getTime();
var msg = f("[%s-%s:%s] %s %s", 'INFO', this.className, pid, dateTime, message);
var state = {
type: 'info', message: message, className: this.className, pid: pid, date: dateTime
};
if(object) state.meta = object;
currentLogger(msg, state);
}
},
/**
* Log a message at the error level
* @method
* @param {string} message The message to log
* @param {object} object additional meta data to log
* @return {null}
*/
Logger.prototype.error = function(message, object) {
if(this.isError()
&& ((Object.keys(filteredClasses).length > 0 && filteredClasses[this.className])
|| (Object.keys(filteredClasses).length == 0 && classFilters[this.className]))) {
var dateTime = new Date().getTime();
var msg = f("[%s-%s:%s] %s %s", 'ERROR', this.className, pid, dateTime, message);
var state = {
type: 'error', message: message, className: this.className, pid: pid, date: dateTime
};
if(object) state.meta = object;
currentLogger(msg, state);
}
},
/**
* Is the logger set at info level
* @method
* @return {boolean}
*/
Logger.prototype.isInfo = function() {
return level == 'info' || level == 'debug';
},
/**
* Is the logger set at error level
* @method
* @return {boolean}
*/
Logger.prototype.isError = function() {
return level == 'error' || level == 'info' || level == 'debug';
},
/**
* Is the logger set at error level
* @method
* @return {boolean}
*/
Logger.prototype.isWarn = function() {
return level == 'error' || level == 'warn' || level == 'info' || level == 'debug';
},
/**
* Is the logger set at debug level
* @method
* @return {boolean}
*/
Logger.prototype.isDebug = function() {
return level == 'debug';
}
/**
* Resets the logger to default settings, error and no filtered classes

@@ -181,3 +199,3 @@ * @method

filteredClasses = {};
}
};

@@ -191,3 +209,3 @@ /**

return currentLogger;
}
};

@@ -201,5 +219,5 @@ /**

Logger.setCurrentLogger = function(logger) {
if(typeof logger != 'function') throw new MongoError("current logger must be a function");
if (typeof logger !== 'function') throw new MongoError('current logger must be a function');
currentLogger = logger;
}
};

@@ -214,3 +232,3 @@ /**

Logger.filter = function(type, values) {
if(type == 'class' && Array.isArray(values)) {
if (type === 'class' && Array.isArray(values)) {
filteredClasses = {};

@@ -222,3 +240,3 @@

}
}
};

@@ -232,9 +250,9 @@ /**

Logger.setLevel = function(_level) {
if(_level != 'info' && _level != 'error' && _level != 'debug' && _level != 'warn') {
throw new Error(f("%s is an illegal logging level", _level));
if (_level !== 'info' && _level !== 'error' && _level !== 'debug' && _level !== 'warn') {
throw new Error(f('%s is an illegal logging level', _level));
}
level = _level;
}
};
module.exports = Logger;

@@ -1,2 +0,2 @@

"use strict";
'use strict';

@@ -6,3 +6,4 @@ var inherits = require('util').inherits,

Connection = require('./connection'),
MongoError = require('../error'),
MongoError = require('../error').MongoError,
MongoNetworkError = require('../error').MongoNetworkError,
Logger = require('./logger'),

@@ -12,10 +13,16 @@ f = require('util').format,

CommandResult = require('./command_result'),
assign = require('../utils').assign;
assign = require('../utils').assign,
MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE,
opcodes = require('../wireprotocol/shared').opcodes,
compress = require('../wireprotocol/compression').compress,
compressorIDs = require('../wireprotocol/compression').compressorIDs,
uncompressibleCommands = require('../wireprotocol/compression').uncompressibleCommands,
resolveClusterTime = require('../topologies/shared').resolveClusterTime;
var MongoCR = require('../auth/mongocr')
, X509 = require('../auth/x509')
, Plain = require('../auth/plain')
, GSSAPI = require('../auth/gssapi')
, SSPI = require('../auth/sspi')
, ScramSHA1 = require('../auth/scram');
var MongoCR = require('../auth/mongocr'),
X509 = require('../auth/x509'),
Plain = require('../auth/plain'),
GSSAPI = require('../auth/gssapi'),
SSPI = require('../auth/sspi'),
ScramSHA1 = require('../auth/scram');

@@ -30,2 +37,7 @@ var DISCONNECTED = 'disconnected';

function hasSessionSupport(topology) {
if (topology == null) return false;
return topology.ismaster == null ? false : topology.ismaster.maxWireVersion >= 6;
}
/**

@@ -36,3 +48,4 @@ * Creates a new Pool instance

* @param {number} options.port The server port
* @param {number} [options.size=1] Max server connection pool size
* @param {number} [options.size=5] Max server connection pool size
* @param {number} [options.minSize=0] Minimum server connection pool size
* @param {boolean} [options.reconnect=true] Server will attempt to reconnect on loss of connection

@@ -66,36 +79,47 @@ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times

*/
var Pool = function(options) {
var Pool = function(topology, options) {
// Add event listener
EventEmitter.call(this);
// Store topology for later use
this.topology = topology;
// Add the options
this.options = assign({
// Host and port settings
host: 'localhost',
port: 27017,
// Pool default max size
size: 5,
// socket settings
connectionTimeout: 30000,
socketTimeout: 360000,
keepAlive: true,
keepAliveInitialDelay: 300000,
noDelay: true,
// SSL Settings
ssl: false, checkServerIdentity: true,
ca: null, crl: null, cert: null, key: null, passPhrase: null,
rejectUnauthorized: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
// Reconnection options
reconnect: true,
reconnectInterval: 1000,
reconnectTries: 30,
// Enable domains
domainsEnabled: false
}, options);
this.options = assign(
{
// Host and port settings
host: 'localhost',
port: 27017,
// Pool default max size
size: 5,
// Pool default min size
minSize: 0,
// socket settings
connectionTimeout: 30000,
socketTimeout: 360000,
keepAlive: true,
keepAliveInitialDelay: 300000,
noDelay: true,
// SSL Settings
ssl: false,
checkServerIdentity: true,
ca: null,
crl: null,
cert: null,
key: null,
passPhrase: null,
rejectUnauthorized: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
// Reconnection options
reconnect: true,
reconnectInterval: 1000,
reconnectTries: 30,
// Enable domains
domainsEnabled: false
},
options
);
// console.log("=================================== pool options")
// console.dir(this.options)
// Identification information

@@ -107,6 +131,9 @@ this.id = _id++;

// No bson parser passed in
if(!options.bson || (options.bson
&& (typeof options.bson.serialize != 'function'
|| typeof options.bson.deserialize != 'function'))) {
throw new Error("must pass in valid bson parser");
if (
!options.bson ||
(options.bson &&
(typeof options.bson.serialize !== 'function' ||
typeof options.bson.deserialize !== 'function'))
) {
throw new Error('must pass in valid bson parser');
}

@@ -129,6 +156,9 @@

this.authProviders = options.authProviders || {
'mongocr': new MongoCR(options.bson), 'x509': new X509(options.bson)
, 'plain': new Plain(options.bson), 'gssapi': new GSSAPI(options.bson)
, 'sspi': new SSPI(options.bson), 'scram-sha-1': new ScramSHA1(options.bson)
}
mongocr: new MongoCR(options.bson),
x509: new X509(options.bson),
plain: new Plain(options.bson),
gssapi: new GSSAPI(options.bson),
sspi: new SSPI(options.bson),
'scram-sha-1': new ScramSHA1(options.bson)
};

@@ -147,3 +177,3 @@ // Contains the reconnect connection

this.connectionIndex = 0;
}
};

@@ -153,14 +183,27 @@ inherits(Pool, EventEmitter);

Object.defineProperty(Pool.prototype, 'size', {
enumerable:true,
get: function() { return this.options.size; }
enumerable: true,
get: function() {
return this.options.size;
}
});
Object.defineProperty(Pool.prototype, 'minSize', {
enumerable: true,
get: function() {
return this.options.minSize;
}
});
Object.defineProperty(Pool.prototype, 'connectionTimeout', {
enumerable:true,
get: function() { return this.options.connectionTimeout; }
enumerable: true,
get: function() {
return this.options.connectionTimeout;
}
});
Object.defineProperty(Pool.prototype, 'socketTimeout', {
enumerable:true,
get: function() { return this.options.socketTimeout; }
enumerable: true,
get: function() {
return this.options.socketTimeout;
}
});

@@ -170,16 +213,23 @@

var legalTransitions = {
'disconnected': [CONNECTING, DESTROYING, DISCONNECTED],
'connecting': [CONNECTING, DESTROYING, CONNECTED, DISCONNECTED],
'connected': [CONNECTED, DISCONNECTED, DESTROYING],
'destroying': [DESTROYING, DESTROYED],
'destroyed': [DESTROYED]
}
disconnected: [CONNECTING, DESTROYING, DISCONNECTED],
connecting: [CONNECTING, DESTROYING, CONNECTED, DISCONNECTED],
connected: [CONNECTED, DISCONNECTED, DESTROYING],
destroying: [DESTROYING, DESTROYED],
destroyed: [DESTROYED]
};
// Get current state
var legalStates = legalTransitions[self.state];
if(legalStates && legalStates.indexOf(newState) != -1) {
if (legalStates && legalStates.indexOf(newState) !== -1) {
self.state = newState;
} else {
self.logger.error(f('Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]'
, self.id, self.state, newState, legalStates));
self.logger.error(
f(
'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
self.id,
self.state,
newState,
legalStates
)
);
}

@@ -189,3 +239,3 @@ }

function authenticate(pool, auth, connection, cb) {
if(auth[0] === undefined) return cb(null);
if (auth[0] === undefined) return cb(null);
// We need to authenticate the server

@@ -195,3 +245,3 @@ var mechanism = auth[0];

// Validate if the mechanism exists
if(!pool.authProviders[mechanism]) {
if (!pool.authProviders[mechanism]) {
throw new MongoError(f('authMechanism %s not supported', mechanism));

@@ -212,3 +262,3 @@ }

// Ensure we stop auth if pool was destroyed
if(self.state == DESTROYED || self.state == DESTROYING) {
if (self.state === DESTROYED || self.state === DESTROYING) {
return callback(new MongoError('pool destroyed'));

@@ -219,3 +269,5 @@ }

connection.workItems.push({
cb: callback, command: true, requestId: command.requestId
cb: callback,
command: true,
requestId: command.requestId
});

@@ -228,3 +280,2 @@

function reauthenticate(pool, connection, cb) {

@@ -234,3 +285,3 @@ // Authenticate

// Finished re-authenticating against providers
if(providers.length == 0) return cb();
if (providers.length === 0) return cb();
// Get the provider name

@@ -242,3 +293,3 @@ var provider = pool.authProviders[providers.pop()];

// We got an error return immediately
if(err) return cb(err);
if (err) return cb(err);
// Continue authenticating the connection

@@ -255,5 +306,2 @@ authenticateAgainstProvider(pool, connection, providers, cb);

return function(err) {
// console.log("========== connectionFailureHandler :: " + event)
// console.dir(err)
if (this._connectionFailHandled) return;

@@ -268,10 +316,9 @@ this._connectionFailHandled = true;

// Flush all work Items on this connection
while(this.workItems.length > 0) {
while (this.workItems.length > 0) {
var workItem = this.workItems.shift();
// if(workItem.cb) workItem.cb(err);
if(workItem.cb) workItem.cb(err);
if (workItem.cb) workItem.cb(err);
}
// Did we catch a timeout, increment the numberOfConsecutiveTimeouts
if(event == 'timeout') {
if (event === 'timeout') {
self.numberOfConsecutiveTimeouts = self.numberOfConsecutiveTimeouts + 1;

@@ -281,3 +328,3 @@

// Force close the pool as we are trying to connect to tcp sink hole
if(self.numberOfConsecutiveTimeouts > self.options.reconnectTries) {
if (self.numberOfConsecutiveTimeouts > self.options.reconnectTries) {
self.numberOfConsecutiveTimeouts = 0;

@@ -292,4 +339,4 @@ // Destroy all connections and pool

// No more socket available propegate the event
if(self.socketCount() == 0) {
if(self.state != DESTROYED && self.state != DESTROYING) {
if (self.socketCount() === 0) {
if (self.state !== DESTROYED && self.state !== DESTROYING) {
stateTransition(self, DISCONNECTED);

@@ -300,3 +347,3 @@ }

// do not trigger the low level error handler in node
event = event == 'error' ? 'close' : event;
event = event === 'error' ? 'close' : event;
self.emit(event, err);

@@ -306,5 +353,15 @@ }

// Start reconnection attempts
if(!self.reconnectId && self.options.reconnect) {
if (!self.reconnectId && self.options.reconnect) {
self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
}
// Do we need to do anything to maintain the minimum pool size
const totalConnections =
self.availableConnections.length +
self.connectingConnections.length +
self.inUseConnections.length;
if (totalConnections < self.minSize) {
_createConnection(self);
}
};

@@ -315,8 +372,7 @@ }

return function() {
// console.log("========================= attemptReconnect")
self.emit('attemptReconnect', self);
if(self.state == DESTROYED || self.state == DESTROYING) return;
if (self.state === DESTROYED || self.state === DESTROYING) return;
// We are connected do not try again
if(self.isConnected()) {
if (self.isConnected()) {
self.reconnectId = null;

@@ -327,5 +383,4 @@ return;

// If we have failure schedule a retry
function _connectionFailureHandler(self, event) {
function _connectionFailureHandler(self) {
return function() {
// console.log("========== _connectionFailureHandler :: " + event)
if (this._connectionFailHandled) return;

@@ -338,12 +393,20 @@ this._connectionFailHandled = true;

// How many retries are left
if(self.retriesLeft == 0) {
if (self.retriesLeft === 0) {
// Destroy the instance
self.destroy();
// Emit close event
self.emit('reconnectFailed'
, new MongoError(f('failed to reconnect after %s attempts with interval %s ms', self.options.reconnectTries, self.options.reconnectInterval)));
self.emit(
'reconnectFailed',
new MongoNetworkError(
f(
'failed to reconnect after %s attempts with interval %s ms',
self.options.reconnectTries,
self.options.reconnectInterval
)
)
);
} else {
self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
}
}
};
}

@@ -358,3 +421,3 @@

// Pool destroyed stop the connection
if(self.state == DESTROYED || self.state == DESTROYING) {
if (self.state === DESTROYED || self.state === DESTROYING) {
return connection.destroy();

@@ -390,3 +453,3 @@ }

});
}
};
}

@@ -405,3 +468,3 @@

self.reconnectConnection.connect();
}
};
}

@@ -412,3 +475,3 @@

// Move the connection from connecting to available
if(index != -1) {
if (index !== -1) {
from.splice(index, 1);

@@ -425,4 +488,4 @@ to.push(connection);

// Locate the workItem
for(var i = 0; i < connection.workItems.length; i++) {
if(connection.workItems[i].requestId == message.responseTo) {
for (var i = 0; i < connection.workItems.length; i++) {
if (connection.workItems[i].requestId === message.responseTo) {
// Get the callback

@@ -435,3 +498,2 @@ workItem = connection.workItems[i];

// Reset timeout counter

@@ -442,3 +504,3 @@ self.numberOfConsecutiveTimeouts = 0;

// this operation
if(workItem.socketTimeout) {
if (workItem && workItem.socketTimeout) {
connection.resetSocketTimeout();

@@ -448,5 +510,11 @@ }

// Log if debug enabled
if(self.logger.isDebug()) {
self.logger.debug(f('message [%s] received from %s:%s'
, message.raw.toString('hex'), self.options.host, self.options.port));
if (self.logger.isDebug()) {
self.logger.debug(
f(
'message [%s] received from %s:%s',
message.raw.toString('hex'),
self.options.host,
self.options.port
)
);
}

@@ -465,5 +533,8 @@

// 2. our current authentication timestamp is from the workItem one, meaning an auth has happened
if(connection.workItems.length == 1 && (connection.workItems[0].authenticating == true
|| (typeof connection.workItems[0].authenticatingTimestamp == 'number'
&& connection.workItems[0].authenticatingTimestamp != self.authenticatingTimestamp))) {
if (
connection.workItems.length === 1 &&
(connection.workItems[0].authenticating === true ||
(typeof connection.workItems[0].authenticatingTimestamp === 'number' &&
connection.workItems[0].authenticatingTimestamp !== self.authenticatingTimestamp))
) {
// Add connection to the list

@@ -474,3 +545,3 @@ connections.push(connection);

// No connections need to be re-authenticated
if(connections.length == 0) {
if (connections.length === 0) {
// Release the connection back to the pool

@@ -485,9 +556,11 @@ moveConnectionBetween(connection, self.inUseConnections, self.availableConnections);

// Authenticate all connections
for(var i = 0; i < connectionCount; i++) {
for (var i = 0; i < connectionCount; i++) {
reauthenticate(self, connections[i], function() {
connectionCount = connectionCount - 1;
if(connectionCount == 0) {
if (connectionCount === 0) {
// Put non authenticated connections in available connections
self.availableConnections = self.availableConnections.concat(nonAuthenticatedConnections);
self.availableConnections = self.availableConnections.concat(
nonAuthenticatedConnections
);
// Release the connection back to the pool

@@ -504,3 +577,3 @@ moveConnectionBetween(connection, self.inUseConnections, self.availableConnections);

// No domain enabled
if(!self.options.domainsEnabled) {
if (!self.options.domainsEnabled) {
return process.nextTick(function() {

@@ -517,3 +590,3 @@ return cb(err, result);

// Keep executing, ensure current message handler does not stop execution
if(!self.executing) {
if (!self.executing) {
process.nextTick(function() {

@@ -525,14 +598,40 @@ _execute(self)();

// Time to dispatch the message if we have a callback
if(!workItem.immediateRelease) {
if (workItem && !workItem.immediateRelease) {
try {
// Parse the message according to the provided options
message.parse(workItem);
} catch(err) {
return handleOperationCallback(self, workItem.cb, MongoError.create(err));
} catch (err) {
return handleOperationCallback(self, workItem.cb, new MongoError(err));
}
// Look for clusterTime, and operationTime and update them if necessary
if (message.documents[0]) {
if (message.documents[0].$clusterTime) {
const $clusterTime = message.documents[0].$clusterTime;
self.topology.clusterTime = $clusterTime;
if (workItem.session != null) {
resolveClusterTime(workItem.session, $clusterTime);
}
}
if (
message.documents[0].operationTime &&
workItem.session &&
workItem.session.supports.causalConsistency
) {
workItem.session.advanceOperationTime(message.documents[0].operationTime);
}
}
// Establish if we have an error
if(workItem.command && message.documents[0] && (message.documents[0].ok == 0 || message.documents[0]['$err']
|| message.documents[0]['errmsg'] || message.documents[0]['code'])) {
return handleOperationCallback(self, workItem.cb, MongoError.create(message.documents[0]));
if (
workItem.command &&
message.documents[0] &&
(message.documents[0].ok === 0 ||
message.documents[0]['$err'] ||
message.documents[0]['errmsg'] ||
message.documents[0]['code'])
) {
return handleOperationCallback(self, workItem.cb, new MongoError(message.documents[0]));
}

@@ -544,6 +643,15 @@

// Return the documents
handleOperationCallback(self, workItem.cb, null, new CommandResult(workItem.fullResult ? message : message.documents[0], connection, message));
handleOperationCallback(
self,
workItem.cb,
null,
new CommandResult(
workItem.fullResult ? message : message.documents[0],
connection,
message
)
);
}
});
}
};
}

@@ -557,6 +665,5 @@

Pool.prototype.socketCount = function() {
return this.availableConnections.length
+ this.inUseConnections.length;
// + this.connectingConnections.length;
}
return this.availableConnections.length + this.inUseConnections.length;
// + this.connectingConnections.length;
};

@@ -569,6 +676,4 @@ /**

Pool.prototype.allConnections = function() {
return this.availableConnections
.concat(this.inUseConnections)
.concat(this.connectingConnections);
}
return this.availableConnections.concat(this.inUseConnections).concat(this.connectingConnections);
};

@@ -582,3 +687,3 @@ /**

return this.allConnections()[0];
}
};

@@ -592,3 +697,3 @@ /**

// We are in a destroyed state
if(this.state == DESTROYED || this.state == DESTROYING) {
if (this.state === DESTROYED || this.state === DESTROYING) {
return false;

@@ -598,13 +703,12 @@ }

// Get connections
var connections = this.availableConnections
.concat(this.inUseConnections);
var connections = this.availableConnections.concat(this.inUseConnections);
// Check if we have any connected connections
for(var i = 0; i < connections.length; i++) {
if(connections[i].isConnected()) return true;
for (var i = 0; i < connections.length; i++) {
if (connections[i].isConnected()) return true;
}
// Might be authenticating, but we are still connected
if(connections.length == 0 && this.authenticating) {
return true
if (connections.length === 0 && this.authenticating) {
return true;
}

@@ -614,3 +718,3 @@

return false;
}
};

@@ -623,4 +727,4 @@ /**

Pool.prototype.isDestroyed = function() {
return this.state == DESTROYED || this.state == DESTROYING;
}
return this.state === DESTROYED || this.state === DESTROYING;
};

@@ -633,4 +737,4 @@ /**

Pool.prototype.isDisconnected = function() {
return this.state == DISCONNECTED;
}
return this.state === DISCONNECTED;
};

@@ -642,3 +746,3 @@ /**

Pool.prototype.connect = function() {
if(this.state != DISCONNECTED) {
if (this.state !== DISCONNECTED) {
throw new MongoError('connection in unlawful state ' + this.state);

@@ -658,3 +762,3 @@ }

connection.once('connect', function(connection) {
if(self.state == DESTROYED || self.state == DESTROYING) return self.destroy();
if (self.state === DESTROYED || self.state === DESTROYING) return self.destroy();

@@ -664,3 +768,3 @@ // If we are in a topology, delegate the auth to it

// arbiter
if(self.options.inTopology) {
if (self.options.inTopology) {
// Set connected mode

@@ -678,6 +782,6 @@ stateTransition(self, CONNECTED);

reauthenticate(self, connection, function(err) {
if(self.state == DESTROYED || self.state == DESTROYING) return self.destroy();
if (self.state === DESTROYED || self.state === DESTROYING) return self.destroy();
// We have an error emit it
if(err) {
if (err) {
// Destroy the pool

@@ -691,6 +795,6 @@ self.destroy();

authenticate(self, args, connection, function(err) {
if(self.state == DESTROYED || self.state == DESTROYING) return self.destroy();
if (self.state === DESTROYED || self.state === DESTROYING) return self.destroy();
// We have an error emit it
if(err) {
if (err) {
// Destroy the pool

@@ -707,2 +811,7 @@ self.destroy();

// if we have a minPoolSize, create a connection
if (self.minSize) {
for (let i = 0; i < self.minSize; i++) _createConnection(self);
}
// Emit the connect event

@@ -722,3 +831,3 @@ self.emit('connect', self);

connection.connect();
} catch(err) {
} catch (err) {
// SSL or something threw on connect

@@ -729,3 +838,3 @@ process.nextTick(function() {

}
}
};

@@ -746,4 +855,4 @@ /**

// If we don't have the mechanism fail
if(self.authProviders[mechanism] == null && mechanism != 'default') {
throw new MongoError(f("auth provider %s does not exist", mechanism));
if (self.authProviders[mechanism] == null && mechanism !== 'default') {
throw new MongoError(f('auth provider %s does not exist', mechanism));
}

@@ -767,3 +876,3 @@

// No connections available, return
if(connectionsCount == 0) {
if (connectionsCount === 0) {
self.authenticating = false;

@@ -774,3 +883,3 @@ return callback(null);

// Authenticate the connections
for(var i = 0; i < connections.length; i++) {
for (var i = 0; i < connections.length; i++) {
authenticate(self, args, connections[i], function(err) {

@@ -780,6 +889,6 @@ connectionsCount = connectionsCount - 1;

// Store the error
if(err) error = err;
if (err) error = err;
// Processed all connections
if(connectionsCount == 0) {
if (connectionsCount === 0) {
// Auth finished

@@ -790,7 +899,13 @@ self.authenticating = false;

// We had an error, return it
if(error) {
if (error) {
// Log the error
if(self.logger.isError()) {
self.logger.error(f('[%s] failed to authenticate against server %s:%s'
, self.id, self.options.host, self.options.port));
if (self.logger.isError()) {
self.logger.error(
f(
'[%s] failed to authenticate against server %s:%s',
self.id,
self.options.host,
self.options.port
)
);
}

@@ -808,6 +923,6 @@

function waitForLogout(self, cb) {
if(!self.loggingout) return cb();
if (!self.loggingout) return cb();
setTimeout(function() {
waitForLogout(self, cb);
}, 1)
}, 1);
}

@@ -826,3 +941,3 @@

});
}
};

@@ -837,7 +952,7 @@ /**

var self = this;
if(typeof dbName != 'string') {
if (typeof dbName !== 'string') {
throw new MongoError('logout method requires a db name as first argument');
}
if(typeof callback != 'function') {
if (typeof callback !== 'function') {
throw new MongoError('logout method requires a callback');

@@ -856,16 +971,23 @@ }

// Send logout command over all the connections
for(var i = 0; i < connections.length; i++) {
write(self)(connections[i], new Query(this.options.bson
, f('%s.$cmd', dbName)
, {logout:1}, {numberToSkip: 0, numberToReturn: 1}), function(err) {
count = count - 1;
if(err) error = err;
for (var i = 0; i < connections.length; i++) {
write(self)(
connections[i],
new Query(
this.options.bson,
f('%s.$cmd', dbName),
{ logout: 1 },
{ numberToSkip: 0, numberToReturn: 1 }
),
function(err) {
count = count - 1;
if (err) error = err;
if(count == 0) {
self.loggingout = false;
callback(error);
if (count === 0) {
self.loggingout = false;
callback(error);
}
}
});
);
}
}
};

@@ -884,3 +1006,3 @@ /**

});
}
};

@@ -895,3 +1017,3 @@ // Events

// Remove all listeners
for(var i = 0; i < events.length; i++) {
for (var i = 0; i < events.length; i++) {
c.removeAllListeners(events[i]);

@@ -920,3 +1042,3 @@ }

// Do not try again if the pool is already dead
if(this.state == DESTROYED || self.state == DESTROYING) return;
if (this.state === DESTROYED || self.state === DESTROYING) return;
// Set state to destroyed

@@ -926,3 +1048,3 @@ stateTransition(this, DESTROYING);

// Are we force closing
if(force) {
if (force) {
// Get all the known connections

@@ -936,5 +1058,5 @@ var connections = self.availableConnections

// an error
while(self.queue.length > 0) {
while (self.queue.length > 0) {
var workItem = self.queue.shift();
if(typeof workItem.cb == 'function') {
if (typeof workItem.cb === 'function') {
workItem.cb(new MongoError('Pool was force destroyed'));

@@ -963,3 +1085,3 @@ }

if(self.queue.length == 0) {
if (self.queue.length === 0) {
// Get all the known connections

@@ -972,6 +1094,6 @@ var connections = self.availableConnections

// Check if we have any in flight operations
for(var i = 0; i < connections.length; i++) {
for (var i = 0; i < connections.length; i++) {
// There is an operation still in flight, reschedule a
// check waiting for it to drain
if(connections[i].workItems.length > 0) {
if (connections[i].workItems.length > 0) {
return setTimeout(checkStatus, 1);

@@ -982,4 +1104,3 @@ }

destroy(self, connections);
// } else if (self.queue.length > 0 && !this.reconnectId) {
// } else if (self.queue.length > 0 && !this.reconnectId) {
} else {

@@ -995,4 +1116,54 @@ // Ensure we empty the queue

checkStatus();
}
};
// Prepare the buffer that Pool.prototype.write() uses to send to the server
var serializeCommands = function(self, commands, result, callback) {
// Base case when there are no more commands to serialize
if (commands.length === 0) return callback(null, result);
// Pop off the zeroth command and serialize it
var thisCommand = commands.shift();
var originalCommandBuffer = thisCommand.toBin();
// Check whether we and the server have agreed to use a compressor
if (self.options.agreedCompressor && !hasUncompressibleCommands(thisCommand)) {
// Transform originalCommandBuffer into OP_COMPRESSED
var concatenatedOriginalCommandBuffer = Buffer.concat(originalCommandBuffer);
var messageToBeCompressed = concatenatedOriginalCommandBuffer.slice(MESSAGE_HEADER_SIZE);
// Extract information needed for OP_COMPRESSED from the uncompressed message
var originalCommandOpCode = concatenatedOriginalCommandBuffer.readInt32LE(12);
// Compress the message body
compress(self, messageToBeCompressed, function(err, compressedMessage) {
if (err) return callback(err, null);
// Create the msgHeader of OP_COMPRESSED
var msgHeader = new Buffer(MESSAGE_HEADER_SIZE);
msgHeader.writeInt32LE(MESSAGE_HEADER_SIZE + 9 + compressedMessage.length, 0); // messageLength
msgHeader.writeInt32LE(thisCommand.requestId, 4); // requestID
msgHeader.writeInt32LE(0, 8); // responseTo (zero)
msgHeader.writeInt32LE(opcodes.OP_COMPRESSED, 12); // opCode
// Create the compression details of OP_COMPRESSED
var compressionDetails = new Buffer(9);
compressionDetails.writeInt32LE(originalCommandOpCode, 0); // originalOpcode
compressionDetails.writeInt32LE(messageToBeCompressed.length, 4); // Size of the uncompressed compressedMessage, excluding the MsgHeader
compressionDetails.writeUInt8(compressorIDs[self.options.agreedCompressor], 8); // compressorID
// Push the concatenation of the OP_COMPRESSED message onto results
result.push(Buffer.concat([msgHeader, compressionDetails, compressedMessage]));
// Continue recursing through the commands array
serializeCommands(self, commands, result, callback);
});
} else {
// Push the serialization of the command onto results
result.push(originalCommandBuffer);
// Continue recursing through the commands array
serializeCommands(self, commands, result, callback);
}
};
/**

@@ -1006,3 +1177,3 @@ * Write a message to MongoDB

// Ensure we have a callback
if(typeof options == 'function') {
if (typeof options === 'function') {
cb = options;

@@ -1014,9 +1185,14 @@ }

// We need to have a callback function unless the message returns no response
if (!(typeof cb === 'function') && !options.noResponse) {
throw new MongoError('write method must provide a callback');
}
// Pool was destroyed error out
if(this.state == DESTROYED || this.state == DESTROYING) {
if (this.state === DESTROYED || this.state === DESTROYING) {
// Callback with an error
if(cb) {
if (cb) {
try {
cb(new MongoError('pool destroyed'));
} catch(err) {
} catch (err) {
process.nextTick(function() {

@@ -1031,4 +1207,3 @@ throw err;

if(this.options.domainsEnabled
&& process.domain && typeof cb === "function") {
if (this.options.domainsEnabled && process.domain && typeof cb === 'function') {
// if we have a domain bind to it

@@ -1038,3 +1213,6 @@ var oldCb = cb;

// v8 - argumentsToArray one-liner
var args = new Array(arguments.length); for(var i = 0; i < arguments.length; i++) { args[i] = arguments[i]; }
var args = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
// bounce off event loop so domain switch takes place

@@ -1049,35 +1227,24 @@ process.nextTick(function() {

var operation = {
cb: cb, raw: false, promoteLongs: true, promoteValues: true, promoteBuffers: false, fullResult: false
cb: cb,
raw: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
fullResult: false
};
var buffer = null
if(Array.isArray(commands)) {
buffer = [];
for(var i = 0; i < commands.length; i++) {
buffer.push(commands[i].toBin());
}
// Get the requestId
operation.requestId = commands[commands.length - 1].requestId;
} else {
operation.requestId = commands.requestId;
buffer = commands.toBin();
}
// Set the buffers
operation.buffer = buffer;
// Set the options for the parsing
operation.promoteLongs = typeof options.promoteLongs == 'boolean' ? options.promoteLongs : true;
operation.promoteValues = typeof options.promoteValues == 'boolean' ? options.promoteValues : true;
operation.promoteBuffers = typeof options.promoteBuffers == 'boolean' ? options.promoteBuffers : false;
operation.raw = typeof options.raw == 'boolean' ? options.raw : false;
operation.immediateRelease = typeof options.immediateRelease == 'boolean' ? options.immediateRelease : false;
operation.promoteLongs = typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true;
operation.promoteValues =
typeof options.promoteValues === 'boolean' ? options.promoteValues : true;
operation.promoteBuffers =
typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false;
operation.raw = typeof options.raw === 'boolean' ? options.raw : false;
operation.immediateRelease =
typeof options.immediateRelease === 'boolean' ? options.immediateRelease : false;
operation.documentsReturnedIn = options.documentsReturnedIn;
operation.command = typeof options.command == 'boolean' ? options.command : false;
operation.fullResult = typeof options.fullResult == 'boolean' ? options.fullResult : false;
operation.noResponse = typeof options.noResponse == 'boolean' ? options.noResponse : false;
// operation.requestId = options.requestId;
operation.command = typeof options.command === 'boolean' ? options.command : false;
operation.fullResult = typeof options.fullResult === 'boolean' ? options.fullResult : false;
operation.noResponse = typeof options.noResponse === 'boolean' ? options.noResponse : false;
operation.session = options.session || null;

@@ -1088,31 +1255,93 @@ // Optional per operation socketTimeout

// Custom socket Timeout
if(options.socketTimeout) {
if (options.socketTimeout) {
operation.socketTimeout = options.socketTimeout;
}
// We need to have a callback function unless the message returns no response
if(!(typeof cb == 'function') && !options.noResponse) {
throw new MongoError('write method must provide a callback');
// Ensure commands is an array
if (!Array.isArray(commands)) {
commands = [commands];
}
// If we have a monitoring operation schedule as the very first operation
// Otherwise add to back of queue
if(options.monitoring) {
this.queue.unshift(operation);
} else {
this.queue.push(operation);
}
// Get the requestId
operation.requestId = commands[commands.length - 1].requestId;
// Attempt to execute the operation
if(!self.executing) {
process.nextTick(function() {
_execute(self)();
if (hasSessionSupport(this.topology)) {
let sessionOptions = {};
if (this.topology.clusterTime) {
sessionOptions = { $clusterTime: this.topology.clusterTime };
}
if (operation.session) {
// TODO: reenable when sessions development is complete
// if (operation.session.topology !== this.topology) {
// return cb(
// new MongoError('Sessions may only be used with the client they were created from')
// );
// }
if (operation.session.hasEnded) {
return cb(new MongoError('Use of expired sessions is not permitted'));
}
if (
operation.session.clusterTime &&
operation.session.clusterTime.clusterTime.greaterThan(
sessionOptions.$clusterTime.clusterTime
)
) {
sessionOptions.$clusterTime = operation.session.clusterTime;
}
sessionOptions.lsid = operation.session.id;
// update the `lastUse` of the acquired ServerSession
operation.session.serverSession.lastUse = Date.now();
}
// decorate the commands with session-specific details
commands.forEach(command => {
if (command instanceof Query) {
Object.assign(command.query, sessionOptions);
} else {
Object.assign(command, sessionOptions);
}
});
}
}
// Prepare the operation buffer
serializeCommands(self, commands, [], function(err, serializedCommands) {
if (err) throw err;
// Set the operation's buffer to the serialization of the commands
operation.buffer = serializedCommands;
// If we have a monitoring operation schedule as the very first operation
// Otherwise add to back of queue
if (options.monitoring) {
self.queue.unshift(operation);
} else {
self.queue.push(operation);
}
// Attempt to execute the operation
if (!self.executing) {
process.nextTick(function() {
_execute(self)();
});
}
});
};
// Return whether a command contains an uncompressible command term
// Will return true if command contains no uncompressible command terms
var hasUncompressibleCommands = function(command) {
return uncompressibleCommands.some(function(cmd) {
return command.query.hasOwnProperty(cmd);
});
};
// Remove connection method
function remove(connection, connections) {
for(var i = 0; i < connections.length; i++) {
if(connections[i] === connection) {
for (var i = 0; i < connections.length; i++) {
if (connections[i] === connection) {
connections.splice(i, 1);

@@ -1125,13 +1354,13 @@ return true;

function removeConnection(self, connection) {
if(remove(connection, self.availableConnections)) return;
if(remove(connection, self.inUseConnections)) return;
if(remove(connection, self.connectingConnections)) return;
if(remove(connection, self.nonAuthenticatedConnections)) return;
if (remove(connection, self.availableConnections)) return;
if (remove(connection, self.inUseConnections)) return;
if (remove(connection, self.connectingConnections)) return;
if (remove(connection, self.nonAuthenticatedConnections)) return;
}
// All event handlers
var handlers = ["close", "message", "error", "timeout", "parseError", "connect"];
var handlers = ['close', 'message', 'error', 'timeout', 'parseError', 'connect'];
function _createConnection(self) {
if(self.state == DESTROYED || self.state == DESTROYING) {
if (self.state === DESTROYED || self.state === DESTROYING) {
return;

@@ -1152,7 +1381,7 @@ }

// Start reconnection attempts
if(!self.reconnectId && self.options.reconnect) {
if (!self.reconnectId && self.options.reconnect) {
self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
}
}
}
};
};

@@ -1163,3 +1392,3 @@ // Handle successful connection

// Destroyed state return
if(self.state == DESTROYED || self.state == DESTROYING) {
if (self.state === DESTROYED || self.state === DESTROYING) {
// Remove the connection from the list

@@ -1183,3 +1412,3 @@ removeConnection(self, _connection);

reauthenticate(self, _connection, function(err) {
if(self.state == DESTROYED || self.state == DESTROYING) {
if (self.state === DESTROYED || self.state === DESTROYING) {
return _connection.destroy();

@@ -1191,3 +1420,3 @@ }

// Handle error
if(err) {
if (err) {
return _connection.destroy();

@@ -1199,3 +1428,3 @@ }

// As we need to apply the credentials first
if(self.authenticating) {
if (self.authenticating) {
self.nonAuthenticatedConnections.push(_connection);

@@ -1209,4 +1438,4 @@ } else {

});
}
}
};
};

@@ -1225,7 +1454,9 @@ // Add all handlers

function flushMonitoringOperations(queue) {
for(var i = 0; i < queue.length; i++) {
if(queue[i].monitoring) {
for (var i = 0; i < queue.length; i++) {
if (queue[i].monitoring) {
var workItem = queue[i];
queue.splice(i, 1);
workItem.cb(new MongoError({ message: 'no connection available for monitoring', driver:true }));
workItem.cb(
new MongoError({ message: 'no connection available for monitoring', driver: true })
);
}

@@ -1237,5 +1468,5 @@ }

return function() {
if(self.state == DESTROYED) return;
if (self.state === DESTROYED) return;
// Already executing, skip
if(self.executing) return;
if (self.executing) return;
// Set pool as executing

@@ -1246,3 +1477,3 @@ self.executing = true;

function waitForAuth(cb) {
if(!self.authenticating) return cb();
if (!self.authenticating) return cb();
// Wait for a milisecond and try again

@@ -1259,3 +1490,3 @@ setTimeout(function() {

// operations
if(self.connectingConnections.length > 0) {
if (self.connectingConnections.length > 0) {
return;

@@ -1265,10 +1496,12 @@ }

// As long as we have available connections
while(true) {
// eslint-disable-next-line
while (true) {
// Total availble connections
var totalConnections = self.availableConnections.length
+ self.connectingConnections.length
+ self.inUseConnections.length;
var totalConnections =
self.availableConnections.length +
self.connectingConnections.length +
self.inUseConnections.length;
// No available connections available, flush any monitoring ops
if(self.availableConnections.length == 0) {
if (self.availableConnections.length === 0) {
// Flush any monitoring operations

@@ -1280,3 +1513,3 @@ flushMonitoringOperations(self.queue);

// No queue break
if(self.queue.length == 0) {
if (self.queue.length === 0) {
break;

@@ -1291,4 +1524,4 @@ }

// Get a list of all connections
for(var i = 0; i < self.availableConnections.length; i++) {
if(self.availableConnections[i].workItems.length == 0) {
for (var i = 0; i < self.availableConnections.length; i++) {
if (self.availableConnections[i].workItems.length === 0) {
connections.push(self.availableConnections[i]);

@@ -1299,4 +1532,5 @@ }

// No connection found that has no work on it, just pick one for pipelining
if(connections.length == 0) {
connection = self.availableConnections[self.connectionIndex++ % self.availableConnections.length];
if (connections.length === 0) {
connection =
self.availableConnections[self.connectionIndex++ % self.availableConnections.length];
} else {

@@ -1307,3 +1541,3 @@ connection = connections[self.connectionIndex++ % connections.length];

// Is the connection connected
if(connection.isConnected()) {
if (connection.isConnected()) {
// Get the next work item

@@ -1318,12 +1552,14 @@ var workItem = self.queue.shift();

for (var i = 0; i < self.availableConnections.length; i++) {
for (i = 0; i < self.availableConnections.length; i++) {
// If the connection is connected
// And there are no pending workItems on it
// Then we can safely use it for monitoring.
if(self.availableConnections[i].isConnected()
&& self.availableConnections[i].workItems.length == 0) {
foundValidConnection = true;
connection = self.availableConnections[i];
break;
}
if (
self.availableConnections[i].isConnected() &&
self.availableConnections[i].workItems.length === 0
) {
foundValidConnection = true;
connection = self.availableConnections[i];
break;
}
}

@@ -1333,3 +1569,3 @@

// if possible and break from the loop
if(!foundValidConnection) {
if (!foundValidConnection) {
// Put workItem back on the queue

@@ -1339,4 +1575,3 @@ self.queue.unshift(workItem);

// Attempt to grow the pool if it's not yet maxsize
if(totalConnections < self.options.size
&& self.queue.length > 0) {
if (totalConnections < self.options.size && self.queue.length > 0) {
// Create a new connection

@@ -1356,6 +1591,6 @@ _createConnection(self);

// Don't execute operation until we have a full pool
if(totalConnections < self.options.size) {
if (totalConnections < self.options.size) {
// Connection has work items, then put it back on the queue
// and create a new connection
if(connection.workItems.length > 0) {
if (connection.workItems.length > 0) {
// Lets put the workItem back on the list

@@ -1384,3 +1619,3 @@ self.queue.unshift(workItem);

// as long as there is an expected response
if (! workItem.noResponse) {
if (!workItem.noResponse) {
connection.workItems.push(workItem);

@@ -1390,3 +1625,3 @@ }

// We have a custom socketTimeout
if(!workItem.immediateRelease && typeof workItem.socketTimeout == 'number') {
if (!workItem.immediateRelease && typeof workItem.socketTimeout === 'number') {
connection.setSocketTimeout(workItem.socketTimeout);

@@ -1399,5 +1634,5 @@ }

// Put operation on the wire
if(Array.isArray(buffer)) {
for(var i = 0; i < buffer.length; i++) {
writeSuccessful = connection.write(buffer[i])
if (Array.isArray(buffer)) {
for (i = 0; i < buffer.length; i++) {
writeSuccessful = connection.write(buffer[i]);
}

@@ -1408,6 +1643,6 @@ } else {

if(writeSuccessful && workItem.immediateRelease && self.authenticating) {
if (writeSuccessful && workItem.immediateRelease && self.authenticating) {
removeConnection(self, connection);
self.nonAuthenticatedConnections.push(connection);
} else if(writeSuccessful === false) {
} else if (writeSuccessful === false) {
// If write not successful put back on queue

@@ -1430,3 +1665,3 @@ self.queue.unshift(workItem);

self.executing = false;
}
};
}

@@ -1433,0 +1668,0 @@

@@ -1,2 +0,2 @@

"use strict";
'use strict';

@@ -9,14 +9,16 @@ var f = require('util').format,

Object.defineProperty(obj, prop.name, {
enumerable:true,
set: function(value) {
if(typeof value != 'boolean') throw new Error(f("%s required a boolean", prop.name));
// Flip the bit to 1
if(value == true) values.flags |= flag;
// Flip the bit to 0 if it's set, otherwise ignore
if(value == false && (values.flags & flag) == flag) values.flags ^= flag;
prop.value = value;
}
, get: function() { return prop.value; }
enumerable: true,
set: function(value) {
if (typeof value !== 'boolean') throw new Error(f('%s required a boolean', prop.name));
// Flip the bit to 1
if (value === true) values.flags |= flag;
// Flip the bit to 0 if it's set, otherwise ignore
if (value === false && (values.flags & flag) === flag) values.flags ^= flag;
prop.value = value;
},
get: function() {
return prop.value;
}
});
}
};

@@ -26,6 +28,6 @@ // Set property function

Object.defineProperty(obj, propName, {
enumerable:true,
enumerable: true,
get: function() {
// Not parsed yet, parse it
if(values[fieldName] == null && obj.isParsed && !obj.isParsed()) {
if (values[fieldName] == null && obj.isParsed && !obj.isParsed()) {
obj.parse();

@@ -35,3 +37,3 @@ }

// Do we have a post processing function
if(typeof func == 'function') return func(values[fieldName]);
if (typeof func === 'function') return func(values[fieldName]);
// Return raw value

@@ -41,3 +43,3 @@ return values[fieldName];

});
}
};

@@ -47,8 +49,8 @@ // Set simple property

Object.defineProperty(obj, name, {
enumerable:true,
enumerable: true,
get: function() {
return value
return value;
}
});
}
};

@@ -58,5 +60,5 @@ // Shallow copy

tObj = tObj || {};
for(var name in fObj) tObj[name] = fObj[name];
for (var name in fObj) tObj[name] = fObj[name];
return tObj;
}
};

@@ -70,3 +72,3 @@ var debugOptions = function(debugFields, options) {

return finaloptions;
}
};

@@ -79,11 +81,35 @@ var retrieveBSON = function() {

var optionalBSON = require_optional('bson-ext');
if(optionalBSON) {
if (optionalBSON) {
optionalBSON.native = true;
return optionalBSON;
}
} catch(err) {}
} catch (err) {} // eslint-disable-line
return BSON;
}
};
// Throw an error if an attempt to use Snappy is made when Snappy is not installed
var noSnappyWarning = function() {
throw new Error(
'Attempted to use Snappy compression, but Snappy is not installed. Install or disable Snappy compression and try again.'
);
};
// Facilitate loading Snappy optionally
var retrieveSnappy = function() {
var snappy = null;
try {
snappy = require_optional('snappy');
} catch (error) {} // eslint-disable-line
if (!snappy) {
snappy = {
compress: noSnappyWarning,
uncompress: noSnappyWarning,
compressSync: noSnappyWarning,
uncompressSync: noSnappyWarning
};
}
return snappy;
};
exports.setProperty = setProperty;

@@ -95,1 +121,2 @@ exports.getProperty = getProperty;

exports.retrieveBSON = retrieveBSON;
exports.retrieveSnappy = retrieveSnappy;

@@ -1,7 +0,8 @@

"use strict";
'use strict';
var Logger = require('./connection/logger')
, retrieveBSON = require('./connection/utils').retrieveBSON
, MongoError = require('./error')
, f = require('util').format;
var Logger = require('./connection/logger'),
retrieveBSON = require('./connection/utils').retrieveBSON,
MongoError = require('./error').MongoError,
MongoNetworkError = require('./error').MongoNetworkError,
f = require('util').format;

@@ -92,29 +93,31 @@ var BSON = retrieveBSON(),

this.cursorState = {
cursorId: null
, cmd: cmd
, documents: options.documents || []
, cursorIndex: 0
, dead: false
, killed: false
, init: false
, notified: false
, limit: options.limit || cmd.limit || 0
, skip: options.skip || cmd.skip || 0
, batchSize: options.batchSize || cmd.batchSize || 1000
, currentLimit: 0
cursorId: null,
cmd: cmd,
documents: options.documents || [],
cursorIndex: 0,
dead: false,
killed: false,
init: false,
notified: false,
limit: options.limit || cmd.limit || 0,
skip: options.skip || cmd.skip || 0,
batchSize: options.batchSize || cmd.batchSize || 1000,
currentLimit: 0,
// Result field name if not a cursor (contains the array of results)
, transforms: options.transforms
}
transforms: options.transforms
};
// Add promoteLong to cursor state
if(typeof topologyOptions.promoteLongs == 'boolean') {
if (typeof topologyOptions.promoteLongs === 'boolean') {
this.cursorState.promoteLongs = topologyOptions.promoteLongs;
} else if(typeof options.promoteLongs == 'boolean') {
} else if (typeof options.promoteLongs === 'boolean') {
this.cursorState.promoteLongs = options.promoteLongs;
} else if (typeof options.session === 'object') {
this.cursorState.session = options.session;
}
// Add promoteValues to cursor state
if(typeof topologyOptions.promoteValues == 'boolean') {
if (typeof topologyOptions.promoteValues === 'boolean') {
this.cursorState.promoteValues = topologyOptions.promoteValues;
} else if(typeof options.promoteValues == 'boolean') {
} else if (typeof options.promoteValues === 'boolean') {
this.cursorState.promoteValues = options.promoteValues;

@@ -124,8 +127,12 @@ }

// Add promoteBuffers to cursor state
if(typeof topologyOptions.promoteBuffers == 'boolean') {
if (typeof topologyOptions.promoteBuffers === 'boolean') {
this.cursorState.promoteBuffers = topologyOptions.promoteBuffers;
} else if(typeof options.promoteBuffers == 'boolean') {
} else if (typeof options.promoteBuffers === 'boolean') {
this.cursorState.promoteBuffers = options.promoteBuffers;
}
if (topologyOptions.reconnect) {
this.cursorState.reconnect = topologyOptions.reconnect;
}
// Logger

@@ -136,34 +143,34 @@ this.logger = Logger('Cursor', topologyOptions);

// Did we pass in a cursor id
if(typeof cmd == 'number') {
if (typeof cmd === 'number') {
this.cursorState.cursorId = Long.fromNumber(cmd);
this.cursorState.lastCursorId = this.cursorState.cursorId;
} else if(cmd instanceof Long) {
} else if (cmd instanceof Long) {
this.cursorState.cursorId = cmd;
this.cursorState.lastCursorId = cmd;
}
}
};
Cursor.prototype.setCursorBatchSize = function(value) {
this.cursorState.batchSize = value;
}
};
Cursor.prototype.cursorBatchSize = function() {
return this.cursorState.batchSize;
}
};
Cursor.prototype.setCursorLimit = function(value) {
this.cursorState.limit = value;
}
};
Cursor.prototype.cursorLimit = function() {
return this.cursorState.limit;
}
};
Cursor.prototype.setCursorSkip = function(value) {
this.cursorState.skip = value;
}
};
Cursor.prototype.cursorSkip = function() {
return this.cursorState.skip;
}
};

@@ -175,3 +182,3 @@ //

callback(err, result);
} catch(err) {
} catch (err) {
process.nextTick(function() {

@@ -181,3 +188,3 @@ throw err;

}
}
};

@@ -188,10 +195,14 @@ // Internal methods

if(self.logger.isDebug()) {
self.logger.debug(f('issue initial query [%s] with flags [%s]'
, JSON.stringify(self.cmd)
, JSON.stringify(self.query)));
if (self.logger.isDebug()) {
self.logger.debug(
f(
'issue initial query [%s] with flags [%s]',
JSON.stringify(self.cmd),
JSON.stringify(self.query)
)
);
}
var queryCallback = function(err, r) {
if(err) return callback(err);
if (err) return callback(err);

@@ -202,45 +213,44 @@ // Get the raw message

// Query failure bit set
if(result.queryFailure) {
return callback(MongoError.create(result.documents[0]), null);
if (result.queryFailure) {
return callback(new MongoError(result.documents[0]), null);
}
// Check if we have a command cursor
if(Array.isArray(result.documents) && result.documents.length == 1
&& (!self.cmd.find || (self.cmd.find && self.cmd.virtual == false))
&& (result.documents[0].cursor != 'string'
|| result.documents[0]['$err']
|| result.documents[0]['errmsg']
|| Array.isArray(result.documents[0].result))
) {
if (
Array.isArray(result.documents) &&
result.documents.length === 1 &&
(!self.cmd.find || (self.cmd.find && self.cmd.virtual === false)) &&
(result.documents[0].cursor !== 'string' ||
result.documents[0]['$err'] ||
result.documents[0]['errmsg'] ||
Array.isArray(result.documents[0].result))
) {
// We have a an error document return the error
if(result.documents[0]['$err']
|| result.documents[0]['errmsg']) {
return callback(MongoError.create(result.documents[0]), null);
if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
return callback(new MongoError(result.documents[0]), null);
}
// We have a cursor document
if(result.documents[0].cursor != null
&& typeof result.documents[0].cursor != 'string') {
var id = result.documents[0].cursor.id;
// If we have a namespace change set the new namespace for getmores
if(result.documents[0].cursor.ns) {
self.ns = result.documents[0].cursor.ns;
}
// Promote id to long if needed
self.cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id;
self.cursorState.lastCursorId = self.cursorState.cursorId;
// If we have a firstBatch set it
if(Array.isArray(result.documents[0].cursor.firstBatch)) {
self.cursorState.documents = result.documents[0].cursor.firstBatch;//.reverse();
}
if (result.documents[0].cursor != null && typeof result.documents[0].cursor !== 'string') {
var id = result.documents[0].cursor.id;
// If we have a namespace change set the new namespace for getmores
if (result.documents[0].cursor.ns) {
self.ns = result.documents[0].cursor.ns;
}
// Promote id to long if needed
self.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
self.cursorState.lastCursorId = self.cursorState.cursorId;
// If we have a firstBatch set it
if (Array.isArray(result.documents[0].cursor.firstBatch)) {
self.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
}
// Return after processing command cursor
return callback(null, null);
// Return after processing command cursor
return callback(null, result);
}
if(Array.isArray(result.documents[0].result)) {
if (Array.isArray(result.documents[0].result)) {
self.cursorState.documents = result.documents[0].result;
self.cursorState.cursorId = Long.ZERO;
return callback(null, null);
return callback(null, result);
}

@@ -255,3 +265,3 @@ }

// Transform the results with passed in transformation method if provided
if(self.cursorState.transforms && typeof self.cursorState.transforms.query == 'function') {
if (self.cursorState.transforms && typeof self.cursorState.transforms.query === 'function') {
self.cursorState.documents = self.cursorState.transforms.query(result);

@@ -261,4 +271,4 @@ }

// Return callback
callback(null, null);
}
callback(null, result);
};

@@ -269,3 +279,3 @@ // Options passed to the pool

// If we have a raw query decorate the function
if(self.options.raw || self.cmd.raw) {
if (self.options.raw || self.cmd.raw) {
// queryCallback.raw = self.options.raw || self.cmd.raw;

@@ -276,3 +286,3 @@ queryOptions.raw = self.options.raw || self.cmd.raw;

// Do we have documentsReturnedIn set on the query
if(typeof self.query.documentsReturnedIn == 'string') {
if (typeof self.query.documentsReturnedIn === 'string') {
// queryCallback.documentsReturnedIn = self.query.documentsReturnedIn;

@@ -283,3 +293,3 @@ queryOptions.documentsReturnedIn = self.query.documentsReturnedIn;

// Add promote Long value if defined
if(typeof self.cursorState.promoteLongs == 'boolean') {
if (typeof self.cursorState.promoteLongs === 'boolean') {
queryOptions.promoteLongs = self.cursorState.promoteLongs;

@@ -289,3 +299,3 @@ }

// Add promote values if defined
if(typeof self.cursorState.promoteValues == 'boolean') {
if (typeof self.cursorState.promoteValues === 'boolean') {
queryOptions.promoteValues = self.cursorState.promoteValues;

@@ -295,11 +305,17 @@ }

// Add promote values if defined
if(typeof self.cursorState.promoteBuffers == 'boolean') {
if (typeof self.cursorState.promoteBuffers === 'boolean') {
queryOptions.promoteBuffers = self.cursorState.promoteBuffers;
}
if (typeof self.cursorState.session === 'object') {
queryOptions.session = self.cursorState.session;
}
// Write the initial command out
self.server.s.pool.write(self.query, queryOptions, queryCallback);
}
};
Cursor.prototype._getmore = function(callback) {
if(this.logger.isDebug()) this.logger.debug(f('schedule getMore call for query [%s]', JSON.stringify(this.query)))
if (this.logger.isDebug())
this.logger.debug(f('schedule getMore call for query [%s]', JSON.stringify(this.query)));
// Determine if it's a raw query

@@ -310,4 +326,6 @@ var raw = this.options.raw || this.cmd.raw;

var batchSize = this.cursorState.batchSize;
if(this.cursorState.limit > 0
&& ((this.cursorState.currentLimit + batchSize) > this.cursorState.limit)) {
if (
this.cursorState.limit > 0 &&
this.cursorState.currentLimit + batchSize > this.cursorState.limit
) {
batchSize = this.cursorState.limit - this.cursorState.currentLimit;

@@ -320,4 +338,13 @@ }

// We have a wire protocol handler
this.server.wireProtocolHandler.getMore(this.bson, this.ns, this.cursorState, batchSize, raw, pool, this.options, callback);
}
this.server.wireProtocolHandler.getMore(
this.bson,
this.ns,
this.cursorState,
batchSize,
raw,
pool,
this.options,
callback
);
};

@@ -332,4 +359,8 @@ Cursor.prototype._killcursor = function(callback) {

// If no cursor id just return
if(this.cursorState.cursorId == null || this.cursorState.cursorId.isZero() || this.cursorState.init == false) {
if(callback) callback(null, null);
if (
this.cursorState.cursorId == null ||
this.cursorState.cursorId.isZero() ||
this.cursorState.init === false
) {
if (callback) callback(null, null);
return;

@@ -341,4 +372,4 @@ }

// Execute command
this.server.wireProtocolHandler.killCursor(this.bson, this.ns, this.cursorState.cursorId, pool, callback);
}
this.server.wireProtocolHandler.killCursor(this.bson, this.ns, this.cursorState, pool, callback);
};

@@ -352,3 +383,3 @@ /**

return this.topology.cursor(this.ns, this.cmd, this.options);
}
};

@@ -361,4 +392,4 @@ /**

Cursor.prototype.isDead = function() {
return this.cursorState.dead == true;
}
return this.cursorState.dead === true;
};

@@ -371,4 +402,4 @@ /**

Cursor.prototype.isKilled = function() {
return this.cursorState.killed == true;
}
return this.cursorState.killed === true;
};

@@ -381,4 +412,4 @@ /**

Cursor.prototype.isNotified = function() {
return this.cursorState.notified == true;
}
return this.cursorState.notified === true;
};

@@ -392,3 +423,3 @@ /**

return this.cursorState.documents.length - this.cursorState.cursorIndex;
}
};

@@ -403,8 +434,11 @@ /**

var length = number < unreadDocumentsLength ? number : unreadDocumentsLength;
var elements = this.cursorState.documents.slice(this.cursorState.cursorIndex, this.cursorState.cursorIndex + length);
var elements = this.cursorState.documents.slice(
this.cursorState.cursorIndex,
this.cursorState.cursorIndex + length
);
// Transform the doc with passed in transformation method if provided
if(this.cursorState.transforms && typeof this.cursorState.transforms.doc == 'function') {
if (this.cursorState.transforms && typeof this.cursorState.transforms.doc === 'function') {
// Transform all the elements
for(var i = 0; i < elements.length; i++) {
for (var i = 0; i < elements.length; i++) {
elements[i] = this.cursorState.transforms.doc(elements[i]);

@@ -416,4 +450,7 @@ }

// Just return the number of elements up to the limit
if(this.cursorState.limit > 0 && (this.cursorState.currentLimit + elements.length) > this.cursorState.limit) {
elements = elements.slice(0, (this.cursorState.limit - this.cursorState.currentLimit));
if (
this.cursorState.limit > 0 &&
this.cursorState.currentLimit + elements.length > this.cursorState.limit
) {
elements = elements.slice(0, this.cursorState.limit - this.cursorState.currentLimit);
this.kill();

@@ -428,3 +465,3 @@ }

return elements;
}
};

@@ -438,3 +475,3 @@ /**

this._killcursor(callback);
}
};

@@ -447,4 +484,4 @@ /**

Cursor.prototype.rewind = function() {
if(this.cursorState.init) {
if(!this.cursorState.dead) {
if (this.cursorState.init) {
if (!this.cursorState.dead) {
this.kill();

@@ -462,3 +499,3 @@ }

}
}
};

@@ -469,4 +506,3 @@ /**

var isConnectionDead = function(self, callback) {
if(self.pool
&& self.pool.isDestroyed()) {
if (self.pool && self.pool.isDestroyed()) {
self.cursorState.notified = true;

@@ -476,3 +512,7 @@ self.cursorState.killed = true;

self.cursorState.cursorIndex = 0;
callback(MongoError.create(f('connection to host %s:%s was destroyed', self.pool.host, self.pool.port)))
callback(
new MongoNetworkError(
f('connection to host %s:%s was destroyed', self.pool.host, self.pool.port)
)
);
return true;

@@ -482,3 +522,3 @@ }

return false;
}
};

@@ -490,3 +530,3 @@ /**

// Cursor is dead but not marked killed, return null
if(self.cursorState.dead && !self.cursorState.killed) {
if (self.cursorState.dead && !self.cursorState.killed) {
self.cursorState.notified = true;

@@ -501,3 +541,3 @@ self.cursorState.killed = true;

return false;
}
};

@@ -508,4 +548,4 @@ /**

var isCursorDeadAndKilled = function(self, callback) {
if(self.cursorState.dead && self.cursorState.killed) {
handleCallback(callback, MongoError.create('cursor is dead'));
if (self.cursorState.dead && self.cursorState.killed) {
handleCallback(callback, new MongoError('cursor is dead'));
return true;

@@ -515,3 +555,3 @@ }

return false;
}
};

@@ -522,3 +562,3 @@ /**

var isCursorKilled = function(self, callback) {
if(self.cursorState.killed) {
if (self.cursorState.killed) {
self.cursorState.notified = true;

@@ -532,3 +572,3 @@ self.cursorState.documents = [];

return false;
}
};

@@ -544,3 +584,3 @@ /**

handleCallback(callback, null, null);
}
};

@@ -555,7 +595,7 @@ /**

handleCallback(callback, null, null);
}
};
var nextFunction = function(self, callback) {
// We have notified about it
if(self.cursorState.notified) {
if (self.cursorState.notified) {
return callback(new Error('cursor is exhausted'));

@@ -565,19 +605,20 @@ }

// Cursor is killed return null
if(isCursorKilled(self, callback)) return;
if (isCursorKilled(self, callback)) return;
// Cursor is dead but not marked killed, return null
if(isCursorDeadButNotkilled(self, callback)) return;
if (isCursorDeadButNotkilled(self, callback)) return;
// We have a dead and killed cursor, attempting to call next should error
if(isCursorDeadAndKilled(self, callback)) return;
if (isCursorDeadAndKilled(self, callback)) return;
// We have just started the cursor
if(!self.cursorState.init) {
if (!self.cursorState.init) {
// Topology is not connected, save the call in the provided store to be
// Executed at some point when the handler deems it's reconnected
if(!self.topology.isConnected(self.options)) {
if (!self.topology.s.options.reconnect) {
if (!self.topology.isConnected(self.options)) {
if (!self.cursorState.reconnect) {
// Reconnect is disabled, so we'll never reconnect
return callback(new MongoError('no connection available'));
}
if (self.disconnectHandler != null) {

@@ -589,3 +630,9 @@ if (self.topology.isDestroyed()) {

return self.disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
return self.disconnectHandler.addObjectAndMethod(
'cursor',
self,
'next',
[callback],
callback
);
}

@@ -596,6 +643,12 @@ }

self.server = self.topology.getServer(self.options);
} catch(err) {
} catch (err) {
// Handle the error and add object to next method call
if(self.disconnectHandler != null) {
return self.disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
if (self.disconnectHandler != null) {
return self.disconnectHandler.addObjectAndMethod(
'cursor',
self,
'next',
[callback],
callback
);
}

@@ -611,5 +664,3 @@

// Server does not support server
if(self.cmd
&& self.cmd.collation
&& self.server.ismaster.maxWireVersion < 5) {
if (self.cmd && self.cmd.collation && self.server.ismaster.maxWireVersion < 5) {
return callback(new MongoError(f('server %s does not support collation', self.server.name)));

@@ -619,4 +670,11 @@ }

try {
self.query = self.server.wireProtocolHandler.command(self.bson, self.ns, self.cmd, self.cursorState, self.topology, self.options);
} catch(err) {
self.query = self.server.wireProtocolHandler.command(
self.bson,
self.ns,
self.cmd,
self.cursorState,
self.topology,
self.options
);
} catch (err) {
return callback(err);

@@ -627,17 +685,24 @@ }

// If we don't have a cursorId execute the first query
if(self.cursorState.cursorId == null) {
if (self.cursorState.cursorId == null) {
// Check if pool is dead and return if not possible to
// execute the query against the db
if(isConnectionDead(self, callback)) return;
if (isConnectionDead(self, callback)) return;
// Check if topology is destroyed
if(self.topology.isDestroyed()) return callback(new MongoError('connection destroyed, not possible to instantiate cursor'));
if (self.topology.isDestroyed())
return callback(
new MongoNetworkError('connection destroyed, not possible to instantiate cursor')
);
// query, cmd, options, cursorState, callback
self._find(function(err) {
if(err) return handleCallback(callback, err, null);
if (err) return handleCallback(callback, err, null);
if(self.cursorState.documents.length == 0
&& self.cursorState.cursorId && self.cursorState.cursorId.isZero()
&& !self.cmd.tailable && !self.cmd.awaitData) {
if (
self.cursorState.documents.length === 0 &&
self.cursorState.cursorId &&
self.cursorState.cursorId.isZero() &&
!self.cmd.tailable &&
!self.cmd.awaitData
) {
return setCursorNotified(self, callback);

@@ -648,3 +713,6 @@ }

});
} else if(self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
} else if (
self.cursorState.limit > 0 &&
self.cursorState.currentLimit >= self.cursorState.limit
) {
// Ensure we kill the cursor on the server

@@ -654,56 +722,78 @@ self.kill();

return setCursorDeadAndNotified(self, callback);
} else if(self.cursorState.cursorIndex == self.cursorState.documents.length
&& !Long.ZERO.equals(self.cursorState.cursorId)) {
// Ensure an empty cursor state
self.cursorState.documents = [];
self.cursorState.cursorIndex = 0;
} else if (
self.cursorState.cursorIndex === self.cursorState.documents.length &&
!Long.ZERO.equals(self.cursorState.cursorId)
) {
// Ensure an empty cursor state
self.cursorState.documents = [];
self.cursorState.cursorIndex = 0;
// Check if topology is destroyed
if(self.topology.isDestroyed()) return callback(new MongoError('connection destroyed, not possible to instantiate cursor'));
// Check if topology is destroyed
if (self.topology.isDestroyed())
return callback(
new MongoNetworkError('connection destroyed, not possible to instantiate cursor')
);
// Check if connection is dead and return if not possible to
// execute a getmore on this connection
if(isConnectionDead(self, callback)) return;
// Check if connection is dead and return if not possible to
// execute a getmore on this connection
if (isConnectionDead(self, callback)) return;
// Execute the next get more
self._getmore(function(err, doc, connection) {
if(err) return handleCallback(callback, err);
// Execute the next get more
self._getmore(function(err, doc, connection) {
if (err) return handleCallback(callback, err);
// Save the returned connection to ensure all getMore's fire over the same connection
self.connection = connection;
// Save the returned connection to ensure all getMore's fire over the same connection
self.connection = connection;
// Tailable cursor getMore result, notify owner about it
// No attempt is made here to retry, this is left to the user of the
// core module to handle to keep core simple
if(self.cursorState.documents.length == 0
&& self.cmd.tailable && Long.ZERO.equals(self.cursorState.cursorId)) {
// No more documents in the tailed cursor
return handleCallback(callback, MongoError.create({
message: 'No more documents in tailed cursor'
, tailable: self.cmd.tailable
, awaitData: self.cmd.awaitData
}));
} else if(self.cursorState.documents.length == 0
&& self.cmd.tailable && !Long.ZERO.equals(self.cursorState.cursorId)) {
return nextFunction(self, callback);
}
// Tailable cursor getMore result, notify owner about it
// No attempt is made here to retry, this is left to the user of the
// core module to handle to keep core simple
if (
self.cursorState.documents.length === 0 &&
self.cmd.tailable &&
Long.ZERO.equals(self.cursorState.cursorId)
) {
// No more documents in the tailed cursor
return handleCallback(
callback,
new MongoError({
message: 'No more documents in tailed cursor',
tailable: self.cmd.tailable,
awaitData: self.cmd.awaitData
})
);
} else if (
self.cursorState.documents.length === 0 &&
self.cmd.tailable &&
!Long.ZERO.equals(self.cursorState.cursorId)
) {
return nextFunction(self, callback);
}
if(self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
return setCursorDeadAndNotified(self, callback);
}
if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
return setCursorDeadAndNotified(self, callback);
}
nextFunction(self, callback);
});
} else if(self.cursorState.documents.length == self.cursorState.cursorIndex
&& self.cmd.tailable && Long.ZERO.equals(self.cursorState.cursorId)) {
return handleCallback(callback, MongoError.create({
message: 'No more documents in tailed cursor'
, tailable: self.cmd.tailable
, awaitData: self.cmd.awaitData
}));
} else if(self.cursorState.documents.length == self.cursorState.cursorIndex
&& Long.ZERO.equals(self.cursorState.cursorId)) {
setCursorDeadAndNotified(self, callback);
nextFunction(self, callback);
});
} else if (
self.cursorState.documents.length === self.cursorState.cursorIndex &&
self.cmd.tailable &&
Long.ZERO.equals(self.cursorState.cursorId)
) {
return handleCallback(
callback,
new MongoError({
message: 'No more documents in tailed cursor',
tailable: self.cmd.tailable,
awaitData: self.cmd.awaitData
})
);
} else if (
self.cursorState.documents.length === self.cursorState.cursorIndex &&
Long.ZERO.equals(self.cursorState.cursorId)
) {
setCursorDeadAndNotified(self, callback);
} else {
if(self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
// Ensure we kill the cursor on the server

@@ -722,3 +812,3 @@ self.kill();

// Doc overflow
if(!doc || doc.$err) {
if (!doc || doc.$err) {
// Ensure we kill the cursor on the server

@@ -733,3 +823,3 @@ self.kill();

// Transform the doc with passed in transformation method if provided
if(self.cursorState.transforms && typeof self.cursorState.transforms.doc == 'function') {
if (self.cursorState.transforms && typeof self.cursorState.transforms.doc === 'function') {
doc = self.cursorState.transforms.doc(doc);

@@ -741,3 +831,3 @@ }

}
}
};

@@ -751,4 +841,4 @@ /**

nextFunction(this, callback);
}
};
module.exports = Cursor;

@@ -1,3 +0,5 @@

"use strict";
'use strict';
var util = require('util');
/**

@@ -7,10 +9,29 @@ * Creates a new MongoError

* @augments Error
* @param {string} message The error message
* @param {Error|string|object} message The error message
* @property {string} message The error message
* @property {string} stack The error call stack
* @return {MongoError} A MongoError instance
*/
function MongoError(message) {
this.name = 'MongoError';
this.message = message;
Error.captureStackTrace(this, MongoError);
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MongoError';
if (message instanceof Error) {
this.message = message.message;
this.stack = message.stack;
} else {
if (typeof message === 'string') {
this.message = message;
} else {
this.message = message.message || message.errmsg || message.$err || 'n/a';
for (var name in message) {
this[name] = message[name];
}
}
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
util.inherits(MongoError, Error);

@@ -20,27 +41,28 @@ /**

* @method
* @param {object} options The error options
* @param {Error|string|object} options The options used to create the error.
* @return {MongoError} A MongoError instance
* @deprecated Use new MongoError() instead.
*/
MongoError.create = function(options) {
var err = null;
return new MongoError(options);
};
if(options instanceof Error) {
err = new MongoError(options.message);
err.stack = options.stack;
} else if(typeof options == 'string') {
err = new MongoError(options);
} else {
err = new MongoError(options.message || options.errmsg || options.$err || "n/a");
// Other options
for(var name in options) {
err[name] = options[name];
}
}
/**
* Creates a new MongoNetworkError
* @class
* @param {Error|string|object} message The error message
* @property {string} message The error message
* @property {string} stack The error call stack
* @return {MongoNetworkError} A MongoNetworkError instance
* @extends {MongoError}
*/
var MongoNetworkError = function(message) {
MongoError.call(this, message);
this.name = 'MongoNetworkError';
};
util.inherits(MongoNetworkError, MongoError);
return err;
}
// Extend JavaScript error
MongoError.prototype = new Error;
module.exports = MongoError;
module.exports = {
MongoError: MongoError,
MongoNetworkError: MongoNetworkError
};

@@ -0,1 +1,3 @@

'use strict';
var fs = require('fs');

@@ -7,3 +9,3 @@

exports.attachToRunner = function(runner, outputFile) {
var smokeOutput = { results : [] };
var smokeOutput = { results: [] };
var runningTests = {};

@@ -24,3 +26,3 @@

exit_code: 0,
url: ""
url: ''
});

@@ -42,3 +44,3 @@ delete runningTests[test.name];

smokeOutput.results.push({
status: "fail",
status: 'fail',
start: runningTests[testName].startTime,

@@ -48,3 +50,3 @@ end: Date.now(),

exit_code: 0,
url: ""
url: ''
});

@@ -51,0 +53,0 @@ }

@@ -1,4 +0,4 @@

"use strict"
'use strict';
var inherits = require('util').inherits,
const inherits = require('util').inherits,
f = require('util').format,

@@ -9,12 +9,14 @@ EventEmitter = require('events').EventEmitter,

retrieveBSON = require('../connection/utils').retrieveBSON,
MongoError = require('../error'),
MongoError = require('../error').MongoError,
errors = require('../error'),
Server = require('./server'),
assign = require('../utils').assign,
clone = require('./shared').clone,
sdam = require('./shared'),
diff = require('./shared').diff,
cloneOptions = require('./shared').cloneOptions,
createClientInfo = require('./shared').createClientInfo;
createClientInfo = require('./shared').createClientInfo,
SessionMixins = require('./shared').SessionMixins,
isRetryableWritesSupported = require('./shared').isRetryableWritesSupported,
getNextTransactionNumber = require('./shared').getNextTransactionNumber;
var BSON = retrieveBSON();
const BSON = retrieveBSON();

@@ -40,8 +42,8 @@ /**

var MongoCR = require('../auth/mongocr')
, X509 = require('../auth/x509')
, Plain = require('../auth/plain')
, GSSAPI = require('../auth/gssapi')
, SSPI = require('../auth/sspi')
, ScramSHA1 = require('../auth/scram');
var MongoCR = require('../auth/mongocr'),
X509 = require('../auth/x509'),
Plain = require('../auth/plain'),
GSSAPI = require('../auth/gssapi'),
SSPI = require('../auth/sspi'),
ScramSHA1 = require('../auth/scram');

@@ -58,16 +60,23 @@ //

var legalTransitions = {
'disconnected': [CONNECTING, DESTROYED, DISCONNECTED],
'connecting': [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
'connected': [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
'unreferenced': [UNREFERENCED, DESTROYED],
'destroyed': [DESTROYED]
}
disconnected: [CONNECTING, DESTROYED, DISCONNECTED],
connecting: [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
connected: [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
unreferenced: [UNREFERENCED, DESTROYED],
destroyed: [DESTROYED]
};
// Get current state
var legalStates = legalTransitions[self.state];
if(legalStates && legalStates.indexOf(newState) != -1) {
if (legalStates && legalStates.indexOf(newState) !== -1) {
self.state = newState;
} else {
self.logger.error(f('Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]'
, self.id, self.state, newState, legalStates));
self.logger.error(
f(
'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
self.id,
self.state,
newState,
legalStates
)
);
}

@@ -133,7 +142,22 @@ }

this.s = {
options: assign({}, options),
options: Object.assign({}, options),
// BSON instance
bson: options.bson || new BSON([BSON.Binary, BSON.Code, BSON.DBRef, BSON.Decimal128,
BSON.Double, BSON.Int32, BSON.Long, BSON.Map, BSON.MaxKey, BSON.MinKey,
BSON.ObjectId, BSON.BSONRegExp, BSON.Symbol, BSON.Timestamp]),
bson:
options.bson ||
new BSON([
BSON.Binary,
BSON.Code,
BSON.DBRef,
BSON.Decimal128,
BSON.Double,
BSON.Int32,
BSON.Long,
BSON.Map,
BSON.MaxKey,
BSON.MinKey,
BSON.ObjectId,
BSON.BSONRegExp,
BSON.Symbol,
BSON.Timestamp
]),
// Factory overrides

@@ -154,3 +178,3 @@ Cursor: options.cursorFactory || BasicCursor,

// Are we running in debug mode
debug: typeof options.debug == 'boolean' ? options.debug : false,
debug: typeof options.debug === 'boolean' ? options.debug : false,
// localThresholdMS

@@ -161,4 +185,4 @@ localThresholdMS: options.localThresholdMS || 15,

// Authentication context
authenticationContexts: [],
}
authenticationContexts: []
};

@@ -170,7 +194,14 @@ // Set the client info

// a lot of recycled connections to happen.
if(this.s.logger.isWarn()
&& this.s.options.socketTimeout != 0
&& this.s.options.socketTimeout < this.s.haInterval) {
this.s.logger.warn(f('warning socketTimeout %s is less than haInterval %s. This might cause unnecessary server reconnections due to socket timeouts'
, this.s.options.socketTimeout, this.s.haInterval));
if (
this.s.logger.isWarn() &&
this.s.options.socketTimeout !== 0 &&
this.s.options.socketTimeout < this.s.haInterval
) {
this.s.logger.warn(
f(
'warning socketTimeout %s is less than haInterval %s. This might cause unnecessary server reconnections due to socket timeouts',
this.s.options.socketTimeout,
this.s.haInterval
)
);
}

@@ -180,6 +211,9 @@

this.authProviders = options.authProviders || {
'mongocr': new MongoCR(this.s.bson), 'x509': new X509(this.s.bson)
, 'plain': new Plain(this.s.bson), 'gssapi': new GSSAPI(this.s.bson)
, 'sspi': new SSPI(this.s.bson), 'scram-sha-1': new ScramSHA1(this.s.bson)
}
mongocr: new MongoCR(this.s.bson),
x509: new X509(this.s.bson),
plain: new Plain(this.s.bson),
gssapi: new GSSAPI(this.s.bson),
sspi: new SSPI(this.s.bson),
'scram-sha-1': new ScramSHA1(this.s.bson)
};

@@ -206,21 +240,38 @@ // Disconnected state

this.topologyDescription = {
"topologyType": "Unknown", "servers": []
topologyType: 'Unknown',
servers: []
};
// Highest clusterTime seen in responses from the current deployment
this.clusterTime = null;
// Add event listener
EventEmitter.call(this);
}
};
inherits(Mongos, EventEmitter);
Object.assign(Mongos.prototype, SessionMixins);
Object.defineProperty(Mongos.prototype, 'type', {
enumerable:true, get: function() { return 'mongos'; }
enumerable: true,
get: function() {
return 'mongos';
}
});
Object.defineProperty(Mongos.prototype, 'parserType', {
enumerable:true, get: function() {
return BSON.native ? "c++" : "js";
enumerable: true,
get: function() {
return BSON.native ? 'c++' : 'js';
}
});
Object.defineProperty(Mongos.prototype, 'logicalSessionTimeoutMinutes', {
enumerable: true,
get: function() {
if (!this.ismaster) return null;
return this.ismaster.logicalSessionTimeoutMinutes || null;
}
});
/**

@@ -231,3 +282,3 @@ * Emit event if it exists

function emitSDAMEvent(self, event, description) {
if(self.listeners(event).length > 0) {
if (self.listeners(event).length > 0) {
self.emit(event, description);

@@ -250,11 +301,17 @@ }

var servers = this.s.seedlist.map(function(x) {
return new Server(assign({}, self.s.options, x, {
authProviders: self.authProviders, reconnect:false, monitoring:false, inTopology: true
}, {
clientInfo: clone(self.s.clientInfo)
}));
return new Server(
Object.assign({}, self.s.options, x, {
authProviders: self.authProviders,
reconnect: false,
monitoring: false,
parent: self,
clientInfo: clone(self.s.clientInfo)
})
);
});
servers.forEach(function(server) {
server.on('serverDescriptionChanged', function(event) { self.emit('serverDescriptionChanged', event); });
server.on('serverDescriptionChanged', function(event) {
self.emit('serverDescriptionChanged', event);
});
});

@@ -267,7 +324,7 @@

connectProxies(self, servers);
}
};
function handleEvent(self) {
return function() {
if(self.state == DESTROYED) return;
if (self.state === DESTROYED) return;
// Move to list of disconnectedProxies

@@ -284,3 +341,3 @@ moveServerFrom(self.connectedProxies, self.disconnectedProxies, this);

});
}
};
}

@@ -293,3 +350,3 @@

// Destroy the instance
if(self.state == DESTROYED) {
if (self.state === DESTROYED) {
// Emit the initial topology

@@ -303,3 +360,3 @@ emitTopologyDescriptionChanged(self);

// Check the type of server
if(event == 'connect') {
if (event === 'connect') {
// Do we have authentication contexts that need to be applied

@@ -311,6 +368,6 @@ applyAuthenticationContexts(self, _this, function() {

// Is this not a proxy, remove t
if(self.ismaster.msg == 'isdbgrid') {
if (self.ismaster.msg === 'isdbgrid') {
// Add to the connectd list
for(var i = 0; i < self.connectedProxies.length; i++) {
if(self.connectedProxies[i].name == _this.name) {
for (var i = 0; i < self.connectedProxies.length; i++) {
if (self.connectedProxies[i].name === _this.name) {
// Move from connectingProxies

@@ -326,3 +383,3 @@ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _this);

// Remove the handlers
for(i = 0; i < handlers.length; i++) {
for (i = 0; i < handlers.length; i++) {
_this.removeAllListeners(handlers[i]);

@@ -342,8 +399,7 @@ }

} else {
// Print warning if we did not find a mongos proxy
if(self.s.logger.isWarn()) {
if (self.s.logger.isWarn()) {
var message = 'expected mongos proxy, but found replicaset member mongod for server %s';
// We have a standalone server
if(!self.ismaster.hosts) {
if (!self.ismaster.hosts) {
message = 'expected mongos proxy, but found standalone mongod for server %s';

@@ -375,5 +431,5 @@ }

// Trigger topologyMonitor
if(self.connectingProxies.length == 0) {
if (self.connectingProxies.length === 0) {
// Emit connected if we are connected
if(self.connectedProxies.length > 0) {
if (self.connectedProxies.length > 0 && self.state === CONNECTING) {
// Set the state to connected

@@ -385,6 +441,8 @@ stateTransition(self, CONNECTED);

self.emit('all', self);
} else if(self.disconnectedProxies.length == 0) {
} else if (self.disconnectedProxies.length === 0) {
// Print warning if we did not find a mongos proxy
if(self.s.logger.isWarn()) {
self.s.logger.warn(f('no mongos proxies found in seed list, did you mean to connect to a replicaset'));
if (self.s.logger.isWarn()) {
self.s.logger.warn(
f('no mongos proxies found in seed list, did you mean to connect to a replicaset')
);
}

@@ -397,3 +455,3 @@

// Topology monitor
topologyMonitor(self, {firstConnect:true});
topologyMonitor(self, { firstConnect: true });
}

@@ -433,3 +491,3 @@ };

// Start all the servers
while(servers.length > 0) {
while (servers.length > 0) {
connect(servers.shift(), timeoutInterval++);

@@ -447,4 +505,4 @@ }

// Determine the lower bound for the Proxies
for(var i = 0; i < connectedProxies.length; i++) {
if(connectedProxies[i].lastIsMasterMS < lowerBoundLatency) {
for (var i = 0; i < connectedProxies.length; i++) {
if (connectedProxies[i].lastIsMasterMS < lowerBoundLatency) {
lowerBoundLatency = connectedProxies[i].lastIsMasterMS;

@@ -456,4 +514,6 @@ }

connectedProxies = connectedProxies.filter(function(server) {
if((server.lastIsMasterMS <= (lowerBoundLatency + self.s.localThresholdMS))
&& server.isConnected()) {
if (
server.lastIsMasterMS <= lowerBoundLatency + self.s.localThresholdMS &&
server.isConnected()
) {
return true;

@@ -464,3 +524,3 @@ }

// We have no connectedProxies pick first of the connected ones
if(connectedProxies.length == 0) {
if (connectedProxies.length === 0) {
return self.connectedProxies[0];

@@ -478,4 +538,4 @@ }

function moveServerFrom(from, to, proxy) {
for(var i = 0; i < from.length; i++) {
if(from[i].name == proxy.name) {
for (var i = 0; i < from.length; i++) {
if (from[i].name === proxy.name) {
from.splice(i, 1);

@@ -485,4 +545,4 @@ }

for(i = 0; i < to.length; i++) {
if(to[i].name == proxy.name) {
for (i = 0; i < to.length; i++) {
if (to[i].name === proxy.name) {
to.splice(i, 1);

@@ -496,4 +556,4 @@ }

function removeProxyFrom(from, proxy) {
for(var i = 0; i < from.length; i++) {
if(from[i].name == proxy.name) {
for (var i = 0; i < from.length; i++) {
if (from[i].name === proxy.name) {
from.splice(i, 1);

@@ -515,3 +575,3 @@ }

// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);

@@ -522,7 +582,7 @@ // Return destroy

if(event == 'connect' && !self.authenticating) {
if (event === 'connect' && !self.authenticating) {
// Do we have authentication contexts that need to be applied
applyAuthenticationContexts(self, _self, function() {
// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);

@@ -533,3 +593,3 @@ return _self.destroy();

// Remove the handlers
for(var i = 0; i < handlers.length; i++) {
for (var i = 0; i < handlers.length; i++) {
_self.removeAllListeners(handlers[i]);

@@ -551,3 +611,3 @@ }

});
} else if(event == 'connect' && self.authenticating) {
} else if (event === 'connect' && self.authenticating) {
// Move from connectingProxies

@@ -559,10 +619,10 @@ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);

// Are we done finish up callback
if(count == 0) {
if (count === 0) {
callback();
}
}
}
};
};
// No new servers
if(count == 0) {
if (count === 0) {
return callback();

@@ -575,3 +635,3 @@ }

// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return;

@@ -581,17 +641,22 @@ }

// Create a new server instance
var server = new Server(assign({}, self.s.options, {
host: _server.name.split(':')[0],
port: parseInt(_server.name.split(':')[1], 10)
}, {
authProviders: self.authProviders, reconnect:false, monitoring: false, inTopology: true
}, {
clientInfo: clone(self.s.clientInfo)
}));
var server = new Server(
Object.assign({}, self.s.options, {
host: _server.name.split(':')[0],
port: parseInt(_server.name.split(':')[1], 10),
authProviders: self.authProviders,
reconnect: false,
monitoring: false,
parent: self,
clientInfo: clone(self.s.clientInfo)
})
);
// Relay the server description change
server.on('serverDescriptionChanged', function(event) { self.emit('serverDescriptionChanged', event); });
server.on('serverDescriptionChanged', function(event) {
self.emit('serverDescriptionChanged', event);
});
// Emit opening server event
self.emit('serverOpening', {
topologyId: server.s.topologyId != -1 ? server.s.topologyId : self.id,
topologyId: server.s.topologyId !== -1 ? server.s.topologyId : self.id,
address: server.name

@@ -613,3 +678,3 @@ });

// Create new instances
for(var i = 0; i < proxies.length; i++) {
for (var i = 0; i < proxies.length; i++) {
execute(proxies[i], i);

@@ -620,3 +685,3 @@ }

function applyAuthenticationContexts(self, server, callback) {
if(self.s.authenticationContexts.length == 0) {
if (self.s.authenticationContexts.length === 0) {
return callback();

@@ -631,3 +696,3 @@ }

function applyAuth(authContexts, server, callback) {
if(authContexts.length == 0) return callback();
if (authContexts.length === 0) return callback();
// Get the first auth context

@@ -638,3 +703,3 @@ var authContext = authContexts.shift();

// Push our callback handler
customAuthContext.push(function(err) {
customAuthContext.push(function(/* err */) {
applyAuth(authContexts, server, callback);

@@ -644,3 +709,3 @@ });

// Attempt authentication
server.auth.apply(server, customAuthContext)
server.auth.apply(server, customAuthContext);
}

@@ -657,6 +722,6 @@

self.haTimeoutId = setTimeout(function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// If we have a primary and a disconnect handler, execute
// buffered operations
if(self.isConnected() && self.s.disconnectHandler) {
if (self.isConnected() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute();

@@ -679,41 +744,54 @@ }

// Execute ismaster
_server.command('admin.$cmd', {
ismaster:true
}, {
monitoring: true,
socketTimeout: self.s.options.connectionTimeout || 2000,
}, function(err, r) {
if(self.state == DESTROYED || self.state == UNREFERENCED) {
// Move from connectingProxies
moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
_server.destroy();
return cb(err, r);
}
_server.command(
'admin.$cmd',
{
ismaster: true
},
{
monitoring: true,
socketTimeout: self.s.options.connectionTimeout || 2000
},
function(err, r) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
// Move from connectingProxies
moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
_server.destroy();
return cb(err, r);
}
// Calculate latency
var latencyMS = new Date().getTime() - start;
// Calculate latency
var latencyMS = new Date().getTime() - start;
// We had an error, remove it from the state
if(err) {
// Emit the server heartbeat failure
emitSDAMEvent(self, 'serverHeartbeatFailed', { durationMS: latencyMS, failure: err, connectionId: _server.name });
// Move from connected proxies to disconnected proxies
moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
} else {
// Update the server ismaster
_server.ismaster = r.result;
_server.lastIsMasterMS = latencyMS;
// We had an error, remove it from the state
if (err) {
// Emit the server heartbeat failure
emitSDAMEvent(self, 'serverHeartbeatFailed', {
durationMS: latencyMS,
failure: err,
connectionId: _server.name
});
// Move from connected proxies to disconnected proxies
moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
} else {
// Update the server ismaster
_server.ismaster = r.result;
_server.lastIsMasterMS = latencyMS;
// Server heart beat event
emitSDAMEvent(self, 'serverHeartbeatSucceeded', { durationMS: latencyMS, reply: r.result, connectionId: _server.name });
// Server heart beat event
emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
durationMS: latencyMS,
reply: r.result,
connectionId: _server.name
});
}
cb(err, r);
}
cb(err, r);
});
);
}
// No proxies initiate monitor again
if(proxies.length == 0) {
if (proxies.length === 0) {
// Emit close event if any listeners registered
if(self.listeners("close").length > 0 && self.state == CONNECTING) {
if (self.listeners('close').length > 0 && self.state === CONNECTING) {
self.emit('error', new MongoError('no mongos proxy available'));

@@ -726,12 +804,12 @@ } else {

return reconnectProxies(self, self.disconnectedProxies, function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// Are we connected ? emit connect event
if(self.state == CONNECTING && options.firstConnect) {
if (self.state === CONNECTING && options.firstConnect) {
self.emit('connect', self);
self.emit('fullsetup', self);
self.emit('all', self);
} else if(self.isConnected()) {
} else if (self.isConnected()) {
self.emit('reconnect', self);
} else if(!self.isConnected() && self.listeners("close").length > 0) {
} else if (!self.isConnected() && self.listeners('close').length > 0) {
self.emit('close', self);

@@ -746,12 +824,12 @@ }

// Ping all servers
for(var i = 0; i < proxies.length; i++) {
for (var i = 0; i < proxies.length; i++) {
pingServer(self, proxies[i], function() {
count = count - 1;
if(count == 0) {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (count === 0) {
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// Attempt to connect to any unknown servers
reconnectProxies(self, self.disconnectedProxies, function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// Perform topology monitor

@@ -773,3 +851,3 @@ topologyMonitor(self);

return this.ismaster;
}
};

@@ -790,3 +868,3 @@ /**

clearTimeout(this.haTimeoutId);
}
};

@@ -805,3 +883,3 @@ /**

// Clear out any monitoring process
if(this.haTimeoutId) clearTimeout(this.haTimeoutId);
if (this.haTimeoutId) clearTimeout(this.haTimeoutId);
// Clear out authentication contexts

@@ -828,3 +906,3 @@ this.s.authenticationContexts = [];

emitSDAMEvent(this, 'topologyClosed', { topologyId: this.id });
}
};

@@ -838,3 +916,3 @@ /**

return this.connectedProxies.length > 0;
}
};

@@ -847,4 +925,4 @@ /**

Mongos.prototype.isDestroyed = function() {
return this.state == DESTROYED;
}
return this.state === DESTROYED;
};

@@ -857,13 +935,37 @@ //

var executeWriteOperation = function(self, op, ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
// Ensure we have no options
if (typeof options === 'function') (callback = options), (options = {});
options = options || {};
// Pick a server
var server = pickProxy(self);
let server = pickProxy(self);
// No server found error out
if(!server) return callback(new MongoError('no mongos proxy available'));
// Execute the command
server[op](ns, ops, options, callback);
}
if (!server) return callback(new MongoError('no mongos proxy available'));
if (!options.retryWrites || !options.session || !isRetryableWritesSupported(self)) {
// Execute the command
return server[op](ns, ops, options, callback);
}
// increment and assign txnNumber
options.txnNumber = getNextTransactionNumber(options.session);
server[op](ns, ops, options, (err, result) => {
if (!err) return callback(null, result);
if (!(err instanceof errors.MongoNetworkError) && !err.message.match(/not master/)) {
return callback(err);
}
// Pick another server
server = pickProxy(self);
// No server found error out with original error
if (!server || !isRetryableWritesSupported(server)) {
return callback(err);
}
// rerun the operation
server[op](ns, ops, options, callback);
});
};
/**

@@ -878,10 +980,15 @@ * Insert one or more documents

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
Mongos.prototype.insert = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.isConnected() && this.s.disconnectHandler != null) {
if (!this.isConnected() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('insert', ns, ops, options, callback);

@@ -891,3 +998,3 @@ }

// No mongos proxy available
if(!this.isConnected()) {
if (!this.isConnected()) {
return callback(new MongoError('no mongos proxy available'));

@@ -898,3 +1005,3 @@ }

executeWriteOperation(this, 'insert', ns, ops, options, callback);
}
};

@@ -910,10 +1017,15 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
Mongos.prototype.update = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.isConnected() && this.s.disconnectHandler != null) {
if (!this.isConnected() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('update', ns, ops, options, callback);

@@ -923,3 +1035,3 @@ }

// No mongos proxy available
if(!this.isConnected()) {
if (!this.isConnected()) {
return callback(new MongoError('no mongos proxy available'));

@@ -930,3 +1042,3 @@ }

executeWriteOperation(this, 'update', ns, ops, options, callback);
}
};

@@ -942,10 +1054,15 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
Mongos.prototype.remove = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.isConnected() && this.s.disconnectHandler != null) {
if (!this.isConnected() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('remove', ns, ops, options, callback);

@@ -955,3 +1072,3 @@ }

// No mongos proxy available
if(!this.isConnected()) {
if (!this.isConnected()) {
return callback(new MongoError('no mongos proxy available'));

@@ -962,3 +1079,3 @@ }

executeWriteOperation(this, 'remove', ns, ops, options, callback);
}
};

@@ -974,7 +1091,11 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function
*/
Mongos.prototype.command = function(ns, cmd, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
var self = this;

@@ -987,3 +1108,3 @@

// Executed at some point when the handler deems it's reconnected
if((server == null || !server.isConnected()) && this.s.disconnectHandler != null) {
if ((server == null || !server.isConnected()) && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('command', ns, cmd, options, callback);

@@ -993,3 +1114,3 @@ }

// No server returned we had an error
if(server == null) {
if (server == null) {
return callback(new MongoError('no mongos proxy available'));

@@ -1004,9 +1125,10 @@ }

server.command(ns, cmd, clonedOptions, callback);
}
};
/**
* Perform one or more remove operations
* Get a new cursor
* @method
* @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
* @param {{object}|{Long}} cmd Can be either a command returning a cursor or a cursorId
* @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
* @param {object} [options] Options for the cursor
* @param {object} [options.batchSize=0] Batchsize for the operation

@@ -1017,10 +1139,17 @@ * @param {array} [options.documents=[]] Initial documents list for cursor

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {opResultCallback} callback A callback function
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {object} [options.topology] The internal topology of the created cursor
* @returns {Cursor}
*/
Mongos.prototype.cursor = function(ns, cmd, cursorOptions) {
cursorOptions = cursorOptions || {};
var FinalCursor = cursorOptions.cursorFactory || this.s.Cursor;
return new FinalCursor(this.s.bson, ns, cmd, cursorOptions, this, this.s.options);
}
Mongos.prototype.cursor = function(ns, cmd, options) {
options = options || {};
const topology = options.topology || this;
// Set up final cursor type
var FinalCursor = options.cursorFactory || this.s.Cursor;
// Return the cursor
return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
};
/**

@@ -1042,8 +1171,8 @@ * Authenticate using a specified mechanism

// If we don't have the mechanism fail
if(this.authProviders[mechanism] == null && mechanism != 'default') {
return callback(new MongoError(f("auth provider %s does not exist", mechanism)));
if (this.authProviders[mechanism] == null && mechanism !== 'default') {
return callback(new MongoError(f('auth provider %s does not exist', mechanism)));
}
// Are we already authenticating, throw
if(this.authenticating) {
if (this.authenticating) {
return callback(new MongoError('authentication or logout allready in process'));

@@ -1054,3 +1183,3 @@ }

// Executed at some point when the handler deems it's reconnected
if(!self.isConnected() && self.s.disconnectHandler != null) {
if (!self.isConnected() && self.s.disconnectHandler != null) {
return self.s.disconnectHandler.add('auth', db, allArgs, {}, callback);

@@ -1067,3 +1196,3 @@ }

// No servers return
if(servers.length == 0) {
if (servers.length === 0) {
this.authenticating = false;

@@ -1078,28 +1207,34 @@ callback(null, true);

// Create arguments
var finalArguments = argsWithoutCallback.concat([function(err) {
count = count - 1;
// Save all the errors
if(err) errors.push({name: server.name, err: err});
// We are done
if(count == 0) {
// Auth is done
self.authenticating = false;
var finalArguments = argsWithoutCallback.concat([
function(err) {
count = count - 1;
// Save all the errors
if (err) errors.push({ name: server.name, err: err });
// We are done
if (count === 0) {
// Auth is done
self.authenticating = false;
// Return the auth error
if(errors.length) {
// Remove the entry from the stored authentication contexts
self.s.authenticationContexts.splice(currentContextIndex, 0);
// Return error
return callback(MongoError.create({
message: 'authentication fail', errors: errors
}), false);
// Return the auth error
if (errors.length) {
// Remove the entry from the stored authentication contexts
self.s.authenticationContexts.splice(currentContextIndex, 0);
// Return error
return callback(
new MongoError({
message: 'authentication fail',
errors: errors
}),
false
);
}
// Successfully authenticated session
callback(null, self);
}
// Successfully authenticated session
callback(null, self);
}
}]);
]);
// Execute the auth only against non arbiter servers
if(!server.lastIsMaster().arbiterOnly) {
if (!server.lastIsMaster().arbiterOnly) {
server.auth.apply(server, finalArguments);

@@ -1117,6 +1252,6 @@ }

// Authenticate against all servers
while(servers.length > 0) {
while (servers.length > 0) {
auth(servers.shift());
}
}
};

@@ -1132,3 +1267,3 @@ /**

// Are we authenticating or logging out, throw
if(this.authenticating) {
if (this.authenticating) {
throw new MongoError('authentication or logout allready in process');

@@ -1142,3 +1277,3 @@ }

var providers = Object.keys(this.authProviders);
for(var i = 0; i < providers.length; i++) {
for (var i = 0; i < providers.length; i++) {
this.authProviders[providers[i]].logout(dbName);

@@ -1150,3 +1285,3 @@ }

var count = servers.length;
if(count == 0) return callback();
if (count === 0) return callback();
var errors = [];

@@ -1156,3 +1291,3 @@

_server.logout(dbName, function(err) {
if(err) errors.push({name: _server.name, err: err});
if (err) errors.push({ name: _server.name, err: err });
cb();

@@ -1163,13 +1298,18 @@ });

// Execute logout on all server instances
for(i = 0; i < servers.length; i++) {
for (i = 0; i < servers.length; i++) {
logoutServer(servers[i], function() {
count = count - 1;
if(count == 0) {
if (count === 0) {
// Do not block new operations
self.authenticating = false;
// If we have one or more errors
if(errors.length) return callback(MongoError.create({
message: f('logout failed against db %s', dbName), errors: errors
}), false);
if (errors.length)
return callback(
new MongoError({
message: f('logout failed against db %s', dbName),
errors: errors
}),
false
);

@@ -1179,5 +1319,5 @@ // No errors

}
})
});
}
}
};

@@ -1191,5 +1331,5 @@ /**

var server = pickProxy(this);
if(this.s.debug) this.emit('pickedServer', null, server);
if (this.s.debug) this.emit('pickedServer', null, server);
return server;
}
};

@@ -1203,4 +1343,4 @@ /**

var server = this.getServer();
if(server) return server.getConnection();
}
if (server) return server.getConnection();
};

@@ -1215,3 +1355,3 @@ /**

for(var i = 0; i < this.connectedProxies.length; i++) {
for (var i = 0; i < this.connectedProxies.length; i++) {
connections = connections.concat(this.connectedProxies[i].connections());

@@ -1221,10 +1361,8 @@ }

return connections;
}
};
function emitTopologyDescriptionChanged(self) {
if(self.listeners('topologyDescriptionChanged').length > 0) {
if (self.listeners('topologyDescriptionChanged').length > 0) {
var topology = 'Unknown';
var setName = self.setName;
if(self.connectedProxies.length > 0) {
if (self.connectedProxies.length > 0) {
topology = 'Sharded';

@@ -1237,21 +1375,24 @@ }

servers: []
}
};
// All proxies
var proxies = self.disconnectedProxies
.concat(self.connectingProxies);
var proxies = self.disconnectedProxies.concat(self.connectingProxies);
// Add all the disconnected proxies
description.servers = description.servers.concat(proxies.map(function(x) {
var description = x.getDescription();
description.type = 'Unknown';
return description;
}));
description.servers = description.servers.concat(
proxies.map(function(x) {
var description = x.getDescription();
description.type = 'Unknown';
return description;
})
);
// Add all the connected proxies
description.servers = description.servers.concat(self.connectedProxies.map(function(x) {
var description = x.getDescription();
description.type = 'Mongos';
return description;
}));
description.servers = description.servers.concat(
self.connectedProxies.map(function(x) {
var description = x.getDescription();
description.type = 'Mongos';
return description;
})
);

@@ -1270,3 +1411,3 @@ // Get the diff

// Emit the topologyDescription change
if(diffResult.servers.length > 0) {
if (diffResult.servers.length > 0) {
self.emit('topologyDescriptionChanged', result);

@@ -1273,0 +1414,0 @@ }

@@ -1,5 +0,3 @@

"use strict";
'use strict';
var needSlaveOk = ['primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest'];
/**

@@ -10,12 +8,15 @@ * @fileOverview The **ReadPreference** class is a class that represents a MongoDB ReadPreference and is

* @example
* var ReplSet = require('mongodb-core').ReplSet
* , ReadPreference = require('mongodb-core').ReadPreference
* , assert = require('assert');
* const ReplSet = require('mongodb-core').ReplSet,
* ReadPreference = require('mongodb-core').ReadPreference,
* assert = require('assert');
*
* var server = new ReplSet([{host: 'localhost', port: 30000}], {setName: 'rs'});
* const server = new ReplSet([{host: 'localhost', port: 30000}], {setName: 'rs'});
* // Wait for the connection event
* server.on('connect', function(server) {
* var cursor = server.cursor('db.test'
* , {find: 'db.test', query: {}}
* , {readPreference: new ReadPreference('secondary')});
* const cursor = server.cursor(
* 'db.test',
* { find: 'db.test', query: {} },
* { readPreference: new ReadPreference('secondary') }
* );
*
* cursor.next(function(err, doc) {

@@ -33,7 +34,7 @@ * server.destroy();

* @class
* @param {string} preference A string describing the preference (primary|primaryPreferred|secondary|secondaryPreferred|nearest)
* @param {string} mode A string describing the read preference mode (primary|primaryPreferred|secondary|secondaryPreferred|nearest)
* @param {array} tags The tags object
* @param {object} [options] Additional read preference options
* @param {number} [options.maxStalenessSeconds] Max Secondary Read Stalleness in Seconds, Minimum value is 90 seconds.
* @property {string} preference The preference string (primary|primaryPreferred|secondary|secondaryPreferred|nearest)
* @param {number} [options.maxStalenessSeconds] Max secondary read staleness in seconds, Minimum value is 90 seconds.
* @property {string} mode The read preference mode (primary|primaryPreferred|secondary|secondaryPreferred|nearest)
* @property {array} tags The tags object

@@ -44,4 +45,4 @@ * @property {object} options Additional read preference options

*/
var ReadPreference = function(preference, tags, options) {
this.preference = preference;
const ReadPreference = function(mode, tags, options) {
this.mode = mode;
this.tags = tags;

@@ -51,12 +52,64 @@ this.options = options;

// Add the maxStalenessSeconds value to the read Preference
if(this.options && this.options.maxStalenessSeconds != null) {
if (this.options && this.options.maxStalenessSeconds != null) {
this.options = options;
this.maxStalenessSeconds = this.options.maxStalenessSeconds >= 0
? this.options.maxStalenessSeconds : null;
} else if(tags && typeof tags == 'object') {
this.options = tags, tags = null;
this.maxStalenessSeconds =
this.options.maxStalenessSeconds >= 0 ? this.options.maxStalenessSeconds : null;
} else if (tags && typeof tags === 'object') {
(this.options = tags), (tags = null);
}
}
};
// Support the deprecated `preference` property introduced in the porcelain layer
Object.defineProperty(ReadPreference.prototype, 'preference', {
enumerable: true,
get: function() {
return this.mode;
}
});
/**
* Read preference mode constants
*/
ReadPreference.PRIMARY = 'primary';
ReadPreference.PRIMARY_PREFERRED = 'primaryPreferred';
ReadPreference.SECONDARY = 'secondary';
ReadPreference.SECONDARY_PREFERRED = 'secondaryPreferred';
ReadPreference.NEAREST = 'nearest';
const VALID_MODES = [
ReadPreference.PRIMARY,
ReadPreference.PRIMARY_PREFERRED,
ReadPreference.SECONDARY,
ReadPreference.SECONDARY_PREFERRED,
ReadPreference.NEAREST,
true,
false,
null
];
/**
* Validate if a mode is legal
*
* @method
* @param {string} mode The string representing the read preference mode.
* @return {boolean}
*/
ReadPreference.isValid = function(mode) {
return VALID_MODES.indexOf(mode) !== -1;
};
/**
* Validate if a mode is legal
*
* @method
* @param {string} mode The string representing the read preference mode.
* @return {boolean}
*/
ReadPreference.prototype.isValid = function(mode) {
return ReadPreference.isValid(typeof mode === 'string' ? mode : this.mode);
};
const needSlaveOk = ['primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest'];
/**
* This needs slaveOk bit set

@@ -67,4 +120,4 @@ * @method

ReadPreference.prototype.slaveOk = function() {
return needSlaveOk.indexOf(this.preference) != -1;
}
return needSlaveOk.indexOf(this.mode) !== -1;
};

@@ -77,4 +130,4 @@ /**

ReadPreference.prototype.equals = function(readPreference) {
return readPreference.preference == this.preference;
}
return readPreference.mode === this.mode;
};

@@ -87,7 +140,7 @@ /**

ReadPreference.prototype.toJSON = function() {
var readPreference = {mode: this.preference};
if(Array.isArray(this.tags)) readPreference.tags = this.tags;
if(this.maxStalenessSeconds) readPreference.maxStalenessSeconds = this.maxStalenessSeconds;
const readPreference = { mode: this.mode };
if (Array.isArray(this.tags)) readPreference.tags = this.tags;
if (this.maxStalenessSeconds) readPreference.maxStalenessSeconds = this.maxStalenessSeconds;
return readPreference;
}
};

@@ -94,0 +147,0 @@ /**

@@ -1,2 +0,2 @@

"use strict"
'use strict';

@@ -9,14 +9,22 @@ var inherits = require('util').inherits,

ReadPreference = require('./read_preference'),
MongoError = require('../error');
MongoError = require('../error').MongoError;
var TopologyType = {
'Single': 'Single', 'ReplicaSetNoPrimary': 'ReplicaSetNoPrimary',
'ReplicaSetWithPrimary': 'ReplicaSetWithPrimary', 'Sharded': 'Sharded',
'Unknown': 'Unknown'
Single: 'Single',
ReplicaSetNoPrimary: 'ReplicaSetNoPrimary',
ReplicaSetWithPrimary: 'ReplicaSetWithPrimary',
Sharded: 'Sharded',
Unknown: 'Unknown'
};
var ServerType = {
'Standalone': 'Standalone', 'Mongos': 'Mongos', 'PossiblePrimary': 'PossiblePrimary',
'RSPrimary': 'RSPrimary', 'RSSecondary': 'RSSecondary', 'RSArbiter': 'RSArbiter',
'RSOther': 'RSOther', 'RSGhost': 'RSGhost', 'Unknown': 'Unknown'
Standalone: 'Standalone',
Mongos: 'Mongos',
PossiblePrimary: 'PossiblePrimary',
RSPrimary: 'RSPrimary',
RSSecondary: 'RSSecondary',
RSArbiter: 'RSArbiter',
RSOther: 'RSOther',
RSGhost: 'RSGhost',
Unknown: 'Unknown'
};

@@ -65,6 +73,9 @@

this.replicasetDescription = {
"topologyType": "Unknown", "servers": []
topologyType: 'Unknown',
servers: []
};
}
this.logicalSessionTimeoutMinutes = undefined;
};
inherits(ReplSetState, EventEmitter);

@@ -74,15 +85,15 @@

return this.primary != null && this.secondaries.length > 0;
}
};
ReplSetState.prototype.hasPrimaryOrSecondary = function() {
return this.hasPrimary() || this.hasSecondary();
}
};
ReplSetState.prototype.hasPrimary = function() {
return this.primary != null;
}
};
ReplSetState.prototype.hasSecondary = function() {
return this.secondaries.length > 0;
}
};

@@ -92,4 +103,4 @@ ReplSetState.prototype.get = function(host) {

for(var i = 0; i < servers.length; i++) {
if(servers[i].name.toLowerCase() === host.toLowerCase()) {
for (var i = 0; i < servers.length; i++) {
if (servers[i].name.toLowerCase() === host.toLowerCase()) {
return servers[i];

@@ -100,3 +111,3 @@ }

return null;
}
};

@@ -107,14 +118,22 @@ ReplSetState.prototype.allServers = function(options) {

servers = servers.concat(this.secondaries);
if(!options.ignoreArbiters) servers = servers.concat(this.arbiters);
if (!options.ignoreArbiters) servers = servers.concat(this.arbiters);
servers = servers.concat(this.passives);
return servers;
}
};
ReplSetState.prototype.destroy = function(options) {
// Destroy all sockets
if(this.primary) this.primary.destroy(options);
this.secondaries.forEach(function(x) { x.destroy(options); });
this.arbiters.forEach(function(x) { x.destroy(options); });
this.passives.forEach(function(x) { x.destroy(options); });
this.ghosts.forEach(function(x) { x.destroy(options); });
if (this.primary) this.primary.destroy(options);
this.secondaries.forEach(function(x) {
x.destroy(options);
});
this.arbiters.forEach(function(x) {
x.destroy(options);
});
this.passives.forEach(function(x) {
x.destroy(options);
});
this.ghosts.forEach(function(x) {
x.destroy(options);
});
// Clear out the complete state

@@ -130,3 +149,3 @@ this.secondaries = [];

emitTopologyDescriptionChanged(this);
}
};

@@ -146,7 +165,9 @@ ReplSetState.prototype.remove = function(server, options) {

// Check if it's active and this is just a failed connection attempt
for(var i = 0; i < servers.length; i++) {
if(!options.force
&& servers[i].equals(server)
&& servers[i].isConnected
&& servers[i].isConnected()) {
for (var i = 0; i < servers.length; i++) {
if (
!options.force &&
servers[i].equals(server) &&
servers[i].isConnected &&
servers[i].isConnected()
) {
return;

@@ -157,3 +178,3 @@ }

// If we have it in the set remove it
if(this.set[serverName]) {
if (this.set[serverName]) {
this.set[serverName].type = ServerType.Unknown;

@@ -169,3 +190,3 @@ this.set[serverName].electionId = null;

// Remove from any lists
if(this.primary && this.primary.equals(server)) {
if (this.primary && this.primary.equals(server)) {
this.primary = null;

@@ -187,6 +208,6 @@ this.topologyType = TopologyType.ReplicaSetNoPrimary;

// Do we have a removeType
if(removeType) {
if (removeType) {
this.emit('left', removeType, server);
}
}
};

@@ -204,3 +225,3 @@ ReplSetState.prototype.update = function(server) {

//
if(ismaster) {
if (ismaster) {
// Join all the possible new hosts

@@ -210,13 +231,17 @@ var hosts = Array.isArray(ismaster.hosts) ? ismaster.hosts : [];

hosts = hosts.concat(Array.isArray(ismaster.passives) ? ismaster.passives : []);
hosts = hosts.map(function(s) { return s.toLowerCase() });
hosts = hosts.map(function(s) {
return s.toLowerCase();
});
// Add all hosts as unknownServers
for(var i = 0; i < hosts.length; i++) {
for (var i = 0; i < hosts.length; i++) {
// Add to the list of unknown server
if(this.unknownServers.indexOf(hosts[i]) == -1
&& (!this.set[hosts[i]] || this.set[hosts[i]].type == ServerType.Unknown)) {
if (
this.unknownServers.indexOf(hosts[i]) === -1 &&
(!this.set[hosts[i]] || this.set[hosts[i]].type === ServerType.Unknown)
) {
this.unknownServers.push(hosts[i].toLowerCase());
}
if(!this.set[hosts[i]]) {
if (!this.set[hosts[i]]) {
this.set[hosts[i]] = {

@@ -227,3 +252,3 @@ type: ServerType.Unknown,

setVersion: null
}
};
}

@@ -236,6 +261,9 @@ }

//
if(!ismaster && !inList(ismaster, server, this.unknownServers)) {
if (!ismaster && !inList(ismaster, server, this.unknownServers)) {
self.set[serverName] = {
type: ServerType.Unknown, setVersion: null, electionId: null, setName: null
}
type: ServerType.Unknown,
setVersion: null,
electionId: null,
setName: null
};
// Update set information about the server instance

@@ -247,3 +275,3 @@ self.set[serverName].type = ServerType.Unknown;

if(self.unknownServers.indexOf(server.name) == -1) {
if (self.unknownServers.indexOf(server.name) === -1) {
self.unknownServers.push(serverName);

@@ -256,6 +284,21 @@ }

// Update logicalSessionTimeoutMinutes
if (ismaster.logicalSessionTimeoutMinutes !== undefined) {
if (
self.logicalSessionTimeoutMinutes === undefined ||
ismaster.logicalSessionTimeoutMinutes === null
) {
self.logicalSessionTimeoutMinutes = ismaster.logicalSessionTimeoutMinutes;
} else {
self.logicalSessionTimeoutMinutes = Math.min(
self.logicalSessionTimeoutMinutes,
ismaster.logicalSessionTimeoutMinutes
);
}
}
//
// Is this a mongos
//
if(ismaster && ismaster.msg == 'isdbgrid') {
if (ismaster && ismaster.msg === 'isdbgrid') {
return false;

@@ -265,11 +308,21 @@ }

// A RSOther instance
if((ismaster.setName && ismaster.hidden)
|| (ismaster.setName && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly && !ismaster.passive)) {
if (
(ismaster.setName && ismaster.hidden) ||
(ismaster.setName &&
!ismaster.ismaster &&
!ismaster.secondary &&
!ismaster.arbiterOnly &&
!ismaster.passive)
) {
self.set[serverName] = {
type: ServerType.RSOther, setVersion: null,
electionId: null, setName: ismaster.setName
}
type: ServerType.RSOther,
setVersion: null,
electionId: null,
setName: ismaster.setName
};
// Set the topology
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
if (ismaster.setName) this.setName = ismaster.setName;
return false;

@@ -279,11 +332,15 @@ }

// A RSGhost instance
if(ismaster.isreplicaset) {
if (ismaster.isreplicaset) {
self.set[serverName] = {
type: ServerType.RSGhost, setVersion: null,
electionId: null, setName: null
}
type: ServerType.RSGhost,
setVersion: null,
electionId: null,
setName: null
};
// Set the topology
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
if (ismaster.setName) this.setName = ismaster.setName;

@@ -297,5 +354,5 @@ // Set the topology

//
if(ismaster && ismaster.ismaster && !ismaster.setName) {
if (ismaster && ismaster.ismaster && !ismaster.setName) {
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
this.remove(server, {force:true});
this.remove(server, { force: true });
return false;

@@ -307,4 +364,4 @@ }

//
if(ismaster && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly) {
this.remove(server, {force:true});
if (ismaster && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly) {
this.remove(server, { force: true });
return false;

@@ -316,5 +373,11 @@ }

//
if(ismaster.me && ismaster.me.toLowerCase() != serverName) {
if(this.logger.isWarn()) {
this.logger.warn(f('the seedlist server was removed due to its address %s not matching its ismaster.me address %s', server.name, ismaster.me));
if (ismaster.me && ismaster.me.toLowerCase() !== serverName) {
if (this.logger.isWarn()) {
this.logger.warn(
f(
'the seedlist server was removed due to its address %s not matching its ismaster.me address %s',
server.name,
ismaster.me
)
);
}

@@ -331,3 +394,3 @@

// Set the type of topology we have
if(this.primary && !this.primary.equals(server)) {
if (this.primary && !this.primary.equals(server)) {
this.topologyType = TopologyType.ReplicaSetWithPrimary;

@@ -341,3 +404,3 @@ } else {

//
if(!this.primary && ismaster.primary) {
if (!this.primary && ismaster.primary) {
this.set[ismaster.primary.toLowerCase()] = {

@@ -347,4 +410,4 @@ type: ServerType.PossiblePrimary,

electionId: null,
setVersion: null,
}
setVersion: null
};
}

@@ -358,12 +421,18 @@

//
if(!this.primary && ismaster.ismaster && ismaster.setName) {
if (!this.primary && ismaster.ismaster && ismaster.setName) {
var ismasterElectionId = server.lastIsMaster().electionId;
if(this.setName && this.setName != ismaster.setName) {
if (this.setName && this.setName !== ismaster.setName) {
this.topologyType = TopologyType.ReplicaSetNoPrimary;
return new MongoError(f('setName from ismaster does not match provided connection setName [%s] != [%s]', ismaster.setName, this.setName));
return new MongoError(
f(
'setName from ismaster does not match provided connection setName [%s] != [%s]',
ismaster.setName,
this.setName
)
);
}
if(!this.maxElectionId && ismasterElectionId) {
if (!this.maxElectionId && ismasterElectionId) {
this.maxElectionId = ismasterElectionId;
} else if(this.maxElectionId && ismasterElectionId) {
} else if (this.maxElectionId && ismasterElectionId) {
var result = compareObjectIds(this.maxElectionId, ismasterElectionId);

@@ -373,7 +442,7 @@ // Get the electionIds

if(result == 1) {
if (result === 1) {
this.topologyType = TopologyType.ReplicaSetNoPrimary;
return false;
} else if(result == 0 && ismasterSetVersion) {
if(ismasterSetVersion < this.maxSetVersion) {
} else if (result === 0 && ismasterSetVersion) {
if (ismasterSetVersion < this.maxSetVersion) {
this.topologyType = TopologyType.ReplicaSetNoPrimary;

@@ -389,7 +458,9 @@ return false;

// Hande normalization of server names
var normalizedHosts = ismaster.hosts.map(function(x) { return x.toLowerCase() });
var normalizedHosts = ismaster.hosts.map(function(x) {
return x.toLowerCase();
});
var locationIndex = normalizedHosts.indexOf(serverName);
// Validate that the server exists in the host list
if(locationIndex != -1) {
if (locationIndex !== -1) {
self.primary = server;

@@ -401,7 +472,7 @@ self.set[serverName] = {

setName: ismaster.setName
}
};
// Set the topology
this.topologyType = TopologyType.ReplicaSetWithPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
if (ismaster.setName) this.setName = ismaster.setName;
removeFrom(server, self.unknownServers);

@@ -417,3 +488,3 @@ removeFrom(server, self.secondaries);

return true;
} else if(ismaster.ismaster && ismaster.setName) {
} else if (ismaster.ismaster && ismaster.setName) {
// Get the electionIds

@@ -428,10 +499,9 @@ var currentElectionId = self.set[self.primary.name.toLowerCase()].electionId;

// Is it the same server instance
if(this.primary.equals(server)
&& currentSetName == ismasterSetName) {
return false;
if (this.primary.equals(server) && currentSetName === ismasterSetName) {
return false;
}
// If we do not have the same rs name
if(currentSetName && currentSetName != ismasterSetName) {
if(!this.primary.equals(server)) {
if (currentSetName && currentSetName !== ismasterSetName) {
if (!this.primary.equals(server)) {
this.topologyType = TopologyType.ReplicaSetWithPrimary;

@@ -446,30 +516,29 @@ } else {

// Check if we need to replace the server
if(currentElectionId && ismasterElectionId) {
if (currentElectionId && ismasterElectionId) {
result = compareObjectIds(currentElectionId, ismasterElectionId);
if(result == 1) {
if (result === 1) {
return false;
} else if(result == 0 && (currentSetVersion > ismasterSetVersion)) {
} else if (result === 0 && currentSetVersion > ismasterSetVersion) {
return false;
}
} else if(!currentElectionId && ismasterElectionId
&& ismasterSetVersion) {
if(ismasterSetVersion < this.maxSetVersion) {
return false;
}
} else if (!currentElectionId && ismasterElectionId && ismasterSetVersion) {
if (ismasterSetVersion < this.maxSetVersion) {
return false;
}
}
if(!this.maxElectionId && ismasterElectionId) {
if (!this.maxElectionId && ismasterElectionId) {
this.maxElectionId = ismasterElectionId;
} else if(this.maxElectionId && ismasterElectionId) {
} else if (this.maxElectionId && ismasterElectionId) {
result = compareObjectIds(this.maxElectionId, ismasterElectionId);
if(result == 1) {
if (result === 1) {
return false;
} else if(result == 0 && currentSetVersion && ismasterSetVersion) {
if(ismasterSetVersion < this.maxSetVersion) {
} else if (result === 0 && currentSetVersion && ismasterSetVersion) {
if (ismasterSetVersion < this.maxSetVersion) {
return false;
}
} else {
if(ismasterSetVersion < this.maxSetVersion) {
if (ismasterSetVersion < this.maxSetVersion) {
return false;

@@ -487,5 +556,7 @@ }

self.set[self.primary.name.toLowerCase()] = {
type: ServerType.Unknown, setVersion: null,
electionId: null, setName: null
}
type: ServerType.Unknown,
setVersion: null,
electionId: null,
setName: null
};

@@ -500,9 +571,11 @@ // Signal primary left

self.set[serverName] = {
type: ServerType.RSPrimary, setVersion: ismaster.setVersion,
electionId: ismaster.electionId, setName: ismaster.setName
}
type: ServerType.RSPrimary,
setVersion: ismaster.setVersion,
electionId: ismaster.electionId,
setName: ismaster.setName
};
// Set the topology
this.topologyType = TopologyType.ReplicaSetWithPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
if (ismaster.setName) this.setName = ismaster.setName;
removeFrom(server, self.unknownServers);

@@ -517,7 +590,9 @@ removeFrom(server, self.secondaries);

// A possible instance
if(!this.primary && ismaster.primary) {
if (!this.primary && ismaster.primary) {
self.set[ismaster.primary.toLowerCase()] = {
type: ServerType.PossiblePrimary, setVersion: null,
electionId: null, setName: null
}
type: ServerType.PossiblePrimary,
setVersion: null,
electionId: null,
setName: null
};
}

@@ -528,17 +603,22 @@

//
if(ismaster.secondary && ismaster.setName
&& !inList(ismaster, server, this.secondaries)
&& this.setName && this.setName == ismaster.setName) {
if (
ismaster.secondary &&
ismaster.setName &&
!inList(ismaster, server, this.secondaries) &&
this.setName &&
this.setName === ismaster.setName
) {
addToList(self, ServerType.RSSecondary, ismaster, server, this.secondaries);
// Set the topology
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
if (ismaster.setName) this.setName = ismaster.setName;
removeFrom(server, self.unknownServers);
// Remove primary
if(this.primary
&& this.primary.name.toLowerCase() == serverName) {
server.destroy();
this.primary = null;
self.emit('left', 'primary', server);
if (this.primary && this.primary.name.toLowerCase() === serverName) {
server.destroy();
this.primary = null;
self.emit('left', 'primary', server);
}

@@ -555,9 +635,15 @@

//
if(ismaster.arbiterOnly && ismaster.setName
&& !inList(ismaster, server, this.arbiters)
&& this.setName && this.setName == ismaster.setName) {
if (
ismaster.arbiterOnly &&
ismaster.setName &&
!inList(ismaster, server, this.arbiters) &&
this.setName &&
this.setName === ismaster.setName
) {
addToList(self, ServerType.RSArbiter, ismaster, server, this.arbiters);
// Set the topology
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
if (ismaster.setName) this.setName = ismaster.setName;
removeFrom(server, self.unknownServers);

@@ -572,17 +658,22 @@ self.emit('joined', 'arbiter', server);

//
if(ismaster.passive && ismaster.setName
&& !inList(ismaster, server, this.passives)
&& this.setName && this.setName == ismaster.setName) {
if (
ismaster.passive &&
ismaster.setName &&
!inList(ismaster, server, this.passives) &&
this.setName &&
this.setName === ismaster.setName
) {
addToList(self, ServerType.RSSecondary, ismaster, server, this.passives);
// Set the topology
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
if(ismaster.setName) this.setName = ismaster.setName;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
if (ismaster.setName) this.setName = ismaster.setName;
removeFrom(server, self.unknownServers);
// Remove primary
if(this.primary
&& this.primary.name.toLowerCase() == serverName) {
server.destroy();
this.primary = null;
self.emit('left', 'primary', server);
if (this.primary && this.primary.name.toLowerCase() === serverName) {
server.destroy();
this.primary = null;
self.emit('left', 'primary', server);
}

@@ -598,3 +689,3 @@

//
if(this.set[serverName] && this.set[serverName].type == ServerType.RSPrimary) {
if (this.set[serverName] && this.set[serverName].type === ServerType.RSPrimary) {
self.emit('left', 'primary', this.primary);

@@ -607,5 +698,7 @@ this.primary.destroy();

this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
this.topologyType = this.primary
? TopologyType.ReplicaSetWithPrimary
: TopologyType.ReplicaSetNoPrimary;
return false;
}
};

@@ -620,3 +713,3 @@ /**

// Go over all secondaries
for(var i = 0; i < this.secondaries.length; i++) {
for (var i = 0; i < this.secondaries.length; i++) {
max = Math.max(max, this.secondaries[i].lastWriteDate);

@@ -626,13 +719,12 @@ }

// Perform this servers staleness calculation
if(server.ismaster.maxWireVersion >= 5
&& server.ismaster.secondary
&& this.hasPrimary()) {
server.staleness = (server.lastUpdateTime - server.lastWriteDate)
- (this.primary.lastUpdateTime - this.primary.lastWriteDate)
+ haInterval;
} else if(server.ismaster.maxWireVersion >= 5
&& server.ismaster.secondary){
if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary && this.hasPrimary()) {
server.staleness =
server.lastUpdateTime -
server.lastWriteDate -
(this.primary.lastUpdateTime - this.primary.lastWriteDate) +
haInterval;
} else if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary) {
server.staleness = max - server.lastWriteDate + haInterval;
}
}
};

@@ -644,6 +736,6 @@ /**

ReplSetState.prototype.updateSecondariesMaxStaleness = function(haInterval) {
for(var i = 0; i < this.secondaries.length; i++) {
for (var i = 0; i < this.secondaries.length; i++) {
this.updateServerMaxStaleness(this.secondaries[i], haInterval);
}
}
};

@@ -660,3 +752,3 @@ /**

// maxStalenessSeconds is not allowed with a primary read
if(readPreference.preference == 'primary' && readPreference.maxStalenessSeconds != null) {
if (readPreference.preference === 'primary' && readPreference.maxStalenessSeconds != null) {
return new MongoError('primary readPreference incompatible with maxStalenessSeconds');

@@ -671,6 +763,8 @@ }

// for maxStalenessSeconds when maxStalenessSeconds specified on readPreference. Then error out
if(readPreference.maxStalenessSeconds != null) {
for(var i = 0; i < allservers.length; i++) {
if(allservers[i].ismaster.maxWireVersion < 5) {
return new MongoError('maxStalenessSeconds not supported by at least one of the replicaset members');
if (readPreference.maxStalenessSeconds != null) {
for (var i = 0; i < allservers.length; i++) {
if (allservers[i].ismaster.maxWireVersion < 5) {
return new MongoError(
'maxStalenessSeconds not supported by at least one of the replicaset members'
);
}

@@ -681,5 +775,8 @@ }

// Do we have the nearest readPreference
if(readPreference.preference == 'nearest' && readPreference.maxStalenessSeconds == null) {
if (readPreference.preference === 'nearest' && readPreference.maxStalenessSeconds == null) {
return pickNearest(this, readPreference);
} else if(readPreference.preference == 'nearest' && readPreference.maxStalenessSeconds != null) {
} else if (
readPreference.preference === 'nearest' &&
readPreference.maxStalenessSeconds != null
) {
return pickNearestMaxStalenessSeconds(this, readPreference);

@@ -692,34 +789,35 @@ }

// Check if we can satisfy and of the basic read Preferences
if(readPreference.equals(ReadPreference.secondary)
&& secondaries.length == 0) {
return new MongoError("no secondary server available");
}
if (readPreference.equals(ReadPreference.secondary) && secondaries.length === 0) {
return new MongoError('no secondary server available');
}
if(readPreference.equals(ReadPreference.secondaryPreferred)
&& secondaries.length == 0
&& this.primary == null) {
return new MongoError("no secondary or primary server available");
}
if (
readPreference.equals(ReadPreference.secondaryPreferred) &&
secondaries.length === 0 &&
this.primary == null
) {
return new MongoError('no secondary or primary server available');
}
if(readPreference.equals(ReadPreference.primary)
&& this.primary == null) {
return new MongoError("no primary server available");
}
if (readPreference.equals(ReadPreference.primary) && this.primary == null) {
return new MongoError('no primary server available');
}
// Secondary preferred or just secondaries
if(readPreference.equals(ReadPreference.secondaryPreferred)
|| readPreference.equals(ReadPreference.secondary)) {
if(secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
if (
readPreference.equals(ReadPreference.secondaryPreferred) ||
readPreference.equals(ReadPreference.secondary)
) {
if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
// Pick nearest of any other servers available
var server = pickNearest(this, readPreference);
// No server in the window return primary
if(server) {
if (server) {
return server;
}
} else if(secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
} else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
// Pick nearest of any other servers available
server = pickNearestMaxStalenessSeconds(this, readPreference);
// No server in the window return primary
if(server) {
if (server) {
return server;

@@ -729,3 +827,3 @@ }

if(readPreference.equals(ReadPreference.secondaryPreferred)){
if (readPreference.equals(ReadPreference.secondaryPreferred)) {
return this.primary;

@@ -738,7 +836,7 @@ }

// Primary preferred
if(readPreference.equals(ReadPreference.primaryPreferred)) {
if (readPreference.equals(ReadPreference.primaryPreferred)) {
server = null;
// We prefer the primary if it's available
if(this.primary) {
if (this.primary) {
return this.primary;

@@ -748,5 +846,5 @@ }

// Pick a secondary
if(secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
server = pickNearest(this, readPreference);
} else if(secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
} else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
server = pickNearestMaxStalenessSeconds(this, readPreference);

@@ -756,3 +854,3 @@ }

// Did we find a server
if(server) return server;
if (server) return server;
}

@@ -762,3 +860,3 @@

return this.primary;
}
};

@@ -768,3 +866,3 @@ //

var filterByTags = function(readPreference, servers) {
if(readPreference.tags == null) return servers;
if (readPreference.tags == null) return servers;
var filteredServers = [];

@@ -774,7 +872,7 @@ var tagsArray = Array.isArray(readPreference.tags) ? readPreference.tags : [readPreference.tags];

// Iterate over the tags
for(var j = 0; j < tagsArray.length; j++) {
for (var j = 0; j < tagsArray.length; j++) {
var tags = tagsArray[j];
// Iterate over all the servers
for(var i = 0; i < servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
var serverTag = servers[i].lastIsMaster().tags || {};

@@ -785,4 +883,4 @@

// Check if the server is valid
for(var name in tags) {
if(serverTag[name] != tags[name]) {
for (var name in tags) {
if (serverTag[name] !== tags[name]) {
found = false;

@@ -793,3 +891,3 @@ }

// Add to candidate list
if(found) {
if (found) {
filteredServers.push(servers[i]);

@@ -802,3 +900,3 @@ }

return filteredServers;
}
};

@@ -808,3 +906,2 @@ function pickNearestMaxStalenessSeconds(self, readPreference) {

var servers = [];
var heartbeatFrequencyMS = self.heartbeatFrequencyMS;

@@ -815,3 +912,3 @@ // Get the maxStalenessMS

// Check if the maxStalenessMS > 90 seconds
if(maxStalenessMS < 90 * 1000) {
if (maxStalenessMS < 90 * 1000) {
return new MongoError('maxStalenessSeconds must be set to at least 90 seconds');

@@ -821,10 +918,12 @@ }

// Add primary to list if not a secondary read preference
if(self.primary
&& readPreference.preference != 'secondary'
&& readPreference.preference != 'secondaryPreferred') {
servers.push(self.primary);
if (
self.primary &&
readPreference.preference !== 'secondary' &&
readPreference.preference !== 'secondaryPreferred'
) {
servers.push(self.primary);
}
// Add all the secondaries
for(var i = 0; i < self.secondaries.length; i++) {
for (var i = 0; i < self.secondaries.length; i++) {
servers.push(self.secondaries[i]);

@@ -834,6 +933,4 @@ }

// If we have a secondaryPreferred readPreference and no server add the primary
if(self.primary
&& servers.length == 0
&& readPreference.preference != 'secondaryPreferred') {
servers.push(self.primary);
if (self.primary && servers.length === 0 && readPreference.preference !== 'secondaryPreferred') {
servers.push(self.primary);
}

@@ -856,8 +953,8 @@

// return a.time > b.time;
return a.lastIsMasterMS > b.lastIsMasterMS
return a.lastIsMasterMS > b.lastIsMasterMS;
});
// No servers, default to primary
if(servers.length == 0) {
return null
if (servers.length === 0) {
return null;
}

@@ -881,10 +978,12 @@

// Add primary to list if not a secondary read preference
if(self.primary
&& readPreference.preference != 'secondary'
&& readPreference.preference != 'secondaryPreferred') {
servers.push(self.primary);
if (
self.primary &&
readPreference.preference !== 'secondary' &&
readPreference.preference !== 'secondaryPreferred'
) {
servers.push(self.primary);
}
// Add all the secondaries
for(var i = 0; i < self.secondaries.length; i++) {
for (var i = 0; i < self.secondaries.length; i++) {
servers.push(self.secondaries[i]);

@@ -894,6 +993,4 @@ }

// If we have a secondaryPreferred readPreference and no server add the primary
if(servers.length == 0
&& self.primary
&& readPreference.preference != 'secondaryPreferred') {
servers.push(self.primary);
if (servers.length === 0 && self.primary && readPreference.preference !== 'secondaryPreferred') {
servers.push(self.primary);
}

@@ -907,3 +1004,3 @@

// return a.time > b.time;
return a.lastIsMasterMS > b.lastIsMasterMS
return a.lastIsMasterMS > b.lastIsMasterMS;
});

@@ -920,4 +1017,4 @@

// No servers, default to primary
if(servers.length == 0) {
return null
if (servers.length === 0) {
return null;
}

@@ -936,5 +1033,5 @@

function inList(ismaster, server, list) {
for(var i = 0; i < list.length; i++) {
if(list[i] && list[i].name
&& list[i].name.toLowerCase() == server.name.toLowerCase()) return true;
for (var i = 0; i < list.length; i++) {
if (list[i] && list[i].name && list[i].name.toLowerCase() === server.name.toLowerCase())
return true;
}

@@ -960,7 +1057,7 @@

if(a === b) {
if (a === b) {
return 0;
}
if(typeof Buffer.compare === 'function') {
if (typeof Buffer.compare === 'function') {
return Buffer.compare(a, b);

@@ -988,10 +1085,9 @@ }

function removeFrom(server, list) {
for(var i = 0; i < list.length; i++) {
if(list[i].equals && list[i].equals(server)) {
for (var i = 0; i < list.length; i++) {
if (list[i].equals && list[i].equals(server)) {
list.splice(i, 1);
return true;
} else if(typeof list[i] == 'string'
&& list[i].toLowerCase() == server.name.toLowerCase()) {
list.splice(i, 1);
return true;
} else if (typeof list[i] === 'string' && list[i].toLowerCase() === server.name.toLowerCase()) {
list.splice(i, 1);
return true;
}

@@ -1004,9 +1100,9 @@ }

function emitTopologyDescriptionChanged(self) {
if(self.listeners('topologyDescriptionChanged').length > 0) {
if (self.listeners('topologyDescriptionChanged').length > 0) {
var topology = 'Unknown';
var setName = self.setName;
if(self.hasPrimaryAndSecondary()) {
if (self.hasPrimaryAndSecondary()) {
topology = 'ReplicaSetWithPrimary';
} else if(!self.hasPrimary() && self.hasSecondary()) {
} else if (!self.hasPrimary() && self.hasSecondary()) {
topology = 'ReplicaSetNoPrimary';

@@ -1020,6 +1116,6 @@ }

servers: []
}
};
// Add the primary to the list
if(self.hasPrimary()) {
if (self.hasPrimary()) {
var desc = self.primary.getDescription();

@@ -1031,21 +1127,27 @@ desc.type = 'RSPrimary';

// Add all the secondaries
description.servers = description.servers.concat(self.secondaries.map(function(x) {
var description = x.getDescription();
description.type = 'RSSecondary';
return description;
}));
description.servers = description.servers.concat(
self.secondaries.map(function(x) {
var description = x.getDescription();
description.type = 'RSSecondary';
return description;
})
);
// Add all the arbiters
description.servers = description.servers.concat(self.arbiters.map(function(x) {
var description = x.getDescription();
description.type = 'RSArbiter';
return description;
}));
description.servers = description.servers.concat(
self.arbiters.map(function(x) {
var description = x.getDescription();
description.type = 'RSArbiter';
return description;
})
);
// Add all the passives
description.servers = description.servers.concat(self.passives.map(function(x) {
var description = x.getDescription();
description.type = 'RSSecondary';
return description;
}));
description.servers = description.servers.concat(
self.passives.map(function(x) {
var description = x.getDescription();
description.type = 'RSSecondary';
return description;
})
);

@@ -1060,3 +1162,3 @@ // Get the diff

newDescription: description,
diff: diffResult,
diff: diffResult
};

@@ -1066,3 +1168,3 @@

// if(diffResult.servers.length > 0) {
self.emit('topologyDescriptionChanged', result);
self.emit('topologyDescriptionChanged', result);
// }

@@ -1069,0 +1171,0 @@

@@ -1,2 +0,2 @@

"use strict"
'use strict';

@@ -10,17 +10,20 @@ var inherits = require('util').inherits,

Logger = require('../connection/logger'),
MongoError = require('../error'),
MongoError = require('../error').MongoError,
errors = require('../error'),
Server = require('./server'),
ReplSetState = require('./replset_state'),
assign = require('../utils').assign,
clone = require('./shared').clone,
Timeout = require('./shared').Timeout,
Interval = require('./shared').Interval,
createClientInfo = require('./shared').createClientInfo;
createClientInfo = require('./shared').createClientInfo,
SessionMixins = require('./shared').SessionMixins,
isRetryableWritesSupported = require('./shared').isRetryableWritesSupported,
getNextTransactionNumber = require('./shared').getNextTransactionNumber;
var MongoCR = require('../auth/mongocr')
, X509 = require('../auth/x509')
, Plain = require('../auth/plain')
, GSSAPI = require('../auth/gssapi')
, SSPI = require('../auth/sspi')
, ScramSHA1 = require('../auth/scram');
var MongoCR = require('../auth/mongocr'),
X509 = require('../auth/x509'),
Plain = require('../auth/plain'),
GSSAPI = require('../auth/gssapi'),
SSPI = require('../auth/sspi'),
ScramSHA1 = require('../auth/scram');

@@ -39,16 +42,23 @@ var BSON = retrieveBSON();

var legalTransitions = {
'disconnected': [CONNECTING, DESTROYED, DISCONNECTED],
'connecting': [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
'connected': [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
'unreferenced': [UNREFERENCED, DESTROYED],
'destroyed': [DESTROYED]
}
disconnected: [CONNECTING, DESTROYED, DISCONNECTED],
connecting: [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
connected: [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
unreferenced: [UNREFERENCED, DESTROYED],
destroyed: [DESTROYED]
};
// Get current state
var legalStates = legalTransitions[self.state];
if(legalStates && legalStates.indexOf(newState) != -1) {
if (legalStates && legalStates.indexOf(newState) !== -1) {
self.state = newState;
} else {
self.s.logger.error(f('Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]'
, self.id, self.state, newState, legalStates));
self.s.logger.error(
f(
'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
self.id,
self.state,
newState,
legalStates
)
);
}

@@ -116,9 +126,9 @@ }

// Validate seedlist
if(!Array.isArray(seedlist)) throw new MongoError("seedlist must be an array");
if (!Array.isArray(seedlist)) throw new MongoError('seedlist must be an array');
// Validate list
if(seedlist.length == 0) throw new MongoError("seedlist must contain at least one entry");
if (seedlist.length === 0) throw new MongoError('seedlist must contain at least one entry');
// Validate entries
seedlist.forEach(function(e) {
if(typeof e.host != 'string' || typeof e.port != 'number')
throw new MongoError("seedlist entry must contain a host and port");
if (typeof e.host !== 'string' || typeof e.port !== 'number')
throw new MongoError('seedlist entry must contain a host and port');
});

@@ -135,3 +145,3 @@

// Backward compatibility
if(options.acceptableLatency) localThresholdMS = options.acceptableLatency;
if (options.acceptableLatency) localThresholdMS = options.acceptableLatency;

@@ -143,7 +153,22 @@ // Create a logger

this.s = {
options: assign({}, options),
options: Object.assign({}, options),
// BSON instance
bson: options.bson || new BSON([BSON.Binary, BSON.Code, BSON.DBRef, BSON.Decimal128,
BSON.Double, BSON.Int32, BSON.Long, BSON.Map, BSON.MaxKey, BSON.MinKey,
BSON.ObjectId, BSON.BSONRegExp, BSON.Symbol, BSON.Timestamp]),
bson:
options.bson ||
new BSON([
BSON.Binary,
BSON.Code,
BSON.DBRef,
BSON.Decimal128,
BSON.Double,
BSON.Int32,
BSON.Long,
BSON.Map,
BSON.MaxKey,
BSON.MinKey,
BSON.ObjectId,
BSON.BSONRegExp,
BSON.Symbol,
BSON.Timestamp
]),
// Factory overrides

@@ -157,3 +182,4 @@ Cursor: options.cursorFactory || BasicCursor,

replicaSetState: new ReplSetState({
id: this.id, setName: options.setName,
id: this.id,
setName: options.setName,
acceptableLatency: localThresholdMS,

@@ -176,19 +202,28 @@ heartbeatFrequencyMS: options.haInterval ? options.haInterval : 10000,

// Are we running in debug mode
debug: typeof options.debug == 'boolean' ? options.debug : false,
debug: typeof options.debug === 'boolean' ? options.debug : false,
// Client info
clientInfo: createClientInfo(options),
// Authentication context
authenticationContexts: [],
}
authenticationContexts: []
};
// Add handler for topology change
this.s.replicaSetState.on('topologyDescriptionChanged', function(r) { self.emit('topologyDescriptionChanged', r); });
this.s.replicaSetState.on('topologyDescriptionChanged', function(r) {
self.emit('topologyDescriptionChanged', r);
});
// Log info warning if the socketTimeout < haInterval as it will cause
// a lot of recycled connections to happen.
if(this.s.logger.isWarn()
&& this.s.options.socketTimeout != 0
&& this.s.options.socketTimeout < this.s.haInterval) {
this.s.logger.warn(f('warning socketTimeout %s is less than haInterval %s. This might cause unnecessary server reconnections due to socket timeouts'
, this.s.options.socketTimeout, this.s.haInterval));
if (
this.s.logger.isWarn() &&
this.s.options.socketTimeout !== 0 &&
this.s.options.socketTimeout < this.s.haInterval
) {
this.s.logger.warn(
f(
'warning socketTimeout %s is less than haInterval %s. This might cause unnecessary server reconnections due to socket timeouts',
this.s.options.socketTimeout,
this.s.haInterval
)
);
}

@@ -198,6 +233,9 @@

this.authProviders = options.authProviders || {
'mongocr': new MongoCR(this.s.bson), 'x509': new X509(this.s.bson)
, 'plain': new Plain(this.s.bson), 'gssapi': new GSSAPI(this.s.bson)
, 'sspi': new SSPI(this.s.bson), 'scram-sha-1': new ScramSHA1(this.s.bson)
}
mongocr: new MongoCR(this.s.bson),
x509: new X509(this.s.bson),
plain: new Plain(this.s.bson),
gssapi: new GSSAPI(this.s.bson),
sspi: new SSPI(this.s.bson),
'scram-sha-1': new ScramSHA1(this.s.bson)
};

@@ -214,4 +252,6 @@ // Add forwarding of events from state handler

this.initialConnectState = {
connect: false, fullsetup: false, all: false
}
connect: false,
fullsetup: false,
all: false
};

@@ -227,25 +267,40 @@ // Disconnected state

this.intervalIds = [];
}
// Highest clusterTime seen in responses from the current deployment
this.clusterTime = null;
};
inherits(ReplSet, EventEmitter);
Object.assign(ReplSet.prototype, SessionMixins);
Object.defineProperty(ReplSet.prototype, 'type', {
enumerable:true, get: function() { return 'replset'; }
enumerable: true,
get: function() {
return 'replset';
}
});
Object.defineProperty(ReplSet.prototype, 'parserType', {
enumerable:true, get: function() {
return BSON.native ? "c++" : "js";
enumerable: true,
get: function() {
return BSON.native ? 'c++' : 'js';
}
});
Object.defineProperty(ReplSet.prototype, 'logicalSessionTimeoutMinutes', {
enumerable: true,
get: function() {
return this.s.replicaSetState.logicalSessionTimeoutMinutes || null;
}
});
function rexecuteOperations(self) {
// If we have a primary and a disconnect handler, execute
// buffered operations
if(self.s.replicaSetState.hasPrimaryAndSecondary() && self.s.disconnectHandler) {
if (self.s.replicaSetState.hasPrimaryAndSecondary() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute();
} else if(self.s.replicaSetState.hasPrimary() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute({ executePrimary:true });
} else if(self.s.replicaSetState.hasSecondary() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute({ executeSecondary:true });
} else if (self.s.replicaSetState.hasPrimary() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute({ executePrimary: true });
} else if (self.s.replicaSetState.hasSecondary() && self.s.disconnectHandler) {
self.s.disconnectHandler.execute({ executeSecondary: true });
}

@@ -266,10 +321,10 @@ }

// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
return this.destroy({force:true});
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return this.destroy({ force: true });
}
if(event == 'connect' && !self.authenticating) {
if (event === 'connect' && !self.authenticating) {
// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
return _self.destroy({force:true});
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return _self.destroy({ force: true });
}

@@ -280,4 +335,4 @@

// Destroy the instance
if(self.state == DESTROYED || self.state == UNREFERENCED) {
return _self.destroy({force:true});
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return _self.destroy({ force: true });
}

@@ -288,5 +343,5 @@

// Update the state with the new server
if(result) {
if (result) {
// Primary lastIsMaster store it
if(_self.lastIsMaster() && _self.lastIsMaster().ismaster) {
if (_self.lastIsMaster() && _self.lastIsMaster().ismaster) {
self.ismaster = _self.lastIsMaster();

@@ -296,3 +351,3 @@ }

// Remove the handlers
for(var i = 0; i < handlers.length; i++) {
for (var i = 0; i < handlers.length; i++) {
_self.removeAllListeners(handlers[i]);

@@ -313,8 +368,8 @@ }

} else {
_self.destroy({force:true});
_self.destroy({ force: true });
}
});
} else if(event == 'connect' && self.authenticating) {
this.destroy({force:true});
} else if(event == 'error') {
} else if (event === 'connect' && self.authenticating) {
this.destroy({ force: true });
} else if (event === 'error') {
error = err;

@@ -327,8 +382,10 @@ }

// Are we done finish up callback
if(count == 0) { callback(error); }
}
}
if (count === 0) {
callback(error);
}
};
};
// No new servers
if(count == 0) return callback();
if (count === 0) return callback();

@@ -339,3 +396,3 @@ // Execute method

// Destroyed
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return;

@@ -345,10 +402,13 @@ }

// Create a new server instance
var server = new Server(assign({}, self.s.options, {
host: _server.split(':')[0],
port: parseInt(_server.split(':')[1], 10)
}, {
authProviders: self.authProviders, reconnect:false, monitoring: false, inTopology: true
}, {
clientInfo: clone(self.s.clientInfo)
}));
var server = new Server(
Object.assign({}, self.s.options, {
host: _server.split(':')[0],
port: parseInt(_server.split(':')[1], 10),
authProviders: self.authProviders,
reconnect: false,
monitoring: false,
parent: self,
clientInfo: clone(self.s.clientInfo)
})
);

@@ -363,5 +423,11 @@ // Add temp handlers

// SDAM Monitoring events
server.on('serverOpening', function(e) { self.emit('serverOpening', e); });
server.on('serverDescriptionChanged', function(e) { self.emit('serverDescriptionChanged', e); });
server.on('serverClosed', function(e) { self.emit('serverClosed', e); });
server.on('serverOpening', function(e) {
self.emit('serverOpening', e);
});
server.on('serverDescriptionChanged', function(e) {
self.emit('serverDescriptionChanged', e);
});
server.on('serverClosed', function(e) {
self.emit('serverClosed', e);
});
server.connect(self.s.connectOptions);

@@ -372,3 +438,3 @@ }, i);

// Create new instances
for(var i = 0; i < servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
execute(servers[i], i);

@@ -389,69 +455,82 @@ }

// Ensuring ismaster calls are timed out quickly
server.command('admin.$cmd', {
ismaster:true
}, {
monitoring: true,
socketTimeout: self.s.options.connectionTimeout || 2000,
}, function(err, r) {
if(self.state == DESTROYED || self.state == UNREFERENCED) {
server.destroy({force:true});
return cb(err, r);
}
server.command(
'admin.$cmd',
{
ismaster: true
},
{
monitoring: true,
socketTimeout: self.s.options.connectionTimeout || 2000
},
function(err, r) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
server.destroy({ force: true });
return cb(err, r);
}
// Calculate latency
var latencyMS = new Date().getTime() - start;
// Set the last updatedTime
var hrTime = process.hrtime();
// Calculate the last update time
server.lastUpdateTime = hrTime[0] * 1000 + Math.round(hrTime[1]/1000);
// Calculate latency
var latencyMS = new Date().getTime() - start;
// Set the last updatedTime
var hrTime = process.hrtime();
// Calculate the last update time
server.lastUpdateTime = hrTime[0] * 1000 + Math.round(hrTime[1] / 1000);
// We had an error, remove it from the state
if(err) {
// Emit the server heartbeat failure
emitSDAMEvent(self, 'serverHeartbeatFailed', { durationMS: latencyMS, failure: err, connectionId: server.name });
// We had an error, remove it from the state
if (err) {
// Emit the server heartbeat failure
emitSDAMEvent(self, 'serverHeartbeatFailed', {
durationMS: latencyMS,
failure: err,
connectionId: server.name
});
// Remove server from the state
self.s.replicaSetState.remove(server);
} else {
// Update the server ismaster
server.ismaster = r.result;
// Remove server from the state
self.s.replicaSetState.remove(server);
} else {
// Update the server ismaster
server.ismaster = r.result;
// Check if we have a lastWriteDate convert it to MS
// and store on the server instance for later use
if(server.ismaster.lastWrite && server.ismaster.lastWrite.lastWriteDate) {
server.lastWriteDate = server.ismaster.lastWrite.lastWriteDate.getTime();
}
// Check if we have a lastWriteDate convert it to MS
// and store on the server instance for later use
if (server.ismaster.lastWrite && server.ismaster.lastWrite.lastWriteDate) {
server.lastWriteDate = server.ismaster.lastWrite.lastWriteDate.getTime();
}
// Do we have a brand new server
if(server.lastIsMasterMS == -1) {
server.lastIsMasterMS = latencyMS;
} else if(server.lastIsMasterMS) {
// After the first measurement, average RTT MUST be computed using an
// exponentially-weighted moving average formula, with a weighting factor (alpha) of 0.2.
// If the prior average is denoted old_rtt, then the new average (new_rtt) is
// computed from a new RTT measurement (x) using the following formula:
// alpha = 0.2
// new_rtt = alpha * x + (1 - alpha) * old_rtt
server.lastIsMasterMS = 0.2 * latencyMS + (1 - 0.2) * server.lastIsMasterMS;
}
// Do we have a brand new server
if (server.lastIsMasterMS === -1) {
server.lastIsMasterMS = latencyMS;
} else if (server.lastIsMasterMS) {
// After the first measurement, average RTT MUST be computed using an
// exponentially-weighted moving average formula, with a weighting factor (alpha) of 0.2.
// If the prior average is denoted old_rtt, then the new average (new_rtt) is
// computed from a new RTT measurement (x) using the following formula:
// alpha = 0.2
// new_rtt = alpha * x + (1 - alpha) * old_rtt
server.lastIsMasterMS = 0.2 * latencyMS + (1 - 0.2) * server.lastIsMasterMS;
}
if(self.s.replicaSetState.update(server)) {
// Primary lastIsMaster store it
if(server.lastIsMaster() && server.lastIsMaster().ismaster) {
self.ismaster = server.lastIsMaster();
if (self.s.replicaSetState.update(server)) {
// Primary lastIsMaster store it
if (server.lastIsMaster() && server.lastIsMaster().ismaster) {
self.ismaster = server.lastIsMaster();
}
}
// Server heart beat event
emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
durationMS: latencyMS,
reply: r.result,
connectionId: server.name
});
}
// Server heart beat event
emitSDAMEvent(self, 'serverHeartbeatSucceeded', { durationMS: latencyMS, reply: r.result, connectionId: server.name });
// Calculate the staleness for this server
self.s.replicaSetState.updateServerMaxStaleness(server, self.s.haInterval);
// Callback
cb(err, r);
}
);
};
// Calculate the staleness for this server
self.s.replicaSetState.updateServerMaxStaleness(server, self.s.haInterval);
// Callback
cb(err, r);
});
}
// Each server is monitored in parallel in their own timeout loop

@@ -461,5 +540,5 @@ var monitorServer = function(host, self, options) {

// Is this server already being monitoried, then skip monitoring
if(!options.haInterval) {
for(var i = 0; i < self.intervalIds.length; i++) {
if(self.intervalIds[i].__host === host) {
if (!options.haInterval) {
for (var i = 0; i < self.intervalIds.length; i++) {
if (self.intervalIds[i].__host === host) {
return;

@@ -476,3 +555,3 @@ }

var intervalId = new _process(function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
// clearInterval(intervalId);

@@ -487,6 +566,11 @@ intervalId.stop();

// Check if we have a known server connection and reuse
if(_server) {
if (_server) {
// Ping the server
return pingServer(self, _server, function(err) {
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (err) {
// NOTE: should something happen here?
return;
}
if (self.state === DESTROYED || self.state === UNREFERENCED) {
intervalId.stop();

@@ -499,13 +583,12 @@ return;

return intervalId.isRunning();
} );
});
// Initial sweep
if(_process === Timeout) {
if(self.state == CONNECTING && (
(
self.s.replicaSetState.hasSecondary()
&& self.s.options.secondaryOnlyConnectionAllowed
)
|| self.s.replicaSetState.hasPrimary()
)) {
if (_process === Timeout) {
if (
self.state === CONNECTING &&
((self.s.replicaSetState.hasSecondary() &&
self.s.options.secondaryOnlyConnectionAllowed) ||
self.s.replicaSetState.hasPrimary())
) {
self.state = CONNECTED;

@@ -522,9 +605,8 @@

} else {
if(self.state == DISCONNECTED && (
(
self.s.replicaSetState.hasSecondary()
&& self.s.options.secondaryOnlyConnectionAllowed
)
|| self.s.replicaSetState.hasPrimary()
)) {
if (
self.state === DISCONNECTED &&
((self.s.replicaSetState.hasSecondary() &&
self.s.options.secondaryOnlyConnectionAllowed) ||
self.s.replicaSetState.hasPrimary())
) {
self.state = CONNECTED;

@@ -542,13 +624,15 @@

if(self.initialConnectState.connect
&& !self.initialConnectState.fullsetup
&& self.s.replicaSetState.hasPrimaryAndSecondary()) {
// Set initial connect state
self.initialConnectState.fullsetup = true;
self.initialConnectState.all = true;
if (
self.initialConnectState.connect &&
!self.initialConnectState.fullsetup &&
self.s.replicaSetState.hasPrimaryAndSecondary()
) {
// Set initial connect state
self.initialConnectState.fullsetup = true;
self.initialConnectState.all = true;
process.nextTick(function() {
self.emit('fullsetup', self);
self.emit('all', self);
});
process.nextTick(function() {
self.emit('fullsetup', self);
self.emit('all', self);
});
}

@@ -565,6 +649,6 @@ });

self.intervalIds.push(intervalId);
}
};
function topologyMonitor(self, options) {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
options = options || {};

@@ -579,20 +663,35 @@

if(_process === Timeout) {
if (_process === Timeout) {
return connectNewServers(self, self.s.replicaSetState.unknownServers, function(err) {
// Don't emit errors if the connection was already
if(self.state === DESTROYED || self.state === UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return;
}
if(!self.s.replicaSetState.hasPrimary() && !self.s.options.secondaryOnlyConnectionAllowed) {
if(err) return self.emit('error', err);
self.emit('error', new MongoError('no primary found in replicaset'));
return self.destroy({force:true});
} else if(!self.s.replicaSetState.hasSecondary() && self.s.options.secondaryOnlyConnectionAllowed) {
if(err) return self.emit('error', err);
self.emit('error', new MongoError('no secondary found in replicaset'));
return self.destroy({force:true});
if (!self.s.replicaSetState.hasPrimary() && !self.s.options.secondaryOnlyConnectionAllowed) {
if (err) {
return self.emit('error', err);
}
self.emit(
'error',
new MongoError('no primary found in replicaset or invalid replica set name')
);
return self.destroy({ force: true });
} else if (
!self.s.replicaSetState.hasSecondary() &&
self.s.options.secondaryOnlyConnectionAllowed
) {
if (err) {
return self.emit('error', err);
}
self.emit(
'error',
new MongoError('no secondary found in replicaset or invalid replica set name')
);
return self.destroy({ force: true });
}
for(var i = 0; i < servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
monitorServer(servers[i], self, options);

@@ -602,3 +701,3 @@ }

} else {
for(var i = 0; i < servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
monitorServer(servers[i], self, options);

@@ -611,3 +710,3 @@ }

return function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) {
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return;

@@ -618,3 +717,4 @@ }

var monitoringFrequencey = self.s.replicaSetState.hasPrimary()
? _haInterval : self.s.minHeartbeatFrequencyMS;
? _haInterval
: self.s.minHeartbeatFrequencyMS;

@@ -624,3 +724,3 @@ // Create a timeout

});
}
};
}

@@ -631,3 +731,3 @@

? self.s.minHeartbeatFrequencyMS
: _haInterval
: _haInterval;

@@ -638,4 +738,4 @@ self.intervalIds.push(new Timeout(executeReconnect(self), intervalTime).start());

function addServerToList(list, server) {
for(var i = 0; i < list.length; i++) {
if(list[i].name.toLowerCase() === server.name.toLowerCase()) return true;
for (var i = 0; i < list.length; i++) {
if (list[i].name.toLowerCase() === server.name.toLowerCase()) return true;
}

@@ -648,6 +748,8 @@

return function() {
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// Debug log
if(self.s.logger.isDebug()) {
self.s.logger.debug(f('handleEvent %s from server %s in replset with id %s', event, this.name, self.id));
if (self.s.logger.isDebug()) {
self.s.logger.debug(
f('handleEvent %s from server %s in replset with id %s', event, this.name, self.id)
);
}

@@ -659,10 +761,12 @@

// Are we in a destroyed state return
if(self.state == DESTROYED || self.state == UNREFERENCED) return;
if (self.state === DESTROYED || self.state === UNREFERENCED) return;
// If no primary and secondary available
if(!self.s.replicaSetState.hasPrimary()
&& !self.s.replicaSetState.hasSecondary()
&& self.s.options.secondaryOnlyConnectionAllowed) {
if (
!self.s.replicaSetState.hasPrimary() &&
!self.s.replicaSetState.hasSecondary() &&
self.s.options.secondaryOnlyConnectionAllowed
) {
stateTransition(self, DISCONNECTED);
} else if(!self.s.replicaSetState.hasPrimary()) {
} else if (!self.s.replicaSetState.hasPrimary()) {
stateTransition(self, DISCONNECTED);

@@ -672,7 +776,7 @@ }

addServerToList(self.s.connectingServers, this);
}
};
}
function applyAuthenticationContexts(self, server, callback) {
if(self.s.authenticationContexts.length == 0) {
if (self.s.authenticationContexts.length === 0) {
return callback();

@@ -682,3 +786,3 @@ }

// Do not apply any auth contexts if it's an arbiter
if(server.lastIsMaster() && server.lastIsMaster().arbiterOnly) {
if (server.lastIsMaster() && server.lastIsMaster().arbiterOnly) {
return callback();

@@ -693,3 +797,3 @@ }

function applyAuth(authContexts, server, callback) {
if(authContexts.length == 0) return callback();
if (authContexts.length === 0) return callback();
// Get the first auth context

@@ -700,3 +804,3 @@ var authContext = authContexts.shift();

// Push our callback handler
customAuthContext.push(function(err) {
customAuthContext.push(function(/* err */) {
applyAuth(authContexts, server, callback);

@@ -706,3 +810,3 @@ });

// Attempt authentication
server.auth.apply(server, customAuthContext)
server.auth.apply(server, customAuthContext);
}

@@ -718,18 +822,25 @@

// Debug log
if(self.s.logger.isDebug()) {
self.s.logger.debug(f('handleInitialConnectEvent %s from server %s in replset with id %s', event, this.name, self.id));
if (self.s.logger.isDebug()) {
self.s.logger.debug(
f(
'handleInitialConnectEvent %s from server %s in replset with id %s',
event,
this.name,
self.id
)
);
}
// Destroy the instance
if(self.state == DESTROYED || self.state == UNREFERENCED) {
return this.destroy({force:true});
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return this.destroy({ force: true });
}
// Check the type of server
if(event == 'connect') {
if (event === 'connect') {
// Do we have authentication contexts that need to be applied
applyAuthenticationContexts(self, _this, function() {
// Destroy the instance
if(self.state == DESTROYED || self.state == UNREFERENCED) {
return _this.destroy({force:true});
if (self.state === DESTROYED || self.state === UNREFERENCED) {
return _this.destroy({ force: true });
}

@@ -739,5 +850,5 @@

var result = self.s.replicaSetState.update(_this);
if(result == true) {
if (result === true) {
// Primary lastIsMaster store it
if(_this.lastIsMaster() && _this.lastIsMaster().ismaster) {
if (_this.lastIsMaster() && _this.lastIsMaster().ismaster) {
self.ismaster = _this.lastIsMaster();

@@ -747,8 +858,16 @@ }

// Debug log
if(self.s.logger.isDebug()) {
self.s.logger.debug(f('handleInitialConnectEvent %s from server %s in replset with id %s has state [%s]', event, _this.name, self.id, JSON.stringify(self.s.replicaSetState.set)));
if (self.s.logger.isDebug()) {
self.s.logger.debug(
f(
'handleInitialConnectEvent %s from server %s in replset with id %s has state [%s]',
event,
_this.name,
self.id,
JSON.stringify(self.s.replicaSetState.set)
)
);
}
// Remove the handlers
for(var i = 0; i < handlers.length; i++) {
for (var i = 0; i < handlers.length; i++) {
_this.removeAllListeners(handlers[i]);

@@ -764,4 +883,6 @@ }

// Do we have a primary or primaryAndSecondary
if(self.state === CONNECTING && self.s.replicaSetState.hasPrimary()
|| (self.s.replicaSetState.hasSecondary() && self.s.options.secondaryOnlyConnectionAllowed)) {
if (
(self.state === CONNECTING && self.s.replicaSetState.hasPrimary()) ||
(self.s.replicaSetState.hasSecondary() && self.s.options.secondaryOnlyConnectionAllowed)
) {
// We are connected

@@ -779,8 +900,8 @@ self.state = CONNECTED;

}
} else if(result instanceof MongoError) {
_this.destroy({force:true});
self.destroy({force:true});
} else if (result instanceof MongoError) {
_this.destroy({ force: true });
self.destroy({ force: true });
return self.emit('error', result);
} else {
_this.destroy({force:true});
_this.destroy({ force: true });
}

@@ -797,18 +918,20 @@ });

if(self.initialConnectState.connect
&& !self.initialConnectState.fullsetup
&& self.s.replicaSetState.hasPrimaryAndSecondary()) {
// Set initial connect state
self.initialConnectState.fullsetup = true;
self.initialConnectState.all = true;
if (
self.initialConnectState.connect &&
!self.initialConnectState.fullsetup &&
self.s.replicaSetState.hasPrimaryAndSecondary()
) {
// Set initial connect state
self.initialConnectState.fullsetup = true;
self.initialConnectState.all = true;
process.nextTick(function() {
self.emit('fullsetup', self);
self.emit('all', self);
});
process.nextTick(function() {
self.emit('fullsetup', self);
self.emit('all', self);
});
}
// Remove from the list from connectingServers
for(var i = 0; i < self.s.connectingServers.length; i++) {
if(self.s.connectingServers[i].equals(this)) {
for (var i = 0; i < self.s.connectingServers.length; i++) {
if (self.s.connectingServers[i].equals(this)) {
self.s.connectingServers.splice(i, 1);

@@ -819,4 +942,4 @@ }

// Trigger topologyMonitor
if(self.s.connectingServers.length == 0 && self.state == CONNECTING) {
topologyMonitor(self, {haInterval: 1});
if (self.s.connectingServers.length === 0 && self.state === CONNECTING) {
topologyMonitor(self, { haInterval: 1 });
}

@@ -837,5 +960,5 @@ };

// Add the server to the state
if(self.s.replicaSetState.update(server)) {
if (self.s.replicaSetState.update(server)) {
// Primary lastIsMaster store it
if(server.lastIsMaster() && server.lastIsMaster().ismaster) {
if (server.lastIsMaster() && server.lastIsMaster().ismaster) {
self.ismaster = server.lastIsMaster();

@@ -852,5 +975,11 @@ }

// SDAM Monitoring events
server.on('serverOpening', function(e) { self.emit('serverOpening', e); });
server.on('serverDescriptionChanged', function(e) { self.emit('serverDescriptionChanged', e); });
server.on('serverClosed', function(e) { self.emit('serverClosed', e); });
server.on('serverOpening', function(e) {
self.emit('serverOpening', e);
});
server.on('serverDescriptionChanged', function(e) {
self.emit('serverDescriptionChanged', e);
});
server.on('serverClosed', function(e) {
self.emit('serverClosed', e);
});
// Start connection

@@ -862,3 +991,3 @@ server.connect(self.s.connectOptions);

// Start all the servers
while(servers.length > 0) {
while (servers.length > 0) {
connect(servers.shift(), timeoutInterval++);

@@ -873,3 +1002,3 @@ }

function emitSDAMEvent(self, event, description) {
if(self.listeners(event).length > 0) {
if (self.listeners(event).length > 0) {
self.emit(event, description);

@@ -892,13 +1021,28 @@ }

var servers = this.s.seedlist.map(function(x) {
return new Server(assign({}, self.s.options, x, {
authProviders: self.authProviders, reconnect:false, monitoring:false, inTopology: true
}, {
clientInfo: clone(self.s.clientInfo)
}));
return new Server(
Object.assign({}, self.s.options, x, {
authProviders: self.authProviders,
reconnect: false,
monitoring: false,
parent: self,
clientInfo: clone(self.s.clientInfo)
})
);
});
// Error out as high availbility interval must be < than socketTimeout
if(this.s.options.socketTimeout > 0 && this.s.options.socketTimeout <= this.s.options.haInterval) {
return self.emit('error', new MongoError(f("haInterval [%s] MS must be set to less than socketTimeout [%s] MS"
, this.s.options.haInterval, this.s.options.socketTimeout)));
if (
this.s.options.socketTimeout > 0 &&
this.s.options.socketTimeout <= this.s.options.haInterval
) {
return self.emit(
'error',
new MongoError(
f(
'haInterval [%s] MS must be set to less than socketTimeout [%s] MS',
this.s.options.haInterval,
this.s.options.socketTimeout
)
)
);
}

@@ -910,3 +1054,3 @@

connectServers(self, servers);
}
};

@@ -923,3 +1067,3 @@ /**

// Clear out any monitoring process
if(this.haTimeoutId) clearTimeout(this.haTimeoutId);
if (this.haTimeoutId) clearTimeout(this.haTimeoutId);
// Destroy the replicaset

@@ -936,3 +1080,3 @@ this.s.replicaSetState.destroy(options);

// Clear out all monitoring
for(var i = 0; i < this.intervalIds.length; i++) {
for (var i = 0; i < this.intervalIds.length; i++) {
this.intervalIds[i].stop();

@@ -947,3 +1091,3 @@ this.intervalIds[i].stop();

emitSDAMEvent(this, 'topologyClosed', { topologyId: this.id });
}
};

@@ -963,3 +1107,3 @@ /**

clearTimeout(this.haTimeoutId);
}
};

@@ -974,11 +1118,14 @@ /**

// return the secondaries ismaster result.
if (this.s.options.secondaryOnlyConnectionAllowed
&& !this.s.replicaSetState.hasPrimary()
&& this.s.replicaSetState.hasSecondary()) {
return this.s.replicaSetState.secondaries[0].lastIsMaster();
}
if (
this.s.options.secondaryOnlyConnectionAllowed &&
!this.s.replicaSetState.hasPrimary() &&
this.s.replicaSetState.hasSecondary()
) {
return this.s.replicaSetState.secondaries[0].lastIsMaster();
}
return this.s.replicaSetState.primary
? this.s.replicaSetState.primary.lastIsMaster() : this.ismaster;
}
? this.s.replicaSetState.primary.lastIsMaster()
: this.ismaster;
};

@@ -993,3 +1140,3 @@ /**

var connections = [];
for(var i = 0; i < servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
connections = connections.concat(servers[i].connections());

@@ -999,3 +1146,3 @@ }

return connections;
}
};

@@ -1013,33 +1160,28 @@ /**

// To avoid interleaving of operations
if(this.authenticating) return false;
if (this.authenticating) return false;
// If we specified a read preference check if we are connected to something
// than can satisfy this
if(options.readPreference
&& options.readPreference.equals(ReadPreference.secondary)) {
if (options.readPreference && options.readPreference.equals(ReadPreference.secondary)) {
return this.s.replicaSetState.hasSecondary();
}
if(options.readPreference
&& options.readPreference.equals(ReadPreference.primary)) {
if (options.readPreference && options.readPreference.equals(ReadPreference.primary)) {
return this.s.replicaSetState.hasPrimary();
}
if(options.readPreference
&& options.readPreference.equals(ReadPreference.primaryPreferred)) {
if (options.readPreference && options.readPreference.equals(ReadPreference.primaryPreferred)) {
return this.s.replicaSetState.hasSecondary() || this.s.replicaSetState.hasPrimary();
}
if(options.readPreference
&& options.readPreference.equals(ReadPreference.secondaryPreferred)) {
if (options.readPreference && options.readPreference.equals(ReadPreference.secondaryPreferred)) {
return this.s.replicaSetState.hasSecondary() || this.s.replicaSetState.hasPrimary();
}
if(this.s.options.secondaryOnlyConnectionAllowed
&& this.s.replicaSetState.hasSecondary()) {
return true;
if (this.s.options.secondaryOnlyConnectionAllowed && this.s.replicaSetState.hasSecondary()) {
return true;
}
return this.s.replicaSetState.hasPrimary();
}
};

@@ -1052,4 +1194,4 @@ /**

ReplSet.prototype.isDestroyed = function() {
return this.state == DESTROYED;
}
return this.state === DESTROYED;
};

@@ -1065,7 +1207,7 @@ /**

options = options || {};
// Pick the right server baspickServerd on readPreference
// Pick the right server based on readPreference
var server = this.s.replicaSetState.pickServer(options.readPreference);
if(this.s.debug) this.emit('pickedServer', options.readPreference, server);
if (this.s.debug) this.emit('pickedServer', options.readPreference, server);
return server;
}
};

@@ -1080,4 +1222,4 @@ /**

var server = this.getServer(options);
if(server) return server.getConnection();
}
if (server) return server.getConnection();
};

@@ -1091,3 +1233,3 @@ /**

return this.s.replicaSetState.allServers();
}
};

@@ -1097,15 +1239,33 @@ //

var executeWriteOperation = function(self, op, ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
// Ensure we have no options
if (typeof options === 'function') (callback = options), (options = {});
options = options || {};
// No server returned we had an error
if(self.s.replicaSetState.primary == null) {
return callback(new MongoError("no primary server found"));
if (!options.retryWrites || !options.session || !isRetryableWritesSupported(self)) {
// No server returned we had an error
if (self.s.replicaSetState.primary == null) {
return callback(new MongoError('no primary server found'));
}
// Execute the command
return self.s.replicaSetState.primary[op](ns, ops, options, callback);
}
// Execute the command
self.s.replicaSetState.primary[op](ns, ops, options, callback);
}
// increment and assign txnNumber
options.txnNumber = getNextTransactionNumber(options.session);
self.s.replicaSetState.primary[op](ns, ops, options, (err, result) => {
if (!err) return callback(null, result);
if (!(err instanceof errors.MongoNetworkError) && !err.message.match(/not master/)) {
return callback(err);
}
if (self.s.replicaSetState.primary == null) {
return callback(new MongoError('no primary server found'));
}
// Re-execute the command
self.s.replicaSetState.primary[op](ns, ops, options, callback);
});
};
/**

@@ -1120,10 +1280,15 @@ * Insert one or more documents

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
ReplSet.prototype.insert = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
if (!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('insert', ns, ops, options, callback);

@@ -1134,3 +1299,3 @@ }

executeWriteOperation(this, 'insert', ns, ops, options, callback);
}
};

@@ -1146,10 +1311,15 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
ReplSet.prototype.update = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
if (!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('update', ns, ops, options, callback);

@@ -1160,3 +1330,3 @@ }

executeWriteOperation(this, 'update', ns, ops, options, callback);
}
};

@@ -1172,10 +1342,15 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {boolean} [options.retryWrites] Enable retryable writes for this operation
* @param {opResultCallback} callback A callback function
*/
ReplSet.prototype.remove = function(ns, ops, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
// Not connected but we have a disconnecthandler
if(!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
if (!this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
return this.s.disconnectHandler.add('remove', ns, ops, options, callback);

@@ -1186,3 +1361,3 @@ }

executeWriteOperation(this, 'remove', ns, ops, options, callback);
}
};

@@ -1198,7 +1373,11 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function
*/
ReplSet.prototype.command = function(ns, cmd, options, callback) {
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if(this.state == DESTROYED) return callback(new MongoError(f('topology was destroyed')));
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
var self = this;

@@ -1210,7 +1389,20 @@

// If the readPreference is primary and we have no primary, store it
if(readPreference.preference == 'primary' && !this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
if (
readPreference.preference === 'primary' &&
!this.s.replicaSetState.hasPrimary() &&
this.s.disconnectHandler != null
) {
return this.s.disconnectHandler.add('command', ns, cmd, options, callback);
} else if(readPreference.preference == 'secondary' && !this.s.replicaSetState.hasSecondary() && this.s.disconnectHandler != null) {
} else if (
readPreference.preference === 'secondary' &&
!this.s.replicaSetState.hasSecondary() &&
this.s.disconnectHandler != null
) {
return this.s.disconnectHandler.add('command', ns, cmd, options, callback);
} else if(readPreference.preference != 'primary' && !this.s.replicaSetState.hasSecondary() && !this.s.replicaSetState.hasPrimary() && this.s.disconnectHandler != null) {
} else if (
readPreference.preference !== 'primary' &&
!this.s.replicaSetState.hasSecondary() &&
!this.s.replicaSetState.hasPrimary() &&
this.s.disconnectHandler != null
) {
return this.s.disconnectHandler.add('command', ns, cmd, options, callback);

@@ -1222,9 +1414,13 @@ }

// We received an error, return it
if(!(server instanceof Server)) return callback(server);
if (!(server instanceof Server)) return callback(server);
// Emit debug event
if(self.s.debug) self.emit('pickedServer', ReadPreference.primary, server);
if (self.s.debug) self.emit('pickedServer', ReadPreference.primary, server);
// No server returned we had an error
if(server == null) {
return callback(new MongoError(f("no server found that matches the provided readPreference %s", readPreference)));
if (server == null) {
return callback(
new MongoError(
f('no server found that matches the provided readPreference %s', readPreference)
)
);
}

@@ -1234,3 +1430,3 @@

server.command(ns, cmd, options, callback);
}
};

@@ -1253,8 +1449,8 @@ /**

// If we don't have the mechanism fail
if(this.authProviders[mechanism] == null && mechanism != 'default') {
return callback(new MongoError(f("auth provider %s does not exist", mechanism)));
if (this.authProviders[mechanism] == null && mechanism !== 'default') {
return callback(new MongoError(f('auth provider %s does not exist', mechanism)));
}
// Are we already authenticating, throw
if(this.authenticating) {
if (this.authenticating) {
return callback(new MongoError('authentication or logout allready in process'));

@@ -1265,6 +1461,9 @@ }

// Executed at some point when the handler deems it's reconnected
if(self.s.disconnectHandler != null) {
if(!self.s.replicaSetState.hasPrimary() && !self.s.options.secondaryOnlyConnectionAllowed) {
if (self.s.disconnectHandler != null) {
if (!self.s.replicaSetState.hasPrimary() && !self.s.options.secondaryOnlyConnectionAllowed) {
return self.s.disconnectHandler.add('auth', db, allArgs, {}, callback);
} else if(!self.s.replicaSetState.hasSecondary() && self.s.options.secondaryOnlyConnectionAllowed) {
} else if (
!self.s.replicaSetState.hasSecondary() &&
self.s.options.secondaryOnlyConnectionAllowed
) {
return self.s.disconnectHandler.add('auth', db, allArgs, {}, callback);

@@ -1282,3 +1481,3 @@ }

// No servers return
if(servers.length == 0) {
if (servers.length === 0) {
this.authenticating = false;

@@ -1293,27 +1492,33 @@ callback(null, true);

// Create arguments
var finalArguments = argsWithoutCallback.concat([function(err) {
count = count - 1;
// Save all the errors
if(err) errors.push({name: server.name, err: err});
// We are done
if(count == 0) {
// Auth is done
self.authenticating = false;
var finalArguments = argsWithoutCallback.concat([
function(err) {
count = count - 1;
// Save all the errors
if (err) errors.push({ name: server.name, err: err });
// We are done
if (count === 0) {
// Auth is done
self.authenticating = false;
// Return the auth error
if(errors.length) {
// Remove the entry from the stored authentication contexts
self.s.authenticationContexts.splice(currentContextIndex, 0);
// Return error
return callback(MongoError.create({
message: 'authentication fail', errors: errors
}), false);
// Return the auth error
if (errors.length) {
// Remove the entry from the stored authentication contexts
self.s.authenticationContexts.splice(currentContextIndex, 0);
// Return error
return callback(
new MongoError({
message: 'authentication fail',
errors: errors
}),
false
);
}
// Successfully authenticated session
callback(null, self);
}
// Successfully authenticated session
callback(null, self);
}
}]);
]);
if(!server.lastIsMaster().arbiterOnly) {
if (!server.lastIsMaster().arbiterOnly) {
// Execute the auth only against non arbiter servers

@@ -1337,6 +1542,6 @@ server.auth.apply(server, finalArguments);

// Authenticate against all servers
while(servers.length > 0) {
while (servers.length > 0) {
auth(servers.shift());
}
}
};

@@ -1352,3 +1557,3 @@ /**

// Are we authenticating or logging out, throw
if(this.authenticating) {
if (this.authenticating) {
throw new MongoError('authentication or logout allready in process');

@@ -1362,3 +1567,3 @@ }

var providers = Object.keys(this.authProviders);
for(var i = 0; i < providers.length; i++) {
for (var i = 0; i < providers.length; i++) {
this.authProviders[providers[i]].logout(dbName);

@@ -1375,3 +1580,3 @@ }

var count = servers.length;
if(count == 0) return callback();
if (count === 0) return callback();
var errors = [];

@@ -1381,3 +1586,3 @@

_server.logout(dbName, function(err) {
if(err) errors.push({name: _server.name, err: err});
if (err) errors.push({ name: _server.name, err: err });
cb();

@@ -1388,13 +1593,18 @@ });

// Execute logout on all server instances
for(i = 0; i < servers.length; i++) {
for (i = 0; i < servers.length; i++) {
logoutServer(servers[i], function() {
count = count - 1;
if(count == 0) {
if (count === 0) {
// Do not block new operations
self.authenticating = false;
// If we have one or more errors
if(errors.length) return callback(MongoError.create({
message: f('logout failed against db %s', dbName), errors: errors
}), false);
if (errors.length)
return callback(
new MongoError({
message: f('logout failed against db %s', dbName),
errors: errors
}),
false
);

@@ -1404,11 +1614,12 @@ // No errors

}
})
});
}
}
};
/**
* Perform one or more remove operations
* Get a new cursor
* @method
* @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
* @param {{object}|{Long}} cmd Can be either a command returning a cursor or a cursorId
* @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
* @param {object} [options] Options for the cursor
* @param {object} [options.batchSize=0] Batchsize for the operation

@@ -1419,10 +1630,17 @@ * @param {array} [options.documents=[]] Initial documents list for cursor

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {opResultCallback} callback A callback function
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {object} [options.topology] The internal topology of the created cursor
* @returns {Cursor}
*/
ReplSet.prototype.cursor = function(ns, cmd, cursorOptions) {
cursorOptions = cursorOptions || {};
var FinalCursor = cursorOptions.cursorFactory || this.s.Cursor;
return new FinalCursor(this.s.bson, ns, cmd, cursorOptions, this, this.s.options);
}
ReplSet.prototype.cursor = function(ns, cmd, options) {
options = options || {};
const topology = options.topology || this;
// Set up final cursor type
var FinalCursor = options.cursorFactory || this.s.Cursor;
// Return the cursor
return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
};
/**

@@ -1429,0 +1647,0 @@ * A replset connect event, used to verify that the connection is up and running

@@ -1,2 +0,2 @@

"use strict"
'use strict';

@@ -12,4 +12,4 @@ var inherits = require('util').inherits,

Query = require('../connection/commands').Query,
MongoError = require('../error'),
PreTwoSixWireProtocolSupport = require('../wireprotocol/2_4_support'),
MongoError = require('../error').MongoError,
MongoNetworkError = require('../error').MongoNetworkError,
TwoSixWireProtocolSupport = require('../wireprotocol/2_6_support'),

@@ -20,9 +20,35 @@ ThreeTwoWireProtocolSupport = require('../wireprotocol/3_2_support'),

assign = require('../utils').assign,
createClientInfo = require('./shared').createClientInfo;
createClientInfo = require('./shared').createClientInfo,
createCompressionInfo = require('./shared').createCompressionInfo,
resolveClusterTime = require('./shared').resolveClusterTime,
SessionMixins = require('./shared').SessionMixins;
// Used for filtering out fields for loggin
var debugFields = ['reconnect', 'reconnectTries', 'reconnectInterval', 'emitError', 'cursorFactory', 'host'
, 'port', 'size', 'keepAlive', 'keepAliveInitialDelay', 'noDelay', 'connectionTimeout', 'checkServerIdentity'
, 'socketTimeout', 'singleBufferSerializtion', 'ssl', 'ca', 'crl', 'cert', 'key', 'rejectUnauthorized', 'promoteLongs', 'promoteValues'
, 'promoteBuffers', 'servername'];
var debugFields = [
'reconnect',
'reconnectTries',
'reconnectInterval',
'emitError',
'cursorFactory',
'host',
'port',
'size',
'keepAlive',
'keepAliveInitialDelay',
'noDelay',
'connectionTimeout',
'checkServerIdentity',
'socketTimeout',
'singleBufferSerializtion',
'ssl',
'ca',
'crl',
'cert',
'key',
'rejectUnauthorized',
'promoteLongs',
'promoteValues',
'promoteBuffers',
'servername'
];

@@ -101,5 +127,20 @@ // Server instance id

// BSON instance
bson: options.bson || new BSON([BSON.Binary, BSON.Code, BSON.DBRef, BSON.Decimal128,
BSON.Double, BSON.Int32, BSON.Long, BSON.Map, BSON.MaxKey, BSON.MinKey,
BSON.ObjectId, BSON.BSONRegExp, BSON.Symbol, BSON.Timestamp]),
bson:
options.bson ||
new BSON([
BSON.Binary,
BSON.Code,
BSON.DBRef,
BSON.Decimal128,
BSON.Double,
BSON.Int32,
BSON.Long,
BSON.Map,
BSON.MaxKey,
BSON.MinKey,
BSON.ObjectId,
BSON.BSONRegExp,
BSON.Symbol,
BSON.Timestamp
]),
// Pool

@@ -110,11 +151,18 @@ pool: null,

// Monitor thread (keeps the connection alive)
monitoring: typeof options.monitoring == 'boolean' ? options.monitoring : true,
monitoring: typeof options.monitoring === 'boolean' ? options.monitoring : true,
// Is the server in a topology
inTopology: typeof options.inTopology == 'boolean' ? options.inTopology : false,
inTopology: !!options.parent,
// Monitoring timeout
monitoringInterval: typeof options.monitoringInterval == 'number'
? options.monitoringInterval
: 5000,
monitoringInterval:
typeof options.monitoringInterval === 'number' ? options.monitoringInterval : 5000,
// Topology id
topologyId: -1
topologyId: -1,
compression: { compressors: createCompressionInfo(options) },
// Optional parent topology
parent: options.parent
};
// If this is a single deployment we need to track the clusterTime here
if (!this.s.parent) {
this.s.clusterTime = null;
}

@@ -129,6 +177,6 @@

// Initial connection
this.initalConnect = true;
this.initialConnect = true;
// Wire protocol handler, default to oldest known protocol handler
// this gets changed when the first ismaster is called.
this.wireProtocolHandler = new PreTwoSixWireProtocolSupport();
this.wireProtocolHandler = new TwoSixWireProtocolSupport();
// Default type

@@ -146,47 +194,77 @@ this._type = 'server';

this.staleness = 0;
}
};
inherits(Server, EventEmitter);
Object.assign(Server.prototype, SessionMixins);
Object.defineProperty(Server.prototype, 'type', {
enumerable:true, get: function() { return this._type; }
enumerable: true,
get: function() {
return this._type;
}
});
Object.defineProperty(Server.prototype, 'parserType', {
enumerable:true, get: function() {
return BSON.native ? "c++" : "js";
enumerable: true,
get: function() {
return BSON.native ? 'c++' : 'js';
}
});
Object.defineProperty(Server.prototype, 'logicalSessionTimeoutMinutes', {
enumerable: true,
get: function() {
if (!this.ismaster) return null;
return this.ismaster.logicalSessionTimeoutMinutes || null;
}
});
// In single server deployments we track the clusterTime directly on the topology, however
// in Mongos and ReplSet deployments we instead need to delegate the clusterTime up to the
// tracking objects so we can ensure we are gossiping the maximum time received from the
// server.
Object.defineProperty(Server.prototype, 'clusterTime', {
enumerable: true,
set: function(clusterTime) {
const settings = this.s.parent ? this.s.parent : this.s;
resolveClusterTime(settings, clusterTime);
},
get: function() {
const settings = this.s.parent ? this.s.parent : this.s;
return settings.clusterTime || null;
}
});
Server.enableServerAccounting = function() {
serverAccounting = true;
servers = {};
}
};
Server.disableServerAccounting = function() {
serverAccounting = false;
}
};
Server.servers = function() {
return servers;
}
};
Object.defineProperty(Server.prototype, 'name', {
enumerable:true,
get: function() { return this.s.options.host + ":" + this.s.options.port; }
enumerable: true,
get: function() {
return this.s.options.host + ':' + this.s.options.port;
}
});
function isSupportedServer(response) {
return response && typeof response.maxWireVersion === 'number' && response.maxWireVersion >= 2;
}
function configureWireProtocolHandler(self, ismaster) {
// 3.2 wire protocol handler
if(ismaster.maxWireVersion >= 4) {
if (ismaster.maxWireVersion >= 4) {
return new ThreeTwoWireProtocolSupport(new TwoSixWireProtocolSupport());
}
// 2.6 wire protocol handler
if(ismaster.maxWireVersion >= 2) {
return new TwoSixWireProtocolSupport();
}
// 2.4 or earlier wire protocol handler
return new PreTwoSixWireProtocolSupport();
// default to 2.6 wire protocol handler
return new TwoSixWireProtocolSupport();
}

@@ -197,3 +275,8 @@

// Executed at some point when the handler deems it's reconnected
if(!self.s.pool.isConnected() && self.s.options.reconnect && self.s.disconnectHandler != null && !options.monitoring) {
if (
!self.s.pool.isConnected() &&
self.s.options.reconnect &&
self.s.disconnectHandler != null &&
!options.monitoring
) {
self.s.disconnectHandler.add(type, ns, cmd, options, callback);

@@ -204,4 +287,4 @@ return true;

// If we have no connection error
if(!self.s.pool.isConnected()) {
callback(MongoError.create(f("no connection available to server %s", self.name)));
if (!self.s.pool.isConnected()) {
callback(new MongoError(f('no connection available to server %s', self.name)));
return true;

@@ -214,3 +297,3 @@ }

// Pool was destroyed do not continue process
if(self.s.pool.isDestroyed()) return;
if (self.s.pool.isDestroyed()) return;
// Emit monitoring Process event

@@ -222,3 +305,3 @@ self.emit('monitoring', self);

// Create a query instance
var query = new Query(self.s.bson, 'admin.$cmd', {ismaster:true}, queryOptions);
var query = new Query(self.s.bson, 'admin.$cmd', { ismaster: true }, queryOptions);
// Get start time

@@ -228,17 +311,24 @@ var start = new Date().getTime();

// Execute the ismaster query
self.s.pool.write(query, {
socketTimeout: (typeof self.s.options.connectionTimeout !== 'number') ? 2000 : self.s.options.connectionTimeout,
monitoring: true,
}, function(err, result) {
// Set initial lastIsMasterMS
self.lastIsMasterMS = new Date().getTime() - start;
if(self.s.pool.isDestroyed()) return;
// Update the ismaster view if we have a result
if(result) {
self.ismaster = result.result;
self.s.pool.write(
query,
{
socketTimeout:
typeof self.s.options.connectionTimeout !== 'number'
? 2000
: self.s.options.connectionTimeout,
monitoring: true
},
function(err, result) {
// Set initial lastIsMasterMS
self.lastIsMasterMS = new Date().getTime() - start;
if (self.s.pool.isDestroyed()) return;
// Update the ismaster view if we have a result
if (result) {
self.ismaster = result.result;
}
// Re-schedule the monitoring process
self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
}
// Re-schedule the monitoring process
self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
});
}
);
};
}

@@ -249,10 +339,11 @@

// Log information of received information if in info mode
if(self.s.logger.isInfo()) {
var object = err instanceof MongoError ? JSON.stringify(err) : {}
self.s.logger.info(f('server %s fired event %s out with message %s'
, self.name, event, object));
if (self.s.logger.isInfo()) {
var object = err instanceof MongoError ? JSON.stringify(err) : {};
self.s.logger.info(
f('server %s fired event %s out with message %s', self.name, event, object)
);
}
// Handle connect event
if(event == 'connect') {
if (event === 'connect') {
// Issue an ismaster command at connect

@@ -262,61 +353,117 @@ // Query options

// Create a query instance
var query = new Query(self.s.bson, 'admin.$cmd', {ismaster:true, client: self.clientInfo}, queryOptions);
var compressors =
self.s.compression && self.s.compression.compressors ? self.s.compression.compressors : [];
var query = new Query(
self.s.bson,
'admin.$cmd',
{ ismaster: true, client: self.clientInfo, compression: compressors },
queryOptions
);
// Get start time
var start = new Date().getTime();
// Execute the ismaster query
self.s.pool.write(query, {
socketTimeout: self.s.options.connectionTimeout || 2000,
}, function(err, result) {
// Set initial lastIsMasterMS
self.lastIsMasterMS = new Date().getTime() - start;
if(err) {
self.destroy();
if(self.listeners('error').length > 0) self.emit('error', err);
return;
}
self.s.pool.write(
query,
{
socketTimeout: self.s.options.connectionTimeout || 2000
},
function(err, result) {
// Set initial lastIsMasterMS
self.lastIsMasterMS = new Date().getTime() - start;
if (err) {
self.destroy();
return self.emit('error', err);
}
// Ensure no error emitted after initial connect when reconnecting
self.initalConnect = false;
// Save the ismaster
self.ismaster = result.result;
if (!isSupportedServer(result.result)) {
self.destroy();
return self.emit('error', new MongoError('unsupported server version'), self);
}
// It's a proxy change the type so
// the wireprotocol will send $readPreference
if(self.ismaster.msg == 'isdbgrid') {
self._type = 'mongos';
}
// Add the correct wire protocol handler
self.wireProtocolHandler = configureWireProtocolHandler(self, self.ismaster);
// Have we defined self monitoring
if(self.s.monitoring) {
self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
}
// Determine whether the server is instructing us to use a compressor
if (result.result && result.result.compression) {
for (var i = 0; i < self.s.compression.compressors.length; i++) {
if (result.result.compression.indexOf(self.s.compression.compressors[i]) > -1) {
self.s.pool.options.agreedCompressor = self.s.compression.compressors[i];
break;
}
}
// Emit server description changed if something listening
sdam.emitServerDescriptionChanged(self, {
address: self.name, arbiters: [], hosts: [], passives: [], type: sdam.getTopologyType(self)
});
if (self.s.compression.zlibCompressionLevel) {
self.s.pool.options.zlibCompressionLevel = self.s.compression.zlibCompressionLevel;
}
}
if(!self.s.inTopology) {
// Emit topology description changed if something listening
sdam.emitTopologyDescriptionChanged(self, {
topologyType: 'Single', servers: [{address: self.name, arbiters: [], hosts: [], passives: [], type: sdam.getTopologyType(self)}]
// Ensure no error emitted after initial connect when reconnecting
self.initialConnect = false;
// Save the ismaster
self.ismaster = result.result;
// It's a proxy change the type so
// the wireprotocol will send $readPreference
if (self.ismaster.msg === 'isdbgrid') {
self._type = 'mongos';
}
// Add the correct wire protocol handler
self.wireProtocolHandler = configureWireProtocolHandler(self, self.ismaster);
// Have we defined self monitoring
if (self.s.monitoring) {
self.monitoringProcessId = setTimeout(
monitoringProcess(self),
self.s.monitoringInterval
);
}
// Emit server description changed if something listening
sdam.emitServerDescriptionChanged(self, {
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: sdam.getTopologyType(self)
});
}
// Log the ismaster if available
if(self.s.logger.isInfo()) {
self.s.logger.info(f('server %s connected with ismaster [%s]', self.name, JSON.stringify(self.ismaster)));
if (!self.s.inTopology) {
// Emit topology description changed if something listening
sdam.emitTopologyDescriptionChanged(self, {
topologyType: 'Single',
servers: [
{
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: sdam.getTopologyType(self)
}
]
});
}
// Log the ismaster if available
if (self.s.logger.isInfo()) {
self.s.logger.info(
f('server %s connected with ismaster [%s]', self.name, JSON.stringify(self.ismaster))
);
}
// Emit connect
self.emit('connect', self);
}
// Emit connect
self.emit('connect', self);
});
} else if(event == 'error' || event == 'parseError'
|| event == 'close' || event == 'timeout' || event == 'reconnect'
|| event == 'attemptReconnect' || 'reconnectFailed') {
);
} else if (
event === 'error' ||
event === 'parseError' ||
event === 'close' ||
event === 'timeout' ||
event === 'reconnect' ||
event === 'attemptReconnect' ||
'reconnectFailed'
) {
// Remove server instance from accounting
if(serverAccounting && ['close', 'timeout', 'error', 'parseError', 'reconnectFailed'].indexOf(event) != -1) {
if (
serverAccounting &&
['close', 'timeout', 'error', 'parseError', 'reconnectFailed'].indexOf(event) !== -1
) {
// Emit toplogy opening event if not in topology
if(!self.s.inTopology) {
if (!self.s.inTopology) {
self.emit('topologyOpening', { topologyId: self.id });

@@ -331,3 +478,7 @@ }

sdam.emitServerDescriptionChanged(self, {
address: self.name, arbiters: [], hosts: [], passives: [], type: 'Unknown'
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: 'Unknown'
});

@@ -337,6 +488,6 @@ }

// Reconnect failed return error
if(event == 'reconnectFailed') {
if (event === 'reconnectFailed') {
self.emit('reconnectFailed', err);
// Emit error if any listeners
if(self.listeners('error').length > 0) {
if (self.listeners('error').length > 0) {
self.emit('error', err);

@@ -349,13 +500,26 @@ }

// On first connect fail
if(self.s.pool.state == 'disconnected' && self.initalConnect && ['close', 'timeout', 'error', 'parseError'].indexOf(event) != -1) {
self.initalConnect = false;
return self.emit('error', new MongoError(f('failed to connect to server [%s] on first connect [%s]', self.name, err)));
if (
self.s.pool.state === 'disconnected' &&
self.initialConnect &&
['close', 'timeout', 'error', 'parseError'].indexOf(event) !== -1
) {
self.initialConnect = false;
return self.emit(
'error',
new MongoNetworkError(
f('failed to connect to server [%s] on first connect [%s]', self.name, err)
)
);
}
// Reconnect event, emit the server
if(event == 'reconnect') {
if (event === 'reconnect') {
// Reconnecting emits a server description changed event going from unknown to the
// current server type.
sdam.emitServerDescriptionChanged(self, {
address: self.name, arbiters: [], hosts: [], passives: [], type: sdam.getTopologyType(self)
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: sdam.getTopologyType(self)
});

@@ -368,4 +532,4 @@ return self.emit(event, self);

}
}
}
};
};

@@ -382,11 +546,11 @@ /**

// Set the connections
if(serverAccounting) servers[this.id] = this;
if (serverAccounting) servers[this.id] = this;
// Do not allow connect to be called on anything that's not disconnected
if(self.s.pool && !self.s.pool.isDisconnected() && !self.s.pool.isDestroyed()) {
throw MongoError.create(f('server instance in invalid state %s', self.s.pool.state));
if (self.s.pool && !self.s.pool.isDisconnected() && !self.s.pool.isDestroyed()) {
throw new MongoError(f('server instance in invalid state %s', self.s.pool.state));
}
// Create a pool
self.s.pool = new Pool(assign(self.s.options, options, {bson: this.s.bson}));
self.s.pool = new Pool(this, assign(self.s.options, options, { bson: this.s.bson }));

@@ -403,3 +567,3 @@ // Set up listeners

// Emit toplogy opening event if not in topology
if(!self.s.inTopology) {
if (!self.s.inTopology) {
this.emit('topologyOpening', { topologyId: self.id });

@@ -410,3 +574,3 @@ }

self.emit('serverOpening', {
topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id,
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
address: self.name

@@ -416,3 +580,3 @@ });

// Connect with optional auth settings
if(options.auth) {
if (options.auth) {
self.s.pool.connect.apply(self.s.pool, options.auth);

@@ -422,3 +586,3 @@ } else {

}
}
};

@@ -429,3 +593,3 @@ /**

* @return {object}
*/
*/
Server.prototype.getDescription = function() {

@@ -435,12 +599,12 @@ var ismaster = this.ismaster || {};

type: sdam.getTopologyType(this),
address: this.name,
address: this.name
};
// Add fields if available
if(ismaster.hosts) description.hosts = ismaster.hosts;
if(ismaster.arbiters) description.arbiters = ismaster.arbiters;
if(ismaster.passives) description.passives = ismaster.passives;
if(ismaster.setName) description.setName = ismaster.setName;
if (ismaster.hosts) description.hosts = ismaster.hosts;
if (ismaster.arbiters) description.arbiters = ismaster.arbiters;
if (ismaster.passives) description.passives = ismaster.passives;
if (ismaster.setName) description.setName = ismaster.setName;
return description;
}
};

@@ -454,3 +618,3 @@ /**

return this.ismaster;
}
};

@@ -463,3 +627,3 @@ /**

this.s.pool.unref();
}
};

@@ -472,5 +636,5 @@ /**

Server.prototype.isConnected = function() {
if(!this.s.pool) return false;
if (!this.s.pool) return false;
return this.s.pool.isConnected();
}
};

@@ -483,9 +647,9 @@ /**

Server.prototype.isDestroyed = function() {
if(!this.s.pool) return false;
if (!this.s.pool) return false;
return this.s.pool.isDestroyed();
}
};
function basicWriteValidations(self) {
if(!self.s.pool) return MongoError.create('server instance is not connected');
if(self.s.pool.isDestroyed()) return MongoError.create('server instance pool was destroyed');
if (!self.s.pool) return new MongoError('server instance is not connected');
if (self.s.pool.isDestroyed()) return new MongoError('server instance pool was destroyed');
}

@@ -496,4 +660,4 @@

if(options.readPreference && !(options.readPreference instanceof ReadPreference)) {
throw new Error("readPreference must be an instance of ReadPreference");
if (options.readPreference && !(options.readPreference instanceof ReadPreference)) {
throw new Error('readPreference must be an instance of ReadPreference');
}

@@ -512,2 +676,3 @@ }

* @param {Boolean} [options.fullResult=false] Return the full envelope instead of just the result document.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function

@@ -517,5 +682,8 @@ */

var self = this;
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
var result = basicReadValidations(self, options);
if(result) return callback(result);
if (result) return callback(result);

@@ -526,23 +694,23 @@ // Clone the options

// Debug log
if(self.s.logger.isDebug()) self.s.logger.debug(f('executing command [%s] against %s', JSON.stringify({
ns: ns, cmd: cmd, options: debugOptions(debugFields, options)
}), self.name));
if (self.s.logger.isDebug())
self.s.logger.debug(
f(
'executing command [%s] against %s',
JSON.stringify({
ns: ns,
cmd: cmd,
options: debugOptions(debugFields, options)
}),
self.name
)
);
// If we are not connected or have a disconnectHandler specified
if(disconnectHandler(self, 'command', ns, cmd, options, callback)) return;
if (disconnectHandler(self, 'command', ns, cmd, options, callback)) return;
// Check if we have collation support
if(this.ismaster && this.ismaster.maxWireVersion < 5 && cmd.collation) {
if (this.ismaster && this.ismaster.maxWireVersion < 5 && cmd.collation) {
return callback(new MongoError(f('server %s does not support collation', this.name)));
}
// Query options
var queryOptions = {
numberToSkip: 0,
numberToReturn: -1,
checkKeys: typeof options.checkKeys == 'boolean' ? options.checkKeys: false,
serializeFunctions: typeof options.serializeFunctions == 'boolean' ? options.serializeFunctions : false,
ignoreUndefined: typeof options.ignoreUndefined == 'boolean' ? options.ignoreUndefined : false
};
// Are we executing against a specific topology

@@ -557,11 +725,12 @@ var topology = options.topology || {};

var writeOptions = {
raw: typeof options.raw == 'boolean' ? options.raw : false,
promoteLongs: typeof options.promoteLongs == 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues == 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers == 'boolean' ? options.promoteBuffers : false,
raw: typeof options.raw === 'boolean' ? options.raw : false,
promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
command: true,
monitoring: typeof options.monitoring == 'boolean' ? options.monitoring : false,
fullResult: typeof options.fullResult == 'boolean' ? options.fullResult : false,
monitoring: typeof options.monitoring === 'boolean' ? options.monitoring : false,
fullResult: typeof options.fullResult === 'boolean' ? options.fullResult : false,
requestId: query.requestId,
socketTimeout: typeof options.socketTimeout == 'number' ? options.socketTimeout : null,
socketTimeout: typeof options.socketTimeout === 'number' ? options.socketTimeout : null,
session: options.session || null
};

@@ -571,3 +740,3 @@

self.s.pool.write(query, writeOptions, callback);
}
};

@@ -583,2 +752,3 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function

@@ -588,8 +758,11 @@ */

var self = this;
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
var result = basicWriteValidations(self, options);
if(result) return callback(result);
if (result) return callback(result);
// If we are not connected or have a disconnectHandler specified
if(disconnectHandler(self, 'insert', ns, ops, options, callback)) return;
if (disconnectHandler(self, 'insert', ns, ops, options, callback)) return;

@@ -600,4 +773,12 @@ // Setup the docs as an array

// Execute write
return self.wireProtocolHandler.insert(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
}
return self.wireProtocolHandler.insert(
self.s.pool,
self.ismaster,
ns,
self.s.bson,
ops,
options,
callback
);
};

@@ -613,2 +794,3 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function

@@ -618,11 +800,14 @@ */

var self = this;
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
var result = basicWriteValidations(self, options);
if(result) return callback(result);
if (result) return callback(result);
// If we are not connected or have a disconnectHandler specified
if(disconnectHandler(self, 'update', ns, ops, options, callback)) return;
if (disconnectHandler(self, 'update', ns, ops, options, callback)) return;
// Check if we have collation support
if(this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
if (this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
return callback(new MongoError(f('server %s does not support collation', this.name)));

@@ -634,4 +819,12 @@ }

// Execute write
return self.wireProtocolHandler.update(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
}
return self.wireProtocolHandler.update(
self.s.pool,
self.ismaster,
ns,
self.s.bson,
ops,
options,
callback
);
};

@@ -647,2 +840,3 @@ /**

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {opResultCallback} callback A callback function

@@ -652,11 +846,14 @@ */

var self = this;
if(typeof options == 'function') callback = options, options = {}, options = options || {};
if (typeof options === 'function') {
(callback = options), (options = {}), (options = options || {});
}
var result = basicWriteValidations(self, options);
if(result) return callback(result);
if (result) return callback(result);
// If we are not connected or have a disconnectHandler specified
if(disconnectHandler(self, 'remove', ns, ops, options, callback)) return;
if (disconnectHandler(self, 'remove', ns, ops, options, callback)) return;
// Check if we have collation support
if(this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
if (this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
return callback(new MongoError(f('server %s does not support collation', this.name)));

@@ -668,4 +865,12 @@ }

// Execute write
return self.wireProtocolHandler.remove(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
}
return self.wireProtocolHandler.remove(
self.s.pool,
self.ismaster,
ns,
self.s.bson,
ops,
options,
callback
);
};

@@ -676,3 +881,4 @@ /**

* @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
* @param {{object}|{Long}} cmd Can be either a command returning a cursor or a cursorId
* @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
* @param {object} [options] Options for the cursor
* @param {object} [options.batchSize=0] Batchsize for the operation

@@ -683,12 +889,16 @@ * @param {array} [options.documents=[]] Initial documents list for cursor

* @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
* @param {opResultCallback} callback A callback function
* @param {ClientSession} [options.session=null] Session to use for the operation
* @param {object} [options.topology] The internal topology of the created cursor
* @returns {Cursor}
*/
Server.prototype.cursor = function(ns, cmd, cursorOptions) {
var s = this.s;
cursorOptions = cursorOptions || {};
Server.prototype.cursor = function(ns, cmd, options) {
options = options || {};
const topology = options.topology || this;
// Set up final cursor type
var FinalCursor = cursorOptions.cursorFactory || s.Cursor;
var FinalCursor = options.cursorFactory || this.s.Cursor;
// Return the cursor
return new FinalCursor(s.bson, ns, cmd, cursorOptions, this, s.options);
}
return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
};

@@ -703,3 +913,3 @@ /**

this.s.pool.logout(dbName, callback);
}
};

@@ -719,5 +929,5 @@ /**

// protocol max version. If it's >= 3 then scram-sha1 otherwise mongodb-cr
if(mechanism == 'default' && self.ismaster && self.ismaster.maxWireVersion >= 3) {
if (mechanism === 'default' && self.ismaster && self.ismaster.maxWireVersion >= 3) {
mechanism = 'scram-sha-1';
} else if(mechanism == 'default') {
} else if (mechanism === 'default') {
mechanism = 'mongocr';

@@ -734,3 +944,3 @@ }

// If we are not connected or have a disconnectHandler specified
if(disconnectHandler(self, 'auth', db, args, {}, callback)) {
if (disconnectHandler(self, 'auth', db, args, {}, callback)) {
return;

@@ -740,3 +950,3 @@ }

// Do not authenticate if we are an arbiter
if(this.lastIsMaster() && this.lastIsMaster().arbiterOnly) {
if (this.lastIsMaster() && this.lastIsMaster().arbiterOnly) {
return callback(null, true);

@@ -747,3 +957,3 @@ }

self.s.pool.auth.apply(self.s.pool, args);
}
};

@@ -757,6 +967,6 @@ /**

Server.prototype.equals = function(server) {
if(typeof server == 'string') return this.name.toLowerCase() == server.toLowerCase();
if(server.name) return this.name.toLowerCase() == server.name.toLowerCase();
if (typeof server === 'string') return this.name.toLowerCase() === server.toLowerCase();
if (server.name) return this.name.toLowerCase() === server.name.toLowerCase();
return false;
}
};

@@ -770,3 +980,3 @@ /**

return this.s.pool.allConnections();
}
};

@@ -780,3 +990,3 @@ /**

return this;
}
};

@@ -790,3 +1000,3 @@ /**

return this.s.pool.get();
}
};

@@ -807,6 +1017,6 @@ var listeners = ['close', 'error', 'timeout', 'parseError', 'connect'];

// Set the connections
if(serverAccounting) delete servers[this.id];
if (serverAccounting) delete servers[this.id];
// Destroy the monitoring process if any
if(this.monitoringProcessId) {
if (this.monitoringProcessId) {
clearTimeout(this.monitoringProcessId);

@@ -816,6 +1026,6 @@ }

// No pool, return
if(!self.s.pool) return;
if (!self.s.pool) return;
// Emit close event
if(options.emitClose) {
if (options.emitClose) {
self.emit('close', self);

@@ -825,3 +1035,3 @@ }

// Emit destroy event
if(options.emitDestroy) {
if (options.emitDestroy) {
self.emit('destroy', self);

@@ -836,12 +1046,14 @@ }

// Emit opening server event
if(self.listeners('serverClosed').length > 0) self.emit('serverClosed', {
topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name
});
if (self.listeners('serverClosed').length > 0)
self.emit('serverClosed', {
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
address: self.name
});
// Emit toplogy opening event if not in topology
if(self.listeners('topologyClosed').length > 0 && !self.s.inTopology) {
if (self.listeners('topologyClosed').length > 0 && !self.s.inTopology) {
self.emit('topologyClosed', { topologyId: self.id });
}
if(self.s.logger.isDebug()) {
if (self.s.logger.isDebug()) {
self.s.logger.debug(f('destroy called on server %s', self.name));

@@ -852,3 +1064,3 @@ }

this.s.pool.destroy(options.force);
}
};

@@ -855,0 +1067,0 @@ /**

@@ -1,6 +0,10 @@

"use strict"
'use strict';
var os = require('os'),
f = require('util').format;
const os = require('os'),
f = require('util').format,
ReadPreference = require('./read_preference'),
retrieveBSON = require('../connection/utils').retrieveBSON;
const BSON = retrieveBSON();
/**

@@ -11,3 +15,3 @@ * Emit event if it exists

function emitSDAMEvent(self, event, description) {
if(self.listeners(event).length > 0) {
if (self.listeners(event).length > 0) {
self.emit(event, description);

@@ -27,19 +31,21 @@ }

// Build default client information
var clientInfo = options.clientInfo ? clone(options.clientInfo) : {
driver: {
name: "nodejs-core",
version: driverVersion
},
os: {
type: type,
name: name,
architecture: architecture,
version: release
}
}
var clientInfo = options.clientInfo
? clone(options.clientInfo)
: {
driver: {
name: 'nodejs-core',
version: driverVersion
},
os: {
type: type,
name: name,
architecture: architecture,
version: release
}
};
// Is platform specified
if(clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') == -1) {
if (clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') === -1) {
clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
} else if(!clientInfo.platform){
} else if (!clientInfo.platform) {
clientInfo.platform = nodejsversion;

@@ -49,3 +55,3 @@ }

// Do we have an application specific string
if(options.appname) {
if (options.appname) {
// Cut at 128 bytes

@@ -62,2 +68,17 @@ var buffer = new Buffer(options.appname);

function createCompressionInfo(options) {
if (!options.compression || !options.compression.compressors) {
return [];
}
// Check that all supplied compressors are valid
options.compression.compressors.forEach(function(compressor) {
if (compressor !== 'snappy' && compressor !== 'zlib') {
throw new Error('compressors must be at least one of snappy or zlib');
}
});
return options.compression.compressors;
}
function clone(object) {

@@ -68,17 +89,21 @@ return JSON.parse(JSON.stringify(object));

var getPreviousDescription = function(self) {
if(!self.s.serverDescription) {
if (!self.s.serverDescription) {
self.s.serverDescription = {
address: self.name,
arbiters: [], hosts: [], passives: [], type: 'Unknown'
}
arbiters: [],
hosts: [],
passives: [],
type: 'Unknown'
};
}
return self.s.serverDescription;
}
};
var emitServerDescriptionChanged = function(self, description) {
if(self.listeners('serverDescriptionChanged').length > 0) {
if (self.listeners('serverDescriptionChanged').length > 0) {
// Emit the server description changed events
self.emit('serverDescriptionChanged', {
topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
address: self.name,
previousDescription: getPreviousDescription(self),

@@ -90,22 +115,29 @@ newDescription: description

}
}
};
var getPreviousTopologyDescription = function(self) {
if(!self.s.topologyDescription) {
if (!self.s.topologyDescription) {
self.s.topologyDescription = {
topologyType: 'Unknown',
servers: [{
address: self.name, arbiters: [], hosts: [], passives: [], type: 'Unknown'
}]
}
servers: [
{
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: 'Unknown'
}
]
};
}
return self.s.topologyDescription;
}
};
var emitTopologyDescriptionChanged = function(self, description) {
if(self.listeners('topologyDescriptionChanged').length > 0) {
if (self.listeners('topologyDescriptionChanged').length > 0) {
// Emit the server description changed events
self.emit('topologyDescriptionChanged', {
topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
address: self.name,
previousDescription: getPreviousTopologyDescription(self),

@@ -117,3 +149,3 @@ newDescription: description

}
}
};

@@ -123,23 +155,23 @@ var changedIsMaster = function(self, currentIsmaster, ismaster) {

var newType = getTopologyType(self, ismaster);
if(newType != currentType) return true;
if (newType !== currentType) return true;
return false;
}
};
var getTopologyType = function(self, ismaster) {
if(!ismaster) {
if (!ismaster) {
ismaster = self.ismaster;
}
if(!ismaster) return 'Unknown';
if(ismaster.ismaster && ismaster.msg == 'isdbgrid') return 'Mongos';
if(ismaster.ismaster && !ismaster.hosts) return 'Standalone';
if(ismaster.ismaster) return 'RSPrimary';
if(ismaster.secondary) return 'RSSecondary';
if(ismaster.arbiterOnly) return 'RSArbiter';
if (!ismaster) return 'Unknown';
if (ismaster.ismaster && ismaster.msg === 'isdbgrid') return 'Mongos';
if (ismaster.ismaster && !ismaster.hosts) return 'Standalone';
if (ismaster.ismaster) return 'RSPrimary';
if (ismaster.secondary) return 'RSSecondary';
if (ismaster.arbiterOnly) return 'RSArbiter';
return 'Unknown';
}
};
var inquireServerState = function(self) {
return function(callback) {
if(self.s.state == 'destroyed') return;
if (self.s.state === 'destroyed') return;
// Record response time

@@ -152,4 +184,4 @@ var start = new Date().getTime();

// Attempt to execute ismaster command
self.command('admin.$cmd', { ismaster:true }, { monitoring:true }, function(err, r) {
if(!err) {
self.command('admin.$cmd', { ismaster: true }, { monitoring: true }, function(err, r) {
if (!err) {
// Legacy event sender

@@ -162,9 +194,17 @@ self.emit('ismaster', r, self);

// Server heart beat event
emitSDAMEvent(self, 'serverHeartbeatSucceeded', { durationMS: latencyMS, reply: r.result, connectionId: self.name });
emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
durationMS: latencyMS,
reply: r.result,
connectionId: self.name
});
// Did the server change
if(changedIsMaster(self, self.s.ismaster, r.result)) {
if (changedIsMaster(self, self.s.ismaster, r.result)) {
// Emit server description changed if something listening
emitServerDescriptionChanged(self, {
address: self.name, arbiters: [], hosts: [], passives: [], type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
address: self.name,
arbiters: [],
hosts: [],
passives: [],
type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
});

@@ -179,7 +219,11 @@ }

} else {
emitSDAMEvent(self, 'serverHeartbeatFailed', { durationMS: latencyMS, failure: err, connectionId: self.name });
emitSDAMEvent(self, 'serverHeartbeatFailed', {
durationMS: latencyMS,
failure: err,
connectionId: self.name
});
}
// Peforming an ismaster monitoring callback operation
if(typeof callback == 'function') {
if (typeof callback === 'function') {
return callback(err, r);

@@ -192,3 +236,3 @@ }

};
}
};

@@ -199,7 +243,7 @@ //

var opts = {};
for(var name in options) {
for (var name in options) {
opts[name] = options[name];
}
return opts;
}
};

@@ -209,3 +253,3 @@ function Interval(fn, time) {

this.start = function () {
this.start = function() {
if (!this.isRunning()) {

@@ -218,3 +262,3 @@ timer = setInterval(fn, time);

this.stop = function () {
this.stop = function() {
clearInterval(timer);

@@ -225,3 +269,3 @@ timer = false;

this.isRunning = function () {
this.isRunning = function() {
return timer !== false;

@@ -234,3 +278,3 @@ };

this.start = function () {
this.start = function() {
if (!this.isRunning()) {

@@ -242,3 +286,3 @@ timer = setTimeout(fn, time);

this.stop = function () {
this.stop = function() {
clearTimeout(timer);

@@ -249,4 +293,4 @@ timer = false;

this.isRunning = function () {
if(timer && timer._called) return false;
this.isRunning = function() {
if (timer && timer._called) return false;
return timer !== false;

@@ -260,6 +304,6 @@ };

servers: []
}
};
// Previous entry
if(!previous) {
if (!previous) {
previous = { servers: [] };

@@ -269,14 +313,13 @@ }

// Check if we have any previous servers missing in the current ones
for(var i = 0; i < previous.servers.length; i++) {
for (var i = 0; i < previous.servers.length; i++) {
var found = false;
for(var j = 0; j < current.servers.length; j++) {
if(current.servers[j].address.toLowerCase()
=== previous.servers[i].address.toLowerCase()) {
found = true;
break;
}
for (var j = 0; j < current.servers.length; j++) {
if (current.servers[j].address.toLowerCase() === previous.servers[i].address.toLowerCase()) {
found = true;
break;
}
}
if(!found) {
if (!found) {
// Add to the diff

@@ -286,3 +329,3 @@ diff.servers.push({

from: previous.servers[i].type,
to: 'Unknown',
to: 'Unknown'
});

@@ -293,9 +336,8 @@ }

// Check if there are any severs that don't exist
for(var j = 0; j < current.servers.length; j++) {
var found = false;
for (j = 0; j < current.servers.length; j++) {
found = false;
// Go over all the previous servers
for(var i = 0; i < previous.servers.length; i++) {
if(previous.servers[i].address.toLowerCase()
=== current.servers[j].address.toLowerCase()) {
for (i = 0; i < previous.servers.length; i++) {
if (previous.servers[i].address.toLowerCase() === current.servers[j].address.toLowerCase()) {
found = true;

@@ -307,7 +349,7 @@ break;

// Add the server to the diff
if(!found) {
if (!found) {
diff.servers.push({
address: current.servers[j].address,
from: 'Unknown',
to: current.servers[j].type,
to: current.servers[j].type
});

@@ -318,13 +360,13 @@ }

// Got through all the servers
for(var i = 0; i < previous.servers.length; i++) {
for (i = 0; i < previous.servers.length; i++) {
var prevServer = previous.servers[i];
// Go through all current servers
for(var j = 0; j < current.servers.length; j++) {
for (j = 0; j < current.servers.length; j++) {
var currServer = current.servers[j];
// Matching server
if(prevServer.address.toLowerCase() === currServer.address.toLowerCase()) {
if (prevServer.address.toLowerCase() === currServer.address.toLowerCase()) {
// We had a change in state
if(prevServer.type != currServer.type) {
if (prevServer.type !== currServer.type) {
diff.servers.push({

@@ -344,3 +386,82 @@ address: prevServer.address,

module.exports.inquireServerState = inquireServerState
/**
* Shared function to determine clusterTime for a given topology
*
* @param {*} topology
* @param {*} clusterTime
*/
function resolveClusterTime(topology, $clusterTime) {
if (topology.clusterTime == null) {
topology.clusterTime = $clusterTime;
} else {
if ($clusterTime.clusterTime.greaterThan(topology.clusterTime.clusterTime)) {
topology.clusterTime = $clusterTime;
}
}
}
// NOTE: this is a temporary move until the topologies can be more formally refactored
// to share code.
const SessionMixins = {
endSessions: function(sessions, callback) {
if (this.isConnected()) {
if (typeof callback === 'function') callback();
return;
}
if (!Array.isArray(sessions)) {
sessions = [sessions];
}
// TODO:
// When connected to a sharded cluster the endSessions command
// can be sent to any mongos. When connected to a replica set the
// endSessions command MUST be sent to the primary if the primary
// is available, otherwise it MUST be sent to any available secondary.
// Is it enough to use: ReadPreference.primaryPreferred ?
this.command(
'admin.$cmd',
{ endSessions: sessions.map(s => s.id) },
{ readPreference: ReadPreference.primaryPreferred },
() => {
// intentionally ignored, per spec
if (typeof callback === 'function') callback();
}
);
}
};
const RETRYABLE_WIRE_VERSION = 6;
/**
* Determines whether the provided topology supports retryable writes
*
* @param {Mongos|Replset} topology
*/
const isRetryableWritesSupported = function(topology) {
const maxWireVersion = topology.lastIsMaster().maxWireVersion;
if (maxWireVersion < RETRYABLE_WIRE_VERSION) {
return false;
}
if (!topology.logicalSessionTimeoutMinutes) {
return false;
}
return true;
};
/**
* Increment the transaction number on the ServerSession contained by the provided ClientSession
*
* @param {ClientSession} session
*/
const getNextTransactionNumber = function(session) {
session.serverSession.txnNumber++;
return BSON.Long.fromNumber(session.serverSession.txnNumber);
};
module.exports.SessionMixins = SessionMixins;
module.exports.resolveClusterTime = resolveClusterTime;
module.exports.inquireServerState = inquireServerState;
module.exports.getTopologyType = getTopologyType;

@@ -351,2 +472,3 @@ module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;

module.exports.createClientInfo = createClientInfo;
module.exports.createCompressionInfo = createCompressionInfo;
module.exports.clone = clone;

@@ -356,1 +478,3 @@ module.exports.diff = diff;

module.exports.Timeout = Timeout;
module.exports.isRetryableWritesSupported = isRetryableWritesSupported;
module.exports.getNextTransactionNumber = getNextTransactionNumber;

@@ -0,1 +1,5 @@

'use strict';
const crypto = require('crypto');
/**

@@ -5,29 +9,39 @@ * Copy the values of all enumerable own properties from one or more

*/
var assign = Object.assign ? Object.assign : function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
const assign = Object.assign
? Object.assign
: function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
}
return to;
}
return to;
};
const uuidV4 = () => {
const result = crypto.randomBytes(16);
result[6] = (result[6] & 0x0f) | 0x40;
result[8] = (result[8] & 0x3f) | 0x80;
return result;
};
module.exports = {
assign: assign
assign: assign,
uuidV4: uuidV4
};

@@ -1,11 +0,11 @@

"use strict";
'use strict';
var copy = require('../connection/utils').copy
, retrieveBSON = require('../connection/utils').retrieveBSON
, KillCursor = require('../connection/commands').KillCursor
, GetMore = require('../connection/commands').GetMore
, Query = require('../connection/commands').Query
, f = require('util').format
, MongoError = require('../error')
, getReadPreference = require('./shared').getReadPreference;
var copy = require('../connection/utils').copy,
retrieveBSON = require('../connection/utils').retrieveBSON,
KillCursor = require('../connection/commands').KillCursor,
GetMore = require('../connection/commands').GetMore,
Query = require('../connection/commands').Query,
f = require('util').format,
MongoError = require('../error').MongoError,
getReadPreference = require('./shared').getReadPreference;

@@ -15,3 +15,3 @@ var BSON = retrieveBSON(),

var WireProtocol = function() {}
var WireProtocol = function() {};

@@ -21,4 +21,4 @@ //

var executeWrite = function(pool, bson, type, opsField, ns, ops, options, callback) {
if(ops.length == 0) throw new MongoError("insert must contain at least one document");
if(typeof options == 'function') {
if (ops.length === 0) throw new MongoError('insert must contain at least one document');
if (typeof options === 'function') {
callback = options;

@@ -30,6 +30,6 @@ options = {};

// Split the ns up to get db and collection
var p = ns.split(".");
var p = ns.split('.');
var d = p.shift();
// Options
var ordered = typeof options.ordered == 'boolean' ? options.ordered : true;
var ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
var writeConcern = options.writeConcern;

@@ -44,3 +44,3 @@

// Did we specify a write concern
if(writeConcern && Object.keys(writeConcern).length > 0) {
if (writeConcern && Object.keys(writeConcern).length > 0) {
writeCommand.writeConcern = writeConcern;

@@ -50,3 +50,3 @@ }

// Do we have bypassDocumentValidation set, then enable it on the write command
if(typeof options.bypassDocumentValidation == 'boolean') {
if (typeof options.bypassDocumentValidation === 'boolean') {
writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;

@@ -57,19 +57,20 @@ }

var opts = { command: true };
var queryOptions = { checkKeys : false, numberToSkip: 0, numberToReturn: 1 };
if(type == 'insert') queryOptions.checkKeys = true;
if(typeof options.checkKeys == 'boolean') queryOptions.checkKeys = options.checkKeys;
if (typeof options.session !== 'undefined') opts.session = options.session;
var queryOptions = { checkKeys: false, numberToSkip: 0, numberToReturn: 1 };
if (type === 'insert') queryOptions.checkKeys = false;
if (typeof options.checkKeys === 'boolean') queryOptions.checkKeys = options.checkKeys;
// Ensure we support serialization of functions
if(options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
if (options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
// Do not serialize the undefined fields
if(options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
if (options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
try {
// Create write command
var cmd = new Query(bson, f("%s.$cmd", d), writeCommand, queryOptions);
var cmd = new Query(bson, f('%s.$cmd', d), writeCommand, queryOptions);
// Execute command
pool.write(cmd, opts, callback);
} catch(err) {
} catch (err) {
callback(err);
}
}
};

@@ -82,33 +83,56 @@ //

executeWrite(pool, bson, 'insert', 'documents', ns, ops, options, callback);
}
};
WireProtocol.prototype.update = function(pool, ismaster, ns, bson, ops, options, callback) {
executeWrite(pool, bson, 'update', 'updates', ns, ops, options, callback);
}
};
WireProtocol.prototype.remove = function(pool, ismaster, ns, bson, ops, options, callback) {
executeWrite(pool, bson, 'delete', 'deletes', ns, ops, options, callback);
}
};
WireProtocol.prototype.killCursor = function(bson, ns, cursorId, pool, callback) {
WireProtocol.prototype.killCursor = function(bson, ns, cursorState, pool, callback) {
var cursorId = cursorState.cursorId;
// Create a kill cursor command
var killCursor = new KillCursor(bson, [cursorId]);
// Build killCursor options
const options = {
immediateRelease: true,
noResponse: true
};
if (typeof cursorState.session === 'object') {
options.session = cursorState.session;
}
// Execute the kill cursor command
if(pool && pool.isConnected()) {
pool.write(killCursor, {
immediateRelease:true, noResponse: true
});
if (pool && pool.isConnected()) {
try {
pool.write(killCursor, options, callback);
} catch (err) {
callback(err, null);
}
}
// Callback
if(typeof callback == 'function') callback(null, null);
}
if (typeof callback === 'function') callback(null, null);
};
WireProtocol.prototype.getMore = function(bson, ns, cursorState, batchSize, raw, connection, options, callback) {
WireProtocol.prototype.getMore = function(
bson,
ns,
cursorState,
batchSize,
raw,
connection,
options,
callback
) {
// Create getMore command
var getMore = new GetMore(bson, ns, cursorState.cursorId, {numberToReturn: batchSize});
var getMore = new GetMore(bson, ns, cursorState.cursorId, { numberToReturn: batchSize });
// Query callback
var queryCallback = function(err, result) {
if(err) return callback(err);
if (err) return callback(err);
// Get the raw message

@@ -118,10 +142,8 @@ var r = result.message;

// If we have a timed out query or a cursor that was killed
if((r.responseFlags & (1 << 0)) != 0) {
return callback(new MongoError("cursor does not exist, was killed or timed out"), null);
if ((r.responseFlags & (1 << 0)) !== 0) {
return callback(new MongoError('cursor does not exist, was killed or timed out'), null);
}
// Ensure we have a Long valie cursor id
var cursorId = typeof r.cursorId == 'number'
? Long.fromNumber(r.cursorId)
: r.cursorId;
var cursorId = typeof r.cursorId === 'number' ? Long.fromNumber(r.cursorId) : r.cursorId;

@@ -134,3 +156,3 @@ // Set all the values

callback(null, null, r.connection);
}
};

@@ -141,3 +163,3 @@ // Contains any query options

// If we have a raw query decorate the function
if(raw) {
if (raw) {
queryOptions.raw = raw;

@@ -147,30 +169,34 @@ }

// Check if we need to promote longs
if(typeof cursorState.promoteLongs == 'boolean') {
if (typeof cursorState.promoteLongs === 'boolean') {
queryOptions.promoteLongs = cursorState.promoteLongs;
}
if(typeof cursorState.promoteValues == 'boolean') {
if (typeof cursorState.promoteValues === 'boolean') {
queryOptions.promoteValues = cursorState.promoteValues;
}
if(typeof cursorState.promoteBuffers == 'boolean') {
if (typeof cursorState.promoteBuffers === 'boolean') {
queryOptions.promoteBuffers = cursorState.promoteBuffers;
}
if (typeof cursorState.session === 'object') {
queryOptions.session = cursorState.session;
}
// Write out the getMore command
connection.write(getMore, queryOptions, queryCallback);
}
};
WireProtocol.prototype.command = function(bson, ns, cmd, cursorState, topology, options) {
// Establish type of command
if(cmd.find) {
return setupClassicFind(bson, ns, cmd, cursorState, topology, options)
} else if(cursorState.cursorId != null) {
if (cmd.find) {
return setupClassicFind(bson, ns, cmd, cursorState, topology, options);
} else if (cursorState.cursorId != null) {
return;
} else if(cmd) {
} else if (cmd) {
return setupCommand(bson, ns, cmd, cursorState, topology, options);
} else {
throw new MongoError(f("command %s does not return a cursor", JSON.stringify(cmd)));
throw new MongoError(f('command %s does not return a cursor', JSON.stringify(cmd)));
}
}
};

@@ -189,5 +215,9 @@ //

// Unpack the limit and batchSize values
if(cursorState.limit == 0) {
if (cursorState.limit === 0) {
numberToReturn = cursorState.batchSize;
} else if(cursorState.limit < 0 || cursorState.limit < cursorState.batchSize || (cursorState.limit > 0 && cursorState.batchSize == 0)) {
} else if (
cursorState.limit < 0 ||
cursorState.limit < cursorState.batchSize ||
(cursorState.limit > 0 && cursorState.batchSize === 0)
) {
numberToReturn = cursorState.limit;

@@ -205,3 +235,3 @@ } else {

// We have a Mongos topology, check if we need to add a readPreference
if(topology.type == 'mongos' && readPreference) {
if (topology.type === 'mongos' && readPreference) {
findCmd['$readPreference'] = readPreference.toJSON();

@@ -212,16 +242,16 @@ usesSpecialModifier = true;

// Add special modifiers to the query
if(cmd.sort) findCmd['orderby'] = cmd.sort, usesSpecialModifier = true;
if(cmd.hint) findCmd['$hint'] = cmd.hint, usesSpecialModifier = true;
if(cmd.snapshot) findCmd['$snapshot'] = cmd.snapshot, usesSpecialModifier = true;
if(cmd.returnKey) findCmd['$returnKey'] = cmd.returnKey, usesSpecialModifier = true;
if(cmd.maxScan) findCmd['$maxScan'] = cmd.maxScan, usesSpecialModifier = true;
if(cmd.min) findCmd['$min'] = cmd.min, usesSpecialModifier = true;
if(cmd.max) findCmd['$max'] = cmd.max, usesSpecialModifier = true;
if(cmd.showDiskLoc) findCmd['$showDiskLoc'] = cmd.showDiskLoc, usesSpecialModifier = true;
if(cmd.comment) findCmd['$comment'] = cmd.comment, usesSpecialModifier = true;
if(cmd.maxTimeMS) findCmd['$maxTimeMS'] = cmd.maxTimeMS, usesSpecialModifier = true;
if (cmd.sort) (findCmd['orderby'] = cmd.sort), (usesSpecialModifier = true);
if (cmd.hint) (findCmd['$hint'] = cmd.hint), (usesSpecialModifier = true);
if (cmd.snapshot) (findCmd['$snapshot'] = cmd.snapshot), (usesSpecialModifier = true);
if (cmd.returnKey) (findCmd['$returnKey'] = cmd.returnKey), (usesSpecialModifier = true);
if (cmd.maxScan) (findCmd['$maxScan'] = cmd.maxScan), (usesSpecialModifier = true);
if (cmd.min) (findCmd['$min'] = cmd.min), (usesSpecialModifier = true);
if (cmd.max) (findCmd['$max'] = cmd.max), (usesSpecialModifier = true);
if (cmd.showDiskLoc) (findCmd['$showDiskLoc'] = cmd.showDiskLoc), (usesSpecialModifier = true);
if (cmd.comment) (findCmd['$comment'] = cmd.comment), (usesSpecialModifier = true);
if (cmd.maxTimeMS) (findCmd['$maxTimeMS'] = cmd.maxTimeMS), (usesSpecialModifier = true);
if(cmd.explain) {
// nToReturn must be 0 (match all) or negative (match N and close cursor)
// nToReturn > 0 will give explain results equivalent to limit(0)
if (cmd.explain) {
// nToReturn must be 0 (match all) or negative (match N and close cursor)
// nToReturn > 0 will give explain results equivalent to limit(0)
numberToReturn = -Math.abs(cmd.limit || 0);

@@ -233,3 +263,3 @@ usesSpecialModifier = true;

// If we have a special modifier
if(usesSpecialModifier) {
if (usesSpecialModifier) {
findCmd['$query'] = cmd.query;

@@ -241,8 +271,10 @@ } else {

// Throw on majority readConcern passed in
if(cmd.readConcern && cmd.readConcern.level != 'local') {
throw new MongoError(f('server find command does not support a readConcern level of %s', cmd.readConcern.level));
if (cmd.readConcern && cmd.readConcern.level !== 'local') {
throw new MongoError(
f('server find command does not support a readConcern level of %s', cmd.readConcern.level)
);
}
// Remove readConcern, ensure no failing commands
if(cmd.readConcern) {
if (cmd.readConcern) {
cmd = copy(cmd);

@@ -253,13 +285,15 @@ delete cmd['readConcern'];

// Serialize functions
var serializeFunctions = typeof options.serializeFunctions == 'boolean'
? options.serializeFunctions : false;
var ignoreUndefined = typeof options.ignoreUndefined == 'boolean'
? options.ignoreUndefined : false;
var serializeFunctions =
typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
var ignoreUndefined =
typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
// Build Query object
var query = new Query(bson, ns, findCmd, {
numberToSkip: numberToSkip, numberToReturn: numberToReturn
, checkKeys: false, returnFieldSelector: cmd.fields
, serializeFunctions: serializeFunctions
, ignoreUndefined: ignoreUndefined
numberToSkip: numberToSkip,
numberToReturn: numberToReturn,
checkKeys: false,
returnFieldSelector: cmd.fields,
serializeFunctions: serializeFunctions,
ignoreUndefined: ignoreUndefined
});

@@ -271,19 +305,19 @@

// Set up the option bits for wire protocol
if(typeof cmd.tailable == 'boolean') {
if (typeof cmd.tailable === 'boolean') {
query.tailable = cmd.tailable;
}
if(typeof cmd.oplogReplay == 'boolean') {
if (typeof cmd.oplogReplay === 'boolean') {
query.oplogReplay = cmd.oplogReplay;
}
if(typeof cmd.noCursorTimeout == 'boolean') {
if (typeof cmd.noCursorTimeout === 'boolean') {
query.noCursorTimeout = cmd.noCursorTimeout;
}
if(typeof cmd.awaitData == 'boolean') {
if (typeof cmd.awaitData === 'boolean') {
query.awaitData = cmd.awaitData;
}
if(typeof cmd.partial == 'boolean') {
if (typeof cmd.partial === 'boolean') {
query.partial = cmd.partial;

@@ -294,3 +328,3 @@ }

return query;
}
};

@@ -301,3 +335,3 @@ //

// Set empty options object
options = options || {}
options = options || {};
// Get the readPreference

@@ -308,3 +342,3 @@ var readPreference = getReadPreference(cmd, options);

var finalCmd = {};
for(var name in cmd) {
for (var name in cmd) {
finalCmd[name] = cmd[name];

@@ -317,23 +351,27 @@ }

// Serialize functions
var serializeFunctions = typeof options.serializeFunctions == 'boolean'
? options.serializeFunctions : false;
var serializeFunctions =
typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
var ignoreUndefined = typeof options.ignoreUndefined == 'boolean'
? options.ignoreUndefined : false;
var ignoreUndefined =
typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
// Throw on majority readConcern passed in
if(cmd.readConcern && cmd.readConcern.level != 'local') {
throw new MongoError(f('server %s command does not support a readConcern level of %s', JSON.stringify(cmd), cmd.readConcern.level));
if (cmd.readConcern && cmd.readConcern.level !== 'local') {
throw new MongoError(
f(
'server %s command does not support a readConcern level of %s',
JSON.stringify(cmd),
cmd.readConcern.level
)
);
}
// Remove readConcern, ensure no failing commands
if(cmd.readConcern) delete cmd['readConcern'];
if (cmd.readConcern) delete cmd['readConcern'];
// We have a Mongos topology, check if we need to add a readPreference
if(topology.type == 'mongos'
&& readPreference
&& readPreference.preference != 'primary') {
if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
finalCmd = {
'$query': finalCmd,
'$readPreference': readPreference.toJSON()
$query: finalCmd,
$readPreference: readPreference.toJSON()
};

@@ -344,5 +382,7 @@ }

var query = new Query(bson, f('%s.$cmd', parts.shift()), finalCmd, {
numberToSkip: 0, numberToReturn: -1
, checkKeys: false, serializeFunctions: serializeFunctions
, ignoreUndefined: ignoreUndefined
numberToSkip: 0,
numberToReturn: -1,
checkKeys: false,
serializeFunctions: serializeFunctions,
ignoreUndefined: ignoreUndefined
});

@@ -355,4 +395,4 @@

return query;
}
};
module.exports = WireProtocol;

@@ -1,8 +0,9 @@

"use strict";
'use strict';
var Query = require('../connection/commands').Query
, retrieveBSON = require('../connection/utils').retrieveBSON
, f = require('util').format
, MongoError = require('../error')
, getReadPreference = require('./shared').getReadPreference;
var Query = require('../connection/commands').Query,
retrieveBSON = require('../connection/utils').retrieveBSON,
f = require('util').format,
MongoError = require('../error').MongoError,
MongoNetworkError = require('../error').MongoNetworkError,
getReadPreference = require('./shared').getReadPreference;

@@ -14,3 +15,3 @@ var BSON = retrieveBSON(),

this.legacyWireProtocol = legacyWireProtocol;
}
};

@@ -20,4 +21,4 @@ //

var executeWrite = function(pool, bson, type, opsField, ns, ops, options, callback) {
if(ops.length == 0) throw new MongoError("insert must contain at least one document");
if(typeof options == 'function') {
if (ops.length === 0) throw new MongoError('insert must contain at least one document');
if (typeof options === 'function') {
callback = options;

@@ -29,6 +30,6 @@ options = {};

// Split the ns up to get db and collection
var p = ns.split(".");
var p = ns.split('.');
var d = p.shift();
// Options
var ordered = typeof options.ordered == 'boolean' ? options.ordered : true;
var ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
var writeConcern = options.writeConcern;

@@ -43,3 +44,3 @@

// Did we specify a write concern
if(writeConcern && Object.keys(writeConcern).length > 0) {
if (writeConcern && Object.keys(writeConcern).length > 0) {
writeCommand.writeConcern = writeConcern;

@@ -49,5 +50,5 @@ }

// If we have collation passed in
if(options.collation) {
for(var i = 0; i < writeCommand[opsField].length; i++) {
if(!writeCommand[opsField][i].collation) {
if (options.collation) {
for (var i = 0; i < writeCommand[opsField].length; i++) {
if (!writeCommand[opsField][i].collation) {
writeCommand[opsField][i].collation = options.collation;

@@ -59,26 +60,32 @@ }

// Do we have bypassDocumentValidation set, then enable it on the write command
if(typeof options.bypassDocumentValidation == 'boolean') {
if (typeof options.bypassDocumentValidation === 'boolean') {
writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
}
// optionally add a `txnNumber` if retryable writes are being attempted
if (typeof options.txnNumber !== 'undefined') {
writeCommand.txnNumber = options.txnNumber;
}
// Options object
var opts = { command: true };
var queryOptions = { checkKeys : false, numberToSkip: 0, numberToReturn: 1 };
if(type == 'insert') queryOptions.checkKeys = true;
if(typeof options.checkKeys == 'boolean') queryOptions.checkKeys = options.checkKeys;
if (typeof options.session !== 'undefined') opts.session = options.session;
var queryOptions = { checkKeys: false, numberToSkip: 0, numberToReturn: 1 };
if (type === 'insert') queryOptions.checkKeys = false;
if (typeof options.checkKeys === 'boolean') queryOptions.checkKeys = options.checkKeys;
// Ensure we support serialization of functions
if(options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
if (options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
// Do not serialize the undefined fields
if(options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
if (options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
try {
// Create write command
var cmd = new Query(bson, f("%s.$cmd", d), writeCommand, queryOptions);
var cmd = new Query(bson, f('%s.$cmd', d), writeCommand, queryOptions);
// Execute command
pool.write(cmd, opts, callback);
} catch(err) {
} catch (err) {
callback(err);
}
}
};

@@ -91,13 +98,13 @@ //

executeWrite(pool, bson, 'insert', 'documents', ns, ops, options, callback);
}
};
WireProtocol.prototype.update = function(pool, ismaster, ns, bson, ops, options, callback) {
executeWrite(pool, bson, 'update', 'updates', ns, ops, options, callback);
}
};
WireProtocol.prototype.remove = function(pool, ismaster, ns, bson, ops, options, callback) {
executeWrite(pool, bson, 'delete', 'deletes', ns, ops, options, callback);
}
};
WireProtocol.prototype.killCursor = function(bson, ns, cursorId, pool, callback) {
WireProtocol.prototype.killCursor = function(bson, ns, cursorState, pool, callback) {
// Build command namespace

@@ -107,12 +114,15 @@ var parts = ns.split(/\./);

var commandns = f('%s.$cmd', parts.shift());
// Create getMore command
const cursorId = cursorState.cursorId;
// Create killCursor command
var killcursorCmd = {
killCursors: parts.join('.'),
cursors: [cursorId]
}
};
// Build Query object
var query = new Query(bson, commandns, killcursorCmd, {
numberToSkip: 0, numberToReturn: -1
, checkKeys: false, returnFieldSelector: null
numberToSkip: 0,
numberToReturn: -1,
checkKeys: false,
returnFieldSelector: null
});

@@ -125,4 +135,4 @@

var killCursorCallback = function(err, result) {
if(err) {
if(typeof callback != 'function') return;
if (err) {
if (typeof callback !== 'function') return;
return callback(err);

@@ -134,27 +144,50 @@ }

// If we have a timed out query or a cursor that was killed
if((r.responseFlags & (1 << 0)) != 0) {
if(typeof callback != 'function') return;
return callback(new MongoError("cursor killed or timed out"), null);
if ((r.responseFlags & (1 << 0)) !== 0) {
if (typeof callback !== 'function') return;
return callback(new MongoNetworkError('cursor killed or timed out'), null);
}
if(!Array.isArray(r.documents) || r.documents.length == 0) {
if(typeof callback != 'function') return;
return callback(new MongoError(f('invalid killCursors result returned for cursor id %s', cursorId)));
if (!Array.isArray(r.documents) || r.documents.length === 0) {
if (typeof callback !== 'function') return;
return callback(
new MongoError(f('invalid killCursors result returned for cursor id %s', cursorId))
);
}
// Return the result
if(typeof callback == 'function') {
if (typeof callback === 'function') {
callback(null, r.documents[0]);
}
};
const options = { command: true };
if (typeof cursorState.session === 'object') {
options.session = cursorState.session;
}
// Execute the kill cursor command
if(pool && pool.isConnected()) {
pool.write(query, {
command: true
}, killCursorCallback);
if (pool && pool.isConnected()) {
try {
pool.write(query, options, killCursorCallback);
} catch (err) {
killCursorCallback(err, null);
}
return;
}
}
WireProtocol.prototype.getMore = function(bson, ns, cursorState, batchSize, raw, connection, options, callback) {
// Callback
if (typeof callback === 'function') callback(null, null);
};
WireProtocol.prototype.getMore = function(
bson,
ns,
cursorState,
batchSize,
raw,
connection,
options,
callback
) {
options = options || {};

@@ -171,6 +204,5 @@ // Build command namespace

batchSize: Math.abs(batchSize)
}
};
if(cursorState.cmd.tailable
&& typeof cursorState.cmd.maxAwaitTimeMS == 'number') {
if (cursorState.cmd.tailable && typeof cursorState.cmd.maxAwaitTimeMS === 'number') {
getMoreCmd.maxTimeMS = cursorState.cmd.maxAwaitTimeMS;

@@ -181,4 +213,6 @@ }

var query = new Query(bson, commandns, getMoreCmd, {
numberToSkip: 0, numberToReturn: -1
, checkKeys: false, returnFieldSelector: null
numberToSkip: 0,
numberToReturn: -1,
checkKeys: false,
returnFieldSelector: null
});

@@ -191,3 +225,3 @@

var queryCallback = function(err, result) {
if(err) return callback(err);
if (err) return callback(err);
// Get the raw message

@@ -197,8 +231,8 @@ var r = result.message;

// If we have a timed out query or a cursor that was killed
if((r.responseFlags & (1 << 0)) != 0) {
return callback(new MongoError("cursor killed or timed out"), null);
if ((r.responseFlags & (1 << 0)) !== 0) {
return callback(new MongoNetworkError('cursor killed or timed out'), null);
}
// Raw, return all the extracted documents
if(raw) {
if (raw) {
cursorState.documents = r.documents;

@@ -210,10 +244,11 @@ cursorState.cursorId = r.cursorId;

// We have an error detected
if(r.documents[0].ok == 0) {
return callback(MongoError.create(r.documents[0]));
if (r.documents[0].ok === 0) {
return callback(new MongoError(r.documents[0]));
}
// Ensure we have a Long valid cursor id
var cursorId = typeof r.documents[0].cursor.id == 'number'
? Long.fromNumber(r.documents[0].cursor.id)
: r.documents[0].cursor.id;
var cursorId =
typeof r.documents[0].cursor.id === 'number'
? Long.fromNumber(r.documents[0].cursor.id)
: r.documents[0].cursor.id;

@@ -226,3 +261,3 @@ // Set all the values

callback(null, r.documents[0], r.connection);
}
};

@@ -233,3 +268,3 @@ // Query options

// If we have a raw query decorate the function
if(raw) {
if (raw) {
queryOptions.raw = raw;

@@ -242,27 +277,32 @@ }

// Check if we need to promote longs
if(typeof cursorState.promoteLongs == 'boolean') {
if (typeof cursorState.promoteLongs === 'boolean') {
queryOptions.promoteLongs = cursorState.promoteLongs;
}
if(typeof cursorState.promoteValues == 'boolean') {
if (typeof cursorState.promoteValues === 'boolean') {
queryOptions.promoteValues = cursorState.promoteValues;
}
if(typeof cursorState.promoteBuffers == 'boolean') {
if (typeof cursorState.promoteBuffers === 'boolean') {
queryOptions.promoteBuffers = cursorState.promoteBuffers;
}
if (typeof cursorState.session === 'object') {
queryOptions.session = cursorState.session;
}
// Write out the getMore command
connection.write(query, queryOptions, queryCallback);
}
};
WireProtocol.prototype.command = function(bson, ns, cmd, cursorState, topology, options) {
options = options || {}
options = options || {};
// Check if this is a wire protocol command or not
var wireProtocolCommand = typeof options.wireProtocolCommand == 'boolean' ? options.wireProtocolCommand : true;
var wireProtocolCommand =
typeof options.wireProtocolCommand === 'boolean' ? options.wireProtocolCommand : true;
// Establish type of command
if(cmd.find && wireProtocolCommand) {
if (cmd.find && wireProtocolCommand) {
// Create the find command
var query = executeFindCommand(bson, ns, cmd, cursorState, topology, options)
var query = executeFindCommand(bson, ns, cmd, cursorState, topology, options);
// Mark the cmd as virtual

@@ -274,10 +314,10 @@ cmd.virtual = false;

return query;
} else if(cursorState.cursorId != null) {
} else if (cursorState.cursorId != null) {
return;
} else if(cmd) {
} else if (cmd) {
return setupCommand(bson, ns, cmd, cursorState, topology, options);
} else {
throw new MongoError(f("command %s does not return a cursor", JSON.stringify(cmd)));
throw new MongoError(f('command %s does not return a cursor', JSON.stringify(cmd)));
}
}
};

@@ -360,5 +400,5 @@ // // Command

// I we provided a filter
if(cmd.query) {
if (cmd.query) {
// Check if the user is passing in the $query parameter
if(cmd.query['$query']) {
if (cmd.query['$query']) {
findCmd.filter = cmd.query['$query'];

@@ -374,11 +414,11 @@ } else {

// Handle issue of sort being an Array
if(Array.isArray(sortValue)) {
if (Array.isArray(sortValue)) {
var sortObject = {};
if(sortValue.length > 0 && !Array.isArray(sortValue[0])) {
if (sortValue.length > 0 && !Array.isArray(sortValue[0])) {
var sortDirection = sortValue[1];
// Translate the sort order text
if(sortDirection == 'asc') {
if (sortDirection === 'asc') {
sortDirection = 1;
} else if(sortDirection == 'desc') {
} else if (sortDirection === 'desc') {
sortDirection = -1;

@@ -390,8 +430,8 @@ }

} else {
for(var i = 0; i < sortValue.length; i++) {
for (var i = 0; i < sortValue.length; i++) {
sortDirection = sortValue[i][1];
// Translate the sort order text
if(sortDirection == 'asc') {
if (sortDirection === 'asc') {
sortDirection = 1;
} else if(sortDirection == 'desc') {
} else if (sortDirection === 'desc') {
sortDirection = -1;

@@ -409,14 +449,14 @@ }

// Add sort to command
if(cmd.sort) findCmd.sort = sortValue;
if (cmd.sort) findCmd.sort = sortValue;
// Add a projection to the command
if(cmd.fields) findCmd.projection = cmd.fields;
if (cmd.fields) findCmd.projection = cmd.fields;
// Add a hint to the command
if(cmd.hint) findCmd.hint = cmd.hint;
if (cmd.hint) findCmd.hint = cmd.hint;
// Add a skip
if(cmd.skip) findCmd.skip = cmd.skip;
if (cmd.skip) findCmd.skip = cmd.skip;
// Add a limit
if(cmd.limit) findCmd.limit = cmd.limit;
if (cmd.limit) findCmd.limit = cmd.limit;
// Check if we wish to have a singleBatch
if(cmd.limit < 0) {
if (cmd.limit < 0) {
findCmd.limit = Math.abs(cmd.limit);

@@ -427,5 +467,5 @@ findCmd.singleBatch = true;

// Add a batchSize
if(typeof cmd.batchSize == 'number') {
if (typeof cmd.batchSize === 'number') {
if (cmd.batchSize < 0) {
if (cmd.limit != 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) {
if (cmd.limit !== 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) {
findCmd.limit = Math.abs(cmd.batchSize);

@@ -441,68 +481,66 @@ }

// If we have comment set
if(cmd.comment) findCmd.comment = cmd.comment;
if (cmd.comment) findCmd.comment = cmd.comment;
// If we have maxScan
if(cmd.maxScan) findCmd.maxScan = cmd.maxScan;
if (cmd.maxScan) findCmd.maxScan = cmd.maxScan;
// If we have maxTimeMS set
if(cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
// If we have min
if(cmd.min) findCmd.min = cmd.min;
if (cmd.min) findCmd.min = cmd.min;
// If we have max
if(cmd.max) findCmd.max = cmd.max;
if (cmd.max) findCmd.max = cmd.max;
// If we have returnKey set
if(cmd.returnKey) findCmd.returnKey = cmd.returnKey;
if (cmd.returnKey) findCmd.returnKey = cmd.returnKey;
// If we have showDiskLoc set
if(cmd.showDiskLoc) findCmd.showRecordId = cmd.showDiskLoc;
if (cmd.showDiskLoc) findCmd.showRecordId = cmd.showDiskLoc;
// If we have snapshot set
if(cmd.snapshot) findCmd.snapshot = cmd.snapshot;
if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
// If we have tailable set
if(cmd.tailable) findCmd.tailable = cmd.tailable;
if (cmd.tailable) findCmd.tailable = cmd.tailable;
// If we have oplogReplay set
if(cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
// If we have noCursorTimeout set
if(cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout;
if (cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout;
// If we have awaitData set
if(cmd.awaitData) findCmd.awaitData = cmd.awaitData;
if(cmd.awaitdata) findCmd.awaitData = cmd.awaitdata;
if (cmd.awaitData) findCmd.awaitData = cmd.awaitData;
if (cmd.awaitdata) findCmd.awaitData = cmd.awaitdata;
// If we have partial set
if(cmd.partial) findCmd.partial = cmd.partial;
if (cmd.partial) findCmd.partial = cmd.partial;
// If we have collation passed in
if(cmd.collation) findCmd.collation = cmd.collation;
if (cmd.collation) findCmd.collation = cmd.collation;
// If we have explain, we need to rewrite the find command
// to wrap it in the explain command
if(cmd.explain) {
if (cmd.explain) {
findCmd = {
explain: findCmd
}
};
}
// Did we provide a readConcern
if(cmd.readConcern) findCmd.readConcern = cmd.readConcern;
if (cmd.readConcern) findCmd.readConcern = cmd.readConcern;
// Set up the serialize and ignoreUndefined fields
var serializeFunctions = typeof options.serializeFunctions == 'boolean'
? options.serializeFunctions : false;
var ignoreUndefined = typeof options.ignoreUndefined == 'boolean'
? options.ignoreUndefined : false;
var serializeFunctions =
typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
var ignoreUndefined =
typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
// We have a Mongos topology, check if we need to add a readPreference
if(topology.type == 'mongos'
&& readPreference
&& readPreference.preference != 'primary') {
if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
findCmd = {
'$query': findCmd,
'$readPreference': readPreference.toJSON()
$query: findCmd,
$readPreference: readPreference.toJSON()
};

@@ -513,5 +551,8 @@ }

var query = new Query(bson, commandns, findCmd, {
numberToSkip: 0, numberToReturn: 1
, checkKeys: false, returnFieldSelector: null
, serializeFunctions: serializeFunctions, ignoreUndefined: ignoreUndefined
numberToSkip: 0,
numberToReturn: 1,
checkKeys: false,
returnFieldSelector: null,
serializeFunctions: serializeFunctions,
ignoreUndefined: ignoreUndefined
});

@@ -524,3 +565,3 @@

return query;
}
};

@@ -531,3 +572,3 @@ //

// Set empty options object
options = options || {}
options = options || {};
// Get the readPreference

@@ -538,3 +579,3 @@ var readPreference = getReadPreference(cmd, options);

var finalCmd = {};
for(var name in cmd) {
for (var name in cmd) {
finalCmd[name] = cmd[name];

@@ -547,16 +588,14 @@ }

// Serialize functions
var serializeFunctions = typeof options.serializeFunctions == 'boolean'
? options.serializeFunctions : false;
var serializeFunctions =
typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
// Set up the serialize and ignoreUndefined fields
var ignoreUndefined = typeof options.ignoreUndefined == 'boolean'
? options.ignoreUndefined : false;
var ignoreUndefined =
typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
// We have a Mongos topology, check if we need to add a readPreference
if(topology.type == 'mongos'
&& readPreference
&& readPreference.preference != 'primary') {
if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
finalCmd = {
'$query': finalCmd,
'$readPreference': readPreference.toJSON()
$query: finalCmd,
$readPreference: readPreference.toJSON()
};

@@ -567,5 +606,7 @@ }

var query = new Query(bson, f('%s.$cmd', parts.shift()), finalCmd, {
numberToSkip: 0, numberToReturn: -1
, checkKeys: false, serializeFunctions: serializeFunctions
, ignoreUndefined: ignoreUndefined
numberToSkip: 0,
numberToReturn: -1,
checkKeys: false,
serializeFunctions: serializeFunctions,
ignoreUndefined: ignoreUndefined
});

@@ -578,4 +619,4 @@

return query;
}
};
module.exports = WireProtocol;

@@ -1,6 +0,21 @@

"use strict"
'use strict';
var ReadPreference = require('../topologies/read_preference'),
MongoError = require('../error');
MongoError = require('../error').MongoError;
var MESSAGE_HEADER_SIZE = 16;
// OPCODE Numbers
// Defined at https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#request-opcodes
var opcodes = {
OP_REPLY: 1,
OP_UPDATE: 2001,
OP_INSERT: 2002,
OP_QUERY: 2004,
OP_GETMORE: 2005,
OP_DELETE: 2006,
OP_KILL_CURSORS: 2007,
OP_COMPRESSED: 2012
};
var getReadPreference = function(cmd, options) {

@@ -10,11 +25,11 @@ // Default to command version of the readPreference

// If we have an option readPreference override the command one
if(options.readPreference) {
if (options.readPreference) {
readPreference = options.readPreference;
}
if(typeof readPreference == 'string') {
if (typeof readPreference === 'string') {
readPreference = new ReadPreference(readPreference);
}
if(!(readPreference instanceof ReadPreference)) {
if (!(readPreference instanceof ReadPreference)) {
throw new MongoError('readPreference must be a ReadPreference instance');

@@ -24,6 +39,19 @@ }

return readPreference;
}
};
// Parses the header of a wire protocol message
var parseHeader = function(message) {
return {
length: message.readInt32LE(0),
requestId: message.readInt32LE(4),
responseTo: message.readInt32LE(8),
opCode: message.readInt32LE(12)
};
};
module.exports = {
getReadPreference: getReadPreference
}
getReadPreference: getReadPreference,
MESSAGE_HEADER_SIZE: MESSAGE_HEADER_SIZE,
opcodes: opcodes,
parseHeader: parseHeader
};
{
"name": "mongodb-core",
"version": "2.1.17",
"version": "3.0.0-rc0",
"description": "Core MongoDB driver functionality, no bells and whistles and meant for integration not end applications",
"main": "index.js",
"scripts": {
"test": "node test/runner.js -t functional",
"test": "npm run lint && mongodb-test-runner -t 60000 test/tests/functional",
"coverage": "node_modules/.bin/nyc node test/runner.js -t functional -l && node_modules/.bin/nyc report --reporter=text-lcov | node_modules/.bin/coveralls",
"lint": "eslint lib"
"lint": "eslint index.js lib test",
"format": "prettier --print-width 100 --tab-width 2 --single-quote --write index.js 'test/**/*.js' 'lib/**/*.js'",
"changelog": "conventional-changelog -p angular -i HISTORY.md -s"
},

@@ -21,21 +23,18 @@ "repository": {

"bson": "~1.0.4",
"require_optional": "~1.0.0"
"require_optional": "^1.0.1"
},
"devDependencies": {
"co": "^4.5.4",
"coveralls": "^2.11.6",
"es6-promise": "^3.0.2",
"gleak": "0.5.0",
"integra": "0.1.8",
"jsdoc": "3.3.0-alpha8",
"mkdirp": "0.5.0",
"mongodb-topology-manager": "1.0.x",
"mongodb-version-manager": "christkv/mongodb-version-manager#master",
"nyc": "^5.5.0",
"optimist": "latest",
"rimraf": "2.2.6",
"semver": "4.1.0"
"chai": "^4.1.2",
"co": "^4.6.0",
"conventional-changelog-cli": "^1.3.5",
"eslint": "^4.6.1",
"eslint-plugin-prettier": "^2.2.0",
"jsdoc": "3.5.4",
"mongodb-test-runner": "^1.1.18",
"prettier": "^1.6.1",
"snappy": "^6.0.1"
},
"peerOptionalDependencies": {
"kerberos": "~0.0",
"kerberos": "^0.0.23",
"snappy": "^6.0.1",
"bson-ext": "1.0.5"

@@ -42,0 +41,0 @@ },

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc