mongodb-core
Advanced tools
Comparing version 2.1.17 to 3.0.0-rc0
@@ -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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
568108
9
11551
38
1
Updatedrequire_optional@^1.0.1