Changes
This commit is contained in:
1
node_modules/mongodb/lib/admin.js
generated
vendored
1
node_modules/mongodb/lib/admin.js
generated
vendored
@@ -232,6 +232,7 @@ Admin.prototype.removeUser = function(username, options, callback) {
|
||||
*
|
||||
* @param {string} collectionName The name of the collection to validate.
|
||||
* @param {object} [options] Optional settings.
|
||||
* @param {boolean} [options.background] Validates a collection in the background, without interrupting read or write traffic (only in MongoDB 4.4+)
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {Admin~resultCallback} [callback] The command result callback.
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
|
||||
52
node_modules/mongodb/lib/bulk/common.js
generated
vendored
52
node_modules/mongodb/lib/bulk/common.js
generated
vendored
@@ -11,6 +11,8 @@ const applyRetryableWrites = require('../utils').applyRetryableWrites;
|
||||
const applyWriteConcern = require('../utils').applyWriteConcern;
|
||||
const executeLegacyOperation = require('../utils').executeLegacyOperation;
|
||||
const isPromiseLike = require('../utils').isPromiseLike;
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
const maxWireVersion = require('../core/utils').maxWireVersion;
|
||||
|
||||
// Error codes
|
||||
const WRITE_CONCERN_ERROR = 64;
|
||||
@@ -641,6 +643,10 @@ class FindOperators {
|
||||
document.hint = updateDocument.hint;
|
||||
}
|
||||
|
||||
if (!hasAtomicOperators(updateDocument)) {
|
||||
throw new TypeError('Update document requires atomic operators');
|
||||
}
|
||||
|
||||
// Clear out current Op
|
||||
this.s.currentOp = null;
|
||||
return this.s.options.addToOperationsList(this, UPDATE, document);
|
||||
@@ -650,12 +656,33 @@ class FindOperators {
|
||||
* Add a replace one operation to the bulk operation
|
||||
*
|
||||
* @method
|
||||
* @param {object} updateDocument the new document to replace the existing one with
|
||||
* @param {object} replacement the new document to replace the existing one with
|
||||
* @throws {MongoError} If operation cannot be added to bulk write
|
||||
* @return {OrderedBulkOperation|UnorderedBulkOperation} A reference to the parent BulkOperation
|
||||
*/
|
||||
replaceOne(updateDocument) {
|
||||
this.updateOne(updateDocument);
|
||||
replaceOne(replacement) {
|
||||
// Perform upsert
|
||||
const upsert = typeof this.s.currentOp.upsert === 'boolean' ? this.s.currentOp.upsert : false;
|
||||
|
||||
// Establish the update command
|
||||
const document = {
|
||||
q: this.s.currentOp.selector,
|
||||
u: replacement,
|
||||
multi: false,
|
||||
upsert: upsert
|
||||
};
|
||||
|
||||
if (replacement.hint) {
|
||||
document.hint = replacement.hint;
|
||||
}
|
||||
|
||||
if (hasAtomicOperators(replacement)) {
|
||||
throw new TypeError('Replacement document must not use atomic operators');
|
||||
}
|
||||
|
||||
// Clear out current Op
|
||||
this.s.currentOp = null;
|
||||
return this.s.options.addToOperationsList(this, UPDATE, document);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -943,6 +970,12 @@ class BulkOperationBase {
|
||||
|
||||
// Crud spec update format
|
||||
if (op.updateOne || op.updateMany || op.replaceOne) {
|
||||
if (op.replaceOne && hasAtomicOperators(op[key].replacement)) {
|
||||
throw new TypeError('Replacement document must not use atomic operators');
|
||||
} else if ((op.updateOne || op.updateMany) && !hasAtomicOperators(op[key].update)) {
|
||||
throw new TypeError('Update document requires atomic operators');
|
||||
}
|
||||
|
||||
const multi = op.updateOne || op.replaceOne ? false : true;
|
||||
const operation = {
|
||||
q: op[key].filter,
|
||||
@@ -960,7 +993,15 @@ class BulkOperationBase {
|
||||
} else {
|
||||
if (op[key].upsert) operation.upsert = true;
|
||||
}
|
||||
if (op[key].arrayFilters) operation.arrayFilters = op[key].arrayFilters;
|
||||
if (op[key].arrayFilters) {
|
||||
// TODO: this check should be done at command construction against a connection, not a topology
|
||||
if (maxWireVersion(this.s.topology) < 6) {
|
||||
throw new TypeError('arrayFilters are only supported on MongoDB 3.6+');
|
||||
}
|
||||
|
||||
operation.arrayFilters = op[key].arrayFilters;
|
||||
}
|
||||
|
||||
return this.s.options.addToOperationsList(this, UPDATE, operation);
|
||||
}
|
||||
|
||||
@@ -979,6 +1020,9 @@ class BulkOperationBase {
|
||||
if (op.deleteOne || op.deleteMany) {
|
||||
const limit = op.deleteOne ? 1 : 0;
|
||||
const operation = { q: op[key].filter, limit: limit };
|
||||
if (op[key].hint) {
|
||||
operation.hint = op[key].hint;
|
||||
}
|
||||
if (this.isOrdered) {
|
||||
if (op.collation) operation.collation = op.collation;
|
||||
}
|
||||
|
||||
10
node_modules/mongodb/lib/change_stream.js
generated
vendored
10
node_modules/mongodb/lib/change_stream.js
generated
vendored
@@ -169,7 +169,6 @@ class ChangeStream extends EventEmitter {
|
||||
/**
|
||||
* Is the change stream closed
|
||||
* @method ChangeStream.prototype.isClosed
|
||||
* @param {boolean} [checkCursor=true] also check if the underlying cursor is closed
|
||||
* @return {boolean}
|
||||
*/
|
||||
isClosed() {
|
||||
@@ -326,8 +325,8 @@ class ChangeStreamCursor extends Cursor {
|
||||
|
||||
_initializeCursor(callback) {
|
||||
super._initializeCursor((err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
if (err || result == null) {
|
||||
callback(err, result);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -483,6 +482,11 @@ function waitForTopologyConnected(topology, options, callback) {
|
||||
function processNewChange(changeStream, change, callback) {
|
||||
const cursor = changeStream.cursor;
|
||||
|
||||
// a null change means the cursor has been notified, implicitly closing the change stream
|
||||
if (change == null) {
|
||||
changeStream.closed = true;
|
||||
}
|
||||
|
||||
if (changeStream.closed) {
|
||||
if (callback) callback(new MongoError('ChangeStream is closed'));
|
||||
return;
|
||||
|
||||
15
node_modules/mongodb/lib/cmap/connection.js
generated
vendored
15
node_modules/mongodb/lib/cmap/connection.js
generated
vendored
@@ -4,6 +4,7 @@ const EventEmitter = require('events');
|
||||
const MessageStream = require('./message_stream');
|
||||
const MongoError = require('../core/error').MongoError;
|
||||
const MongoNetworkError = require('../core/error').MongoNetworkError;
|
||||
const MongoNetworkTimeoutError = require('../core/error').MongoNetworkTimeoutError;
|
||||
const MongoWriteConcernError = require('../core/error').MongoWriteConcernError;
|
||||
const CommandResult = require('../core/connection/command_result');
|
||||
const StreamDescription = require('./stream_description').StreamDescription;
|
||||
@@ -77,10 +78,14 @@ class Connection extends EventEmitter {
|
||||
stream.destroy();
|
||||
this.closed = true;
|
||||
this[kQueue].forEach(op =>
|
||||
op.cb(new MongoNetworkError(`connection ${this.id} to ${this.address} timed out`))
|
||||
op.cb(
|
||||
new MongoNetworkTimeoutError(`connection ${this.id} to ${this.address} timed out`, {
|
||||
beforeHandshake: this[kIsMaster] == null
|
||||
})
|
||||
)
|
||||
);
|
||||
this[kQueue].clear();
|
||||
|
||||
this[kQueue].clear();
|
||||
this.emit('close');
|
||||
});
|
||||
|
||||
@@ -218,6 +223,7 @@ function messageHandler(conn) {
|
||||
}
|
||||
|
||||
const operationDescription = conn[kQueue].get(message.responseTo);
|
||||
const callback = operationDescription.cb;
|
||||
|
||||
// SERVER-45775: For exhaust responses we should be able to use the same requestId to
|
||||
// track response, however the server currently synthetically produces remote requests
|
||||
@@ -226,10 +232,7 @@ function messageHandler(conn) {
|
||||
if (message.moreToCome) {
|
||||
// requeue the callback for next synthetic request
|
||||
conn[kQueue].set(message.requestId, operationDescription);
|
||||
}
|
||||
|
||||
const callback = operationDescription.cb;
|
||||
if (operationDescription.socketTimeoutOverride) {
|
||||
} else if (operationDescription.socketTimeoutOverride) {
|
||||
conn[kStream].setTimeout(conn.socketTimeout);
|
||||
}
|
||||
|
||||
|
||||
145
node_modules/mongodb/lib/collection.js
generated
vendored
145
node_modules/mongodb/lib/collection.js
generated
vendored
@@ -5,7 +5,6 @@ const deprecateOptions = require('./utils').deprecateOptions;
|
||||
const checkCollectionName = require('./utils').checkCollectionName;
|
||||
const ObjectID = require('./core').BSON.ObjectID;
|
||||
const MongoError = require('./core').MongoError;
|
||||
const toError = require('./utils').toError;
|
||||
const normalizeHintField = require('./utils').normalizeHintField;
|
||||
const decorateCommand = require('./utils').decorateCommand;
|
||||
const decorateWithCollation = require('./utils').decorateWithCollation;
|
||||
@@ -16,7 +15,6 @@ const unordered = require('./bulk/unordered');
|
||||
const ordered = require('./bulk/ordered');
|
||||
const ChangeStream = require('./change_stream');
|
||||
const executeLegacyOperation = require('./utils').executeLegacyOperation;
|
||||
const resolveReadPreference = require('./utils').resolveReadPreference;
|
||||
const WriteConcern = require('./write_concern');
|
||||
const ReadConcern = require('./read_concern');
|
||||
const MongoDBNamespace = require('./utils').MongoDBNamespace;
|
||||
@@ -24,7 +22,6 @@ const AggregationCursor = require('./aggregation_cursor');
|
||||
const CommandCursor = require('./command_cursor');
|
||||
|
||||
// Operations
|
||||
const checkForAtomicOperators = require('./operations/collection_ops').checkForAtomicOperators;
|
||||
const ensureIndex = require('./operations/collection_ops').ensureIndex;
|
||||
const group = require('./operations/collection_ops').group;
|
||||
const parallelCollectionScan = require('./operations/collection_ops').parallelCollectionScan;
|
||||
@@ -35,7 +32,6 @@ const updateDocuments = require('./operations/common_functions').updateDocuments
|
||||
const AggregateOperation = require('./operations/aggregate');
|
||||
const BulkWriteOperation = require('./operations/bulk_write');
|
||||
const CountDocumentsOperation = require('./operations/count_documents');
|
||||
const CreateIndexOperation = require('./operations/create_index');
|
||||
const CreateIndexesOperation = require('./operations/create_indexes');
|
||||
const DeleteManyOperation = require('./operations/delete_many');
|
||||
const DeleteOneOperation = require('./operations/delete_one');
|
||||
@@ -278,7 +274,7 @@ Object.defineProperty(Collection.prototype, 'hint', {
|
||||
}
|
||||
});
|
||||
|
||||
const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot'];
|
||||
const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot', 'oplogReplay'];
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB
|
||||
@@ -313,6 +309,7 @@ const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot'];
|
||||
* @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true
|
||||
* @param {boolean} [options.noCursorTimeout] The server normally times out idle cursors after an inactivity period (10 minutes) to prevent excess memory use. Set this option to prevent that.
|
||||
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
||||
* @param {boolean} [options.allowDiskUse] Enables writing to temporary files on the server.
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @throws {MongoError}
|
||||
* @return {Cursor}
|
||||
@@ -399,7 +396,7 @@ Collection.prototype.find = deprecateOptions(
|
||||
newOptions.slaveOk = options.slaveOk != null ? options.slaveOk : this.s.db.slaveOk;
|
||||
|
||||
// Add read preference if needed
|
||||
newOptions.readPreference = resolveReadPreference(this, newOptions);
|
||||
newOptions.readPreference = ReadPreference.resolve(this, newOptions);
|
||||
|
||||
// Set slave ok to true if read preference different from primary
|
||||
if (
|
||||
@@ -422,6 +419,10 @@ Collection.prototype.find = deprecateOptions(
|
||||
query: selector
|
||||
};
|
||||
|
||||
if (typeof options.allowDiskUse === 'boolean') {
|
||||
findCommand.allowDiskUse = options.allowDiskUse;
|
||||
}
|
||||
|
||||
// Ensure we use the right await data option
|
||||
if (typeof newOptions.awaitdata === 'boolean') {
|
||||
newOptions.awaitData = newOptions.awaitdata;
|
||||
@@ -744,14 +745,6 @@ Collection.prototype.insert = deprecate(function(docs, options, callback) {
|
||||
*/
|
||||
Collection.prototype.updateOne = function(filter, update, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
const err = checkForAtomicOperators(update);
|
||||
if (err) {
|
||||
if (typeof callback === 'function') return callback(err);
|
||||
return this.s.promiseLibrary.reject(err);
|
||||
}
|
||||
|
||||
options = Object.assign({}, options);
|
||||
|
||||
// Add ignoreUndefined
|
||||
@@ -760,9 +753,11 @@ Collection.prototype.updateOne = function(filter, update, options, callback) {
|
||||
options.ignoreUndefined = this.s.options.ignoreUndefined;
|
||||
}
|
||||
|
||||
const updateOneOperation = new UpdateOneOperation(this, filter, update, options);
|
||||
|
||||
return executeOperation(this.s.topology, updateOneOperation, callback);
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new UpdateOneOperation(this, filter, update, options),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -795,9 +790,11 @@ Collection.prototype.replaceOne = function(filter, doc, options, callback) {
|
||||
options.ignoreUndefined = this.s.options.ignoreUndefined;
|
||||
}
|
||||
|
||||
const replaceOneOperation = new ReplaceOneOperation(this, filter, doc, options);
|
||||
|
||||
return executeOperation(this.s.topology, replaceOneOperation, callback);
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new ReplaceOneOperation(this, filter, doc, options),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -823,14 +820,6 @@ Collection.prototype.replaceOne = function(filter, doc, options, callback) {
|
||||
*/
|
||||
Collection.prototype.updateMany = function(filter, update, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
const err = checkForAtomicOperators(update);
|
||||
if (err) {
|
||||
if (typeof callback === 'function') return callback(err);
|
||||
return this.s.promiseLibrary.reject(err);
|
||||
}
|
||||
|
||||
options = Object.assign({}, options);
|
||||
|
||||
// Add ignoreUndefined
|
||||
@@ -839,9 +828,11 @@ Collection.prototype.updateMany = function(filter, update, options, callback) {
|
||||
options.ignoreUndefined = this.s.options.ignoreUndefined;
|
||||
}
|
||||
|
||||
const updateManyOperation = new UpdateManyOperation(this, filter, update, options);
|
||||
|
||||
return executeOperation(this.s.topology, updateManyOperation, callback);
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new UpdateManyOperation(this, filter, update, options),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -913,6 +904,7 @@ Collection.prototype.update = deprecate(function(selector, update, options, call
|
||||
* @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
|
||||
* @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {string|object} [options.hint] optional index hint for optimizing the filter query
|
||||
* @param {Collection~deleteWriteOpCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
*/
|
||||
@@ -946,6 +938,7 @@ Collection.prototype.removeOne = Collection.prototype.deleteOne;
|
||||
* @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
|
||||
* @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {string|object} [options.hint] optional index hint for optimizing the filter query
|
||||
* @param {Collection~deleteWriteOpCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
*/
|
||||
@@ -1206,6 +1199,7 @@ Collection.prototype.isCapped = function(options, callback) {
|
||||
* @param {object} [options.partialFilterExpression] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
|
||||
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {(number|string)} [options.commitQuorum] (MongoDB 4.4. or higher) Specifies how many data-bearing members of a replica set, including the primary, must complete the index builds successfully before the primary marks the indexes as ready. This option accepts the same values for the "w" field in a write concern plus "votingMembers", which indicates all voting data-bearing nodes.
|
||||
* @param {Collection~resultCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
* @example
|
||||
@@ -1232,14 +1226,14 @@ Collection.prototype.createIndex = function(fieldOrSpec, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
const createIndexOperation = new CreateIndexOperation(
|
||||
this.s.db,
|
||||
const createIndexesOperation = new CreateIndexesOperation(
|
||||
this,
|
||||
this.collectionName,
|
||||
fieldOrSpec,
|
||||
options
|
||||
);
|
||||
|
||||
return executeOperation(this.s.topology, createIndexOperation, callback);
|
||||
return executeOperation(this.s.topology, createIndexesOperation, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1260,6 +1254,7 @@ Collection.prototype.createIndex = function(fieldOrSpec, options, callback) {
|
||||
* @param {Collection~IndexDefinition[]} indexSpecs An array of index specifications to be created
|
||||
* @param {Object} [options] Optional settings
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {(number|string)} [options.commitQuorum] (MongoDB 4.4. or higher) Specifies how many data-bearing members of a replica set, including the primary, must complete the index builds successfully before the primary marks the indexes as ready. This option accepts the same values for the "w" field in a write concern plus "votingMembers", which indicates all voting data-bearing nodes.
|
||||
* @param {Collection~resultCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
* @example
|
||||
@@ -1284,9 +1279,15 @@ Collection.prototype.createIndexes = function(indexSpecs, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
|
||||
options = options ? Object.assign({}, options) : {};
|
||||
|
||||
if (typeof options.maxTimeMS !== 'number') delete options.maxTimeMS;
|
||||
|
||||
const createIndexesOperation = new CreateIndexesOperation(this, indexSpecs, options);
|
||||
const createIndexesOperation = new CreateIndexesOperation(
|
||||
this,
|
||||
this.collectionName,
|
||||
indexSpecs,
|
||||
options
|
||||
);
|
||||
|
||||
return executeOperation(this.s.topology, createIndexesOperation, callback);
|
||||
};
|
||||
@@ -1353,19 +1354,20 @@ Collection.prototype.dropAllIndexes = deprecate(
|
||||
* Reindex all indexes on the collection
|
||||
* Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
* @method
|
||||
* @deprecated use db.command instead
|
||||
* @param {Object} [options] Optional settings
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {Collection~resultCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
*/
|
||||
Collection.prototype.reIndex = function(options, callback) {
|
||||
Collection.prototype.reIndex = deprecate(function(options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
const reIndexOperation = new ReIndexOperation(this, options);
|
||||
|
||||
return executeOperation(this.s.topology, reIndexOperation, callback);
|
||||
};
|
||||
}, 'collection.reIndex is deprecated. Use db.command instead.');
|
||||
|
||||
/**
|
||||
* Get the list of all indexes information for the collection.
|
||||
@@ -1665,13 +1667,11 @@ Collection.prototype.findOneAndDelete = function(filter, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
// Basic validation
|
||||
if (filter == null || typeof filter !== 'object')
|
||||
throw toError('filter parameter must be an object');
|
||||
|
||||
const findOneAndDeleteOperation = new FindOneAndDeleteOperation(this, filter, options);
|
||||
|
||||
return executeOperation(this.s.topology, findOneAndDeleteOperation, callback);
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new FindOneAndDeleteOperation(this, filter, options),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1683,6 +1683,7 @@ Collection.prototype.findOneAndDelete = function(filter, options, callback) {
|
||||
* @param {object} [options] Optional settings.
|
||||
* @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
|
||||
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
||||
* @param {string|object} [options.hint] An optional index to use for this operation
|
||||
* @param {number} [options.maxTimeMS] The maximum amount of time to allow the query to run.
|
||||
* @param {object} [options.projection] Limits the fields to return for all matching documents.
|
||||
* @param {object} [options.sort] Determines which document the operation modifies if the query selects multiple documents.
|
||||
@@ -1699,27 +1700,11 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
// Basic validation
|
||||
if (filter == null || typeof filter !== 'object')
|
||||
throw toError('filter parameter must be an object');
|
||||
if (replacement == null || typeof replacement !== 'object')
|
||||
throw toError('replacement parameter must be an object');
|
||||
|
||||
// Check that there are no atomic operators
|
||||
const keys = Object.keys(replacement);
|
||||
|
||||
if (keys[0] && keys[0][0] === '$') {
|
||||
throw toError('The replacement document must not contain atomic operators.');
|
||||
}
|
||||
|
||||
const findOneAndReplaceOperation = new FindOneAndReplaceOperation(
|
||||
this,
|
||||
filter,
|
||||
replacement,
|
||||
options
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new FindOneAndReplaceOperation(this, filter, replacement, options),
|
||||
callback
|
||||
);
|
||||
|
||||
return executeOperation(this.s.topology, findOneAndReplaceOperation, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1732,6 +1717,7 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
|
||||
* @param {Array} [options.arrayFilters] optional list of array filters referenced in filtered positional operators
|
||||
* @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
|
||||
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
||||
* @param {string|object} [options.hint] An optional index to use for this operation
|
||||
* @param {number} [options.maxTimeMS] The maximum amount of time to allow the query to run.
|
||||
* @param {object} [options.projection] Limits the fields to return for all matching documents.
|
||||
* @param {object} [options.sort] Determines which document the operation modifies if the query selects multiple documents.
|
||||
@@ -1740,7 +1726,7 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
|
||||
* @param {boolean} [options.checkKeys=false] If true, will throw if bson documents start with `$` or include a `.` in any key value
|
||||
* @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
|
||||
* @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {ClientSession} [options.session] An ptional session to use for this operation
|
||||
* @param {Collection~findAndModifyCallback} [callback] The collection result callback
|
||||
* @return {Promise<Collection~findAndModifyWriteOpResultObject>} returns Promise if no callback passed
|
||||
*/
|
||||
@@ -1748,21 +1734,11 @@ Collection.prototype.findOneAndUpdate = function(filter, update, options, callba
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
// Basic validation
|
||||
if (filter == null || typeof filter !== 'object')
|
||||
throw toError('filter parameter must be an object');
|
||||
if (update == null || typeof update !== 'object')
|
||||
throw toError('update parameter must be an object');
|
||||
|
||||
const err = checkForAtomicOperators(update);
|
||||
if (err) {
|
||||
if (typeof callback === 'function') return callback(err);
|
||||
return this.s.promiseLibrary.reject(err);
|
||||
}
|
||||
|
||||
const findOneAndUpdateOperation = new FindOneAndUpdateOperation(this, filter, update, options);
|
||||
|
||||
return executeOperation(this.s.topology, findOneAndUpdateOperation, callback);
|
||||
return executeOperation(
|
||||
this.s.topology,
|
||||
new FindOneAndUpdateOperation(this, filter, update, options),
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1864,7 +1840,7 @@ Collection.prototype.findAndRemove = deprecate(function(query, sort, options, ca
|
||||
* @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
|
||||
* @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
|
||||
* @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
|
||||
* @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
|
||||
* @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
|
||||
* @param {string} [options.comment] Add a comment to an aggregation command
|
||||
* @param {string|object} [options.hint] Add an index selection hint to an aggregation command
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
@@ -1978,7 +1954,7 @@ Collection.prototype.parallelCollectionScan = deprecate(function(options, callba
|
||||
|
||||
options = Object.assign({}, options);
|
||||
// Ensure we have the right read preference inheritance
|
||||
options.readPreference = resolveReadPreference(this, options);
|
||||
options.readPreference = ReadPreference.resolve(this, options);
|
||||
|
||||
// Add a promiseLibrary
|
||||
options.promiseLibrary = this.s.promiseLibrary;
|
||||
@@ -2009,8 +1985,9 @@ Collection.prototype.parallelCollectionScan = deprecate(function(options, callba
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {Collection~resultCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
* @deprecated See {@link https://docs.mongodb.com/manual/geospatial-queries/|geospatial queries docs} for current geospatial support
|
||||
*/
|
||||
Collection.prototype.geoHaystackSearch = function(x, y, options, callback) {
|
||||
Collection.prototype.geoHaystackSearch = deprecate(function(x, y, options, callback) {
|
||||
const args = Array.prototype.slice.call(arguments, 2);
|
||||
callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
|
||||
options = args.length ? args.shift() || {} : {};
|
||||
@@ -2018,7 +1995,7 @@ Collection.prototype.geoHaystackSearch = function(x, y, options, callback) {
|
||||
const geoHaystackSearchOperation = new GeoHaystackSearchOperation(this, x, y, options);
|
||||
|
||||
return executeOperation(this.s.topology, geoHaystackSearchOperation, callback);
|
||||
};
|
||||
}, 'geoHaystackSearch is deprecated, and will be removed in a future version.');
|
||||
|
||||
/**
|
||||
* Run a group command across a collection
|
||||
|
||||
167
node_modules/mongodb/lib/core/auth/auth_provider.js
generated
vendored
167
node_modules/mongodb/lib/core/auth/auth_provider.js
generated
vendored
@@ -1,158 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const MongoError = require('../error').MongoError;
|
||||
|
||||
/**
|
||||
* Creates a new AuthProvider, which dictates how to authenticate for a given
|
||||
* mechanism.
|
||||
* @class
|
||||
* Context used during authentication
|
||||
*
|
||||
* @property {Connection} connection The connection to authenticate
|
||||
* @property {MongoCredentials} credentials The credentials to use for authentication
|
||||
* @property {object} options The options passed to the `connect` method
|
||||
* @property {object?} response The response of the initial handshake
|
||||
* @property {Buffer?} nonce A random nonce generated for use in an authentication conversation
|
||||
*/
|
||||
class AuthContext {
|
||||
constructor(connection, credentials, options) {
|
||||
this.connection = connection;
|
||||
this.credentials = credentials;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
class AuthProvider {
|
||||
constructor(bson) {
|
||||
this.bson = bson;
|
||||
this.authStore = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the handshake document before the initial handshake.
|
||||
*
|
||||
* @param {object} handshakeDoc The document used for the initial handshake on a connection
|
||||
* @param {AuthContext} authContext Context for authentication flow
|
||||
* @param {function} callback
|
||||
*/
|
||||
prepare(handshakeDoc, context, callback) {
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate
|
||||
* @method
|
||||
* @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
|
||||
* @param {Connection[]} connections Connections to authenticate using this authenticator
|
||||
* @param {MongoCredentials} credentials Authentication credentials
|
||||
*
|
||||
* @param {AuthContext} context A shared context for authentication flow
|
||||
* @param {authResultCallback} callback The callback to return the result from the authentication
|
||||
*/
|
||||
auth(sendAuthCommand, connections, credentials, callback) {
|
||||
// Total connections
|
||||
let count = connections.length;
|
||||
|
||||
if (count === 0) {
|
||||
callback(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Valid connections
|
||||
let numberOfValidConnections = 0;
|
||||
let errorObject = null;
|
||||
|
||||
const execute = connection => {
|
||||
this._authenticateSingleConnection(sendAuthCommand, connection, credentials, (err, r) => {
|
||||
// Adjust count
|
||||
count = count - 1;
|
||||
|
||||
// If we have an error
|
||||
if (err) {
|
||||
errorObject = new MongoError(err);
|
||||
} else if (r && (r.$err || r.errmsg)) {
|
||||
errorObject = new MongoError(r);
|
||||
} else {
|
||||
numberOfValidConnections = numberOfValidConnections + 1;
|
||||
}
|
||||
|
||||
// Still authenticating against other connections.
|
||||
if (count !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We have authenticated all connections
|
||||
if (numberOfValidConnections > 0) {
|
||||
// Store the auth details
|
||||
this.addCredentials(credentials);
|
||||
// Return correct authentication
|
||||
callback(null, true);
|
||||
} else {
|
||||
if (errorObject == null) {
|
||||
errorObject = new MongoError(`failed to authenticate using ${credentials.mechanism}`);
|
||||
}
|
||||
callback(errorObject, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const executeInNextTick = _connection => process.nextTick(() => execute(_connection));
|
||||
|
||||
// For each connection we need to authenticate
|
||||
while (connections.length > 0) {
|
||||
executeInNextTick(connections.shift());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a single connection authenticating. Is meant to be overridden.
|
||||
* Will error if called directly
|
||||
* @ignore
|
||||
*/
|
||||
_authenticateSingleConnection(/*sendAuthCommand, connection, credentials, callback*/) {
|
||||
throw new Error('_authenticateSingleConnection must be overridden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds credentials to store only if it does not exist
|
||||
* @param {MongoCredentials} credentials credentials to add to store
|
||||
*/
|
||||
addCredentials(credentials) {
|
||||
const found = this.authStore.some(cred => cred.equals(credentials));
|
||||
|
||||
if (!found) {
|
||||
this.authStore.push(credentials);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re authenticate pool
|
||||
* @method
|
||||
* @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
|
||||
* @param {Connection[]} connections Connections to authenticate using this authenticator
|
||||
* @param {authResultCallback} callback The callback to return the result from the authentication
|
||||
*/
|
||||
reauthenticate(sendAuthCommand, connections, callback) {
|
||||
const authStore = this.authStore.slice(0);
|
||||
let count = authStore.length;
|
||||
if (count === 0) {
|
||||
return callback(null, null);
|
||||
}
|
||||
|
||||
for (let i = 0; i < authStore.length; i++) {
|
||||
this.auth(sendAuthCommand, connections, authStore[i], function(err) {
|
||||
count = count - 1;
|
||||
if (count === 0) {
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove credentials that have been previously stored in the auth provider
|
||||
* @method
|
||||
* @param {string} source Name of database we are removing authStore details about
|
||||
* @return {object}
|
||||
*/
|
||||
logout(source) {
|
||||
this.authStore = this.authStore.filter(credentials => credentials.source !== source);
|
||||
auth(context, callback) {
|
||||
callback(new TypeError('`auth` method must be overridden by subclass'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that writes authentication commands to a specific connection
|
||||
* @callback SendAuthCommand
|
||||
* @param {Connection} connection The connection to write to
|
||||
* @param {Command} command A command with a toBin method that can be written to a connection
|
||||
* @param {AuthWriteCallback} callback Callback called when command response is received
|
||||
*/
|
||||
|
||||
/**
|
||||
* A callback for a specific auth command
|
||||
* @callback AuthWriteCallback
|
||||
* @param {Error} err If command failed, an error from the server
|
||||
* @param {object} r The response from the server
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a result from an authentication strategy
|
||||
* This is a result from an authentication provider
|
||||
*
|
||||
* @callback authResultCallback
|
||||
* @param {error} error An error object. Set to null if no error present
|
||||
* @param {boolean} result The result of the authentication process
|
||||
*/
|
||||
|
||||
module.exports = { AuthProvider };
|
||||
module.exports = { AuthContext, AuthProvider };
|
||||
|
||||
4
node_modules/mongodb/lib/core/auth/defaultAuthProviders.js
generated
vendored
4
node_modules/mongodb/lib/core/auth/defaultAuthProviders.js
generated
vendored
@@ -4,9 +4,9 @@ const MongoCR = require('./mongocr');
|
||||
const X509 = require('./x509');
|
||||
const Plain = require('./plain');
|
||||
const GSSAPI = require('./gssapi');
|
||||
const SSPI = require('./sspi');
|
||||
const ScramSHA1 = require('./scram').ScramSHA1;
|
||||
const ScramSHA256 = require('./scram').ScramSHA256;
|
||||
const MongoDBAWS = require('./mongodb_aws');
|
||||
|
||||
/**
|
||||
* Returns the default authentication providers.
|
||||
@@ -16,11 +16,11 @@ const ScramSHA256 = require('./scram').ScramSHA256;
|
||||
*/
|
||||
function defaultAuthProviders(bson) {
|
||||
return {
|
||||
'mongodb-aws': new MongoDBAWS(bson),
|
||||
mongocr: new MongoCR(bson),
|
||||
x509: new X509(bson),
|
||||
plain: new Plain(bson),
|
||||
gssapi: new GSSAPI(bson),
|
||||
sspi: new SSPI(bson),
|
||||
'scram-sha-1': new ScramSHA1(bson),
|
||||
'scram-sha-256': new ScramSHA256(bson)
|
||||
};
|
||||
|
||||
294
node_modules/mongodb/lib/core/auth/gssapi.js
generated
vendored
294
node_modules/mongodb/lib/core/auth/gssapi.js
generated
vendored
@@ -1,50 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
const retrieveKerberos = require('../utils').retrieveKerberos;
|
||||
let kerberos;
|
||||
|
||||
/**
|
||||
* Creates a new GSSAPI authentication mechanism
|
||||
* @class
|
||||
* @extends AuthProvider
|
||||
*/
|
||||
class GSSAPI extends AuthProvider {
|
||||
/**
|
||||
* Implementation of authentication for a single connection
|
||||
* @override
|
||||
*/
|
||||
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
||||
const source = credentials.source;
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const mechanismProperties = credentials.mechanismProperties;
|
||||
const gssapiServiceName =
|
||||
mechanismProperties['gssapiservicename'] ||
|
||||
mechanismProperties['gssapiServiceName'] ||
|
||||
'mongodb';
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
|
||||
GSSAPIInitialize(
|
||||
this,
|
||||
kerberos.processes.MongoAuthProcess,
|
||||
source,
|
||||
username,
|
||||
password,
|
||||
source,
|
||||
gssapiServiceName,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
mechanismProperties,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate
|
||||
* @override
|
||||
* @method
|
||||
*/
|
||||
auth(sendAuthCommand, connections, credentials, callback) {
|
||||
if (kerberos == null) {
|
||||
try {
|
||||
kerberos = retrieveKerberos();
|
||||
@@ -53,189 +16,76 @@ class GSSAPI extends AuthProvider {
|
||||
}
|
||||
}
|
||||
|
||||
super.auth(sendAuthCommand, connections, credentials, callback);
|
||||
// TODO: Destructure this
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const mechanismProperties = credentials.mechanismProperties;
|
||||
const gssapiServiceName =
|
||||
mechanismProperties['gssapiservicename'] ||
|
||||
mechanismProperties['gssapiServiceName'] ||
|
||||
'mongodb';
|
||||
|
||||
const MongoAuthProcess = kerberos.processes.MongoAuthProcess;
|
||||
const authProcess = new MongoAuthProcess(
|
||||
connection.host,
|
||||
connection.port,
|
||||
gssapiServiceName,
|
||||
mechanismProperties
|
||||
);
|
||||
|
||||
authProcess.init(username, password, err => {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
authProcess.transition('', (err, payload) => {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
const command = {
|
||||
saslStart: 1,
|
||||
mechanism: 'GSSAPI',
|
||||
payload,
|
||||
autoAuthorize: 1
|
||||
};
|
||||
|
||||
connection.command('$external.$cmd', command, (err, result) => {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
const doc = result.result;
|
||||
authProcess.transition(doc.payload, (err, payload) => {
|
||||
if (err) return callback(err, false);
|
||||
const command = {
|
||||
saslContinue: 1,
|
||||
conversationId: doc.conversationId,
|
||||
payload
|
||||
};
|
||||
|
||||
connection.command('$external.$cmd', command, (err, result) => {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
const doc = result.result;
|
||||
authProcess.transition(doc.payload, (err, payload) => {
|
||||
if (err) return callback(err, false);
|
||||
const command = {
|
||||
saslContinue: 1,
|
||||
conversationId: doc.conversationId,
|
||||
payload
|
||||
};
|
||||
|
||||
connection.command('$external.$cmd', command, (err, result) => {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
const response = result.result;
|
||||
authProcess.transition(null, err => {
|
||||
if (err) return callback(err, null);
|
||||
callback(null, response);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize step
|
||||
var GSSAPIInitialize = function(
|
||||
self,
|
||||
MongoAuthProcess,
|
||||
db,
|
||||
username,
|
||||
password,
|
||||
authdb,
|
||||
gssapiServiceName,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
options,
|
||||
callback
|
||||
) {
|
||||
// Create authenticator
|
||||
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);
|
||||
|
||||
// Perform the first step
|
||||
mongo_auth_process.transition('', function(err, payload) {
|
||||
if (err) return callback(err, false);
|
||||
|
||||
// Call the next db step
|
||||
MongoDBGSSAPIFirstStep(
|
||||
self,
|
||||
mongo_auth_process,
|
||||
payload,
|
||||
db,
|
||||
username,
|
||||
password,
|
||||
authdb,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Perform first step against mongodb
|
||||
var MongoDBGSSAPIFirstStep = function(
|
||||
self,
|
||||
mongo_auth_process,
|
||||
payload,
|
||||
db,
|
||||
username,
|
||||
password,
|
||||
authdb,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
) {
|
||||
// Build the sasl start command
|
||||
var command = {
|
||||
saslStart: 1,
|
||||
mechanism: 'GSSAPI',
|
||||
payload: payload,
|
||||
autoAuthorize: 1
|
||||
};
|
||||
|
||||
// Write the commmand on the connection
|
||||
sendAuthCommand(connection, '$external.$cmd', command, (err, doc) => {
|
||||
if (err) return callback(err, false);
|
||||
// Execute mongodb transition
|
||||
mongo_auth_process.transition(doc.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,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Perform first step against mongodb
|
||||
var MongoDBGSSAPISecondStep = function(
|
||||
self,
|
||||
mongo_auth_process,
|
||||
payload,
|
||||
doc,
|
||||
db,
|
||||
username,
|
||||
password,
|
||||
authdb,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
) {
|
||||
// Build Authentication command to send to MongoDB
|
||||
var command = {
|
||||
saslContinue: 1,
|
||||
conversationId: doc.conversationId,
|
||||
payload: payload
|
||||
};
|
||||
|
||||
// Execute the command
|
||||
// Write the commmand on the connection
|
||||
sendAuthCommand(connection, '$external.$cmd', command, (err, doc) => {
|
||||
if (err) return callback(err, false);
|
||||
// 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,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var MongoDBGSSAPIThirdStep = function(
|
||||
self,
|
||||
mongo_auth_process,
|
||||
payload,
|
||||
doc,
|
||||
db,
|
||||
username,
|
||||
password,
|
||||
authdb,
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
callback
|
||||
) {
|
||||
// Build final command
|
||||
var command = {
|
||||
saslContinue: 1,
|
||||
conversationId: doc.conversationId,
|
||||
payload: payload
|
||||
};
|
||||
|
||||
// Execute the command
|
||||
sendAuthCommand(connection, '$external.$cmd', command, (err, r) => {
|
||||
if (err) return callback(err, false);
|
||||
mongo_auth_process.transition(null, function(err) {
|
||||
if (err) return callback(err, null);
|
||||
callback(null, r);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a result from a authentication strategy
|
||||
*
|
||||
* @callback authResultCallback
|
||||
* @param {error} error An error object. Set to null if no error present
|
||||
* @param {boolean} result The result of the authentication process
|
||||
*/
|
||||
|
||||
module.exports = GSSAPI;
|
||||
|
||||
32
node_modules/mongodb/lib/core/auth/mongo_credentials.js
generated
vendored
32
node_modules/mongodb/lib/core/auth/mongo_credentials.js
generated
vendored
@@ -47,7 +47,24 @@ class MongoCredentials {
|
||||
this.password = options.password;
|
||||
this.source = options.source || options.db;
|
||||
this.mechanism = options.mechanism || 'default';
|
||||
this.mechanismProperties = options.mechanismProperties;
|
||||
this.mechanismProperties = options.mechanismProperties || {};
|
||||
|
||||
if (this.mechanism.match(/MONGODB-AWS/i)) {
|
||||
if (this.username == null && process.env.AWS_ACCESS_KEY_ID) {
|
||||
this.username = process.env.AWS_ACCESS_KEY_ID;
|
||||
}
|
||||
|
||||
if (this.password == null && process.env.AWS_SECRET_ACCESS_KEY) {
|
||||
this.password = process.env.AWS_SECRET_ACCESS_KEY;
|
||||
}
|
||||
|
||||
if (this.mechanismProperties.AWS_SESSION_TOKEN == null && process.env.AWS_SESSION_TOKEN) {
|
||||
this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(this.mechanismProperties);
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,12 +86,21 @@ class MongoCredentials {
|
||||
* based on the server version and server supported sasl mechanisms.
|
||||
*
|
||||
* @param {Object} [ismaster] An ismaster response from the server
|
||||
* @returns {MongoCredentials}
|
||||
*/
|
||||
resolveAuthMechanism(ismaster) {
|
||||
// If the mechanism is not "default", then it does not need to be resolved
|
||||
if (this.mechanism.toLowerCase() === 'default') {
|
||||
this.mechanism = getDefaultAuthMechanism(ismaster);
|
||||
if (this.mechanism.match(/DEFAULT/i)) {
|
||||
return new MongoCredentials({
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
source: this.source,
|
||||
mechanism: getDefaultAuthMechanism(ismaster),
|
||||
mechanismProperties: this.mechanismProperties
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
node_modules/mongodb/lib/core/auth/mongocr.js
generated
vendored
18
node_modules/mongodb/lib/core/auth/mongocr.js
generated
vendored
@@ -3,27 +3,21 @@
|
||||
const crypto = require('crypto');
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
/**
|
||||
* Creates a new MongoCR authentication mechanism
|
||||
*
|
||||
* @extends AuthProvider
|
||||
*/
|
||||
class MongoCR extends AuthProvider {
|
||||
/**
|
||||
* Implementation of authentication for a single connection
|
||||
* @override
|
||||
*/
|
||||
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const source = credentials.source;
|
||||
|
||||
sendAuthCommand(connection, `${source}.$cmd`, { getnonce: 1 }, (err, r) => {
|
||||
connection.command(`${source}.$cmd`, { getnonce: 1 }, (err, result) => {
|
||||
let nonce = null;
|
||||
let key = null;
|
||||
|
||||
// Get nonce
|
||||
if (err == null) {
|
||||
const r = result.result;
|
||||
nonce = r.nonce;
|
||||
// Use node md5 generator
|
||||
let md5 = crypto.createHash('md5');
|
||||
@@ -43,7 +37,7 @@ class MongoCR extends AuthProvider {
|
||||
key
|
||||
};
|
||||
|
||||
sendAuthCommand(connection, `${source}.$cmd`, authenticateCommand, callback);
|
||||
connection.command(`${source}.$cmd`, authenticateCommand, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
17
node_modules/mongodb/lib/core/auth/plain.js
generated
vendored
17
node_modules/mongodb/lib/core/auth/plain.js
generated
vendored
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
const retrieveBSON = require('../connection/utils').retrieveBSON;
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
@@ -7,19 +6,13 @@ const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
const BSON = retrieveBSON();
|
||||
const Binary = BSON.Binary;
|
||||
|
||||
/**
|
||||
* Creates a new Plain authentication mechanism
|
||||
*
|
||||
* @extends AuthProvider
|
||||
*/
|
||||
class Plain extends AuthProvider {
|
||||
/**
|
||||
* Implementation of authentication for a single connection
|
||||
* @override
|
||||
*/
|
||||
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
|
||||
const payload = new Binary(`\x00${username}\x00${password}`);
|
||||
const command = {
|
||||
saslStart: 1,
|
||||
@@ -28,7 +21,7 @@ class Plain extends AuthProvider {
|
||||
autoAuthorize: 1
|
||||
};
|
||||
|
||||
sendAuthCommand(connection, '$external.$cmd', command, callback);
|
||||
connection.command('$external.$cmd', command, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
441
node_modules/mongodb/lib/core/auth/scram.js
generated
vendored
441
node_modules/mongodb/lib/core/auth/scram.js
generated
vendored
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const Buffer = require('safe-buffer').Buffer;
|
||||
const retrieveBSON = require('../connection/utils').retrieveBSON;
|
||||
@@ -16,32 +15,236 @@ try {
|
||||
// don't do anything;
|
||||
}
|
||||
|
||||
var parsePayload = function(payload) {
|
||||
var dict = {};
|
||||
var parts = payload.split(',');
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var valueParts = parts[i].split('=');
|
||||
class ScramSHA extends AuthProvider {
|
||||
constructor(bson, cryptoMethod) {
|
||||
super(bson);
|
||||
this.cryptoMethod = cryptoMethod || 'sha1';
|
||||
}
|
||||
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const cryptoMethod = this.cryptoMethod;
|
||||
if (cryptoMethod === 'sha256' && saslprep == null) {
|
||||
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
|
||||
}
|
||||
|
||||
crypto.randomBytes(24, (err, nonce) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// store the nonce for later use
|
||||
Object.assign(authContext, { nonce });
|
||||
|
||||
const credentials = authContext.credentials;
|
||||
const request = Object.assign({}, handshakeDoc, {
|
||||
speculativeAuthenticate: Object.assign(makeFirstMessage(cryptoMethod, credentials, nonce), {
|
||||
db: credentials.source
|
||||
})
|
||||
});
|
||||
|
||||
callback(undefined, request);
|
||||
});
|
||||
}
|
||||
|
||||
auth(authContext, callback) {
|
||||
const response = authContext.response;
|
||||
if (response && response.speculativeAuthenticate) {
|
||||
continueScramConversation(
|
||||
this.cryptoMethod,
|
||||
response.speculativeAuthenticate,
|
||||
authContext,
|
||||
callback
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
executeScram(this.cryptoMethod, authContext, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUsername(username) {
|
||||
return username.replace('=', '=3D').replace(',', '=2C');
|
||||
}
|
||||
|
||||
function clientFirstMessageBare(username, nonce) {
|
||||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
||||
// Since the username is not sasl-prep-d, we need to do this here.
|
||||
return Buffer.concat([
|
||||
Buffer.from('n=', 'utf8'),
|
||||
Buffer.from(username, 'utf8'),
|
||||
Buffer.from(',r=', 'utf8'),
|
||||
Buffer.from(nonce.toString('base64'), 'utf8')
|
||||
]);
|
||||
}
|
||||
|
||||
function makeFirstMessage(cryptoMethod, credentials, nonce) {
|
||||
const username = cleanUsername(credentials.username);
|
||||
const mechanism = cryptoMethod === 'sha1' ? 'SCRAM-SHA-1' : 'SCRAM-SHA-256';
|
||||
|
||||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
||||
// Since the username is not sasl-prep-d, we need to do this here.
|
||||
return {
|
||||
saslStart: 1,
|
||||
mechanism,
|
||||
payload: new Binary(
|
||||
Buffer.concat([Buffer.from('n,,', 'utf8'), clientFirstMessageBare(username, nonce)])
|
||||
),
|
||||
autoAuthorize: 1,
|
||||
options: { skipEmptyExchange: true }
|
||||
};
|
||||
}
|
||||
|
||||
function executeScram(cryptoMethod, authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const nonce = authContext.nonce;
|
||||
const db = credentials.source;
|
||||
|
||||
const saslStartCmd = makeFirstMessage(cryptoMethod, credentials, nonce);
|
||||
connection.command(`${db}.$cmd`, saslStartCmd, (_err, result) => {
|
||||
const err = resolveError(_err, result);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
continueScramConversation(cryptoMethod, result.result, authContext, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function continueScramConversation(cryptoMethod, response, authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const nonce = authContext.nonce;
|
||||
|
||||
const db = credentials.source;
|
||||
const username = cleanUsername(credentials.username);
|
||||
const password = credentials.password;
|
||||
|
||||
let processedPassword;
|
||||
if (cryptoMethod === 'sha256') {
|
||||
processedPassword = saslprep ? saslprep(password) : password;
|
||||
} else {
|
||||
try {
|
||||
processedPassword = passwordDigest(username, password);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
}
|
||||
|
||||
const payload = Buffer.isBuffer(response.payload)
|
||||
? new Binary(response.payload)
|
||||
: response.payload;
|
||||
const dict = parsePayload(payload.value());
|
||||
|
||||
const iterations = parseInt(dict.i, 10);
|
||||
if (iterations && iterations < 4096) {
|
||||
callback(new MongoError(`Server returned an invalid iteration count ${iterations}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
const salt = dict.s;
|
||||
const rnonce = dict.r;
|
||||
if (rnonce.startsWith('nonce')) {
|
||||
callback(new MongoError(`Server returned an invalid nonce: ${rnonce}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up start of proof
|
||||
const withoutProof = `c=biws,r=${rnonce}`;
|
||||
const saltedPassword = HI(
|
||||
processedPassword,
|
||||
Buffer.from(salt, 'base64'),
|
||||
iterations,
|
||||
cryptoMethod
|
||||
);
|
||||
|
||||
const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key');
|
||||
const serverKey = HMAC(cryptoMethod, saltedPassword, 'Server Key');
|
||||
const storedKey = H(cryptoMethod, clientKey);
|
||||
const authMessage = [
|
||||
clientFirstMessageBare(username, nonce),
|
||||
payload.value().toString('base64'),
|
||||
withoutProof
|
||||
].join(',');
|
||||
|
||||
const clientSignature = HMAC(cryptoMethod, storedKey, authMessage);
|
||||
const clientProof = `p=${xor(clientKey, clientSignature)}`;
|
||||
const clientFinal = [withoutProof, clientProof].join(',');
|
||||
|
||||
const serverSignature = HMAC(cryptoMethod, serverKey, authMessage);
|
||||
const saslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: response.conversationId,
|
||||
payload: new Binary(Buffer.from(clientFinal))
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, saslContinueCmd, (_err, result) => {
|
||||
const err = resolveError(_err, result);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const r = result.result;
|
||||
const parsedResponse = parsePayload(r.payload.value());
|
||||
if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
|
||||
callback(new MongoError('Server returned an invalid signature'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!r || r.done !== false) {
|
||||
return callback(err, r);
|
||||
}
|
||||
|
||||
const retrySaslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: r.conversationId,
|
||||
payload: Buffer.alloc(0)
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, retrySaslContinueCmd, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function parsePayload(payload) {
|
||||
const dict = {};
|
||||
const parts = payload.split(',');
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const valueParts = parts[i].split('=');
|
||||
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');
|
||||
// Use node md5 generator
|
||||
var md5 = crypto.createHash('md5');
|
||||
// Generate keys used for authentication
|
||||
md5.update(username + ':mongo:' + password, 'utf8');
|
||||
function passwordDigest(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');
|
||||
}
|
||||
|
||||
const md5 = crypto.createHash('md5');
|
||||
md5.update(`${username}:mongo:${password}`, 'utf8');
|
||||
return md5.digest('hex');
|
||||
};
|
||||
}
|
||||
|
||||
// XOR two buffers
|
||||
function xor(a, b) {
|
||||
if (!Buffer.isBuffer(a)) a = Buffer.from(a);
|
||||
if (!Buffer.isBuffer(b)) b = Buffer.from(b);
|
||||
if (!Buffer.isBuffer(a)) {
|
||||
a = Buffer.from(a);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(b)) {
|
||||
b = Buffer.from(b);
|
||||
}
|
||||
|
||||
const length = Math.max(a.length, b.length);
|
||||
const res = [];
|
||||
|
||||
@@ -66,12 +269,12 @@ function HMAC(method, key, text) {
|
||||
.digest();
|
||||
}
|
||||
|
||||
var _hiCache = {};
|
||||
var _hiCacheCount = 0;
|
||||
var _hiCachePurge = function() {
|
||||
let _hiCache = {};
|
||||
let _hiCacheCount = 0;
|
||||
function _hiCachePurge() {
|
||||
_hiCache = {};
|
||||
_hiCacheCount = 0;
|
||||
};
|
||||
}
|
||||
|
||||
const hiLengthMap = {
|
||||
sha256: 32,
|
||||
@@ -121,205 +324,19 @@ function compareDigest(lhs, rhs) {
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ScramSHA authentication mechanism
|
||||
* @class
|
||||
* @extends AuthProvider
|
||||
*/
|
||||
class ScramSHA extends AuthProvider {
|
||||
constructor(bson, cryptoMethod) {
|
||||
super(bson);
|
||||
this.cryptoMethod = cryptoMethod || 'sha1';
|
||||
}
|
||||
function resolveError(err, result) {
|
||||
if (err) return err;
|
||||
|
||||
static _getError(err, r) {
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (r.$err || r.errmsg) {
|
||||
return new MongoError(r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_executeScram(sendAuthCommand, connection, credentials, nonce, callback) {
|
||||
let username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const db = credentials.source;
|
||||
|
||||
const cryptoMethod = this.cryptoMethod;
|
||||
let mechanism = 'SCRAM-SHA-1';
|
||||
let processedPassword;
|
||||
|
||||
if (cryptoMethod === 'sha256') {
|
||||
mechanism = 'SCRAM-SHA-256';
|
||||
|
||||
processedPassword = saslprep ? saslprep(password) : password;
|
||||
} else {
|
||||
try {
|
||||
processedPassword = passwordDigest(username, password);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the user
|
||||
username = username.replace('=', '=3D').replace(',', '=2C');
|
||||
|
||||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
||||
// Since the username is not sasl-prep-d, we need to do this here.
|
||||
const firstBare = Buffer.concat([
|
||||
Buffer.from('n=', 'utf8'),
|
||||
Buffer.from(username, 'utf8'),
|
||||
Buffer.from(',r=', 'utf8'),
|
||||
Buffer.from(nonce, 'utf8')
|
||||
]);
|
||||
|
||||
// Build command structure
|
||||
const saslStartCmd = {
|
||||
saslStart: 1,
|
||||
mechanism,
|
||||
payload: new Binary(Buffer.concat([Buffer.from('n,,', 'utf8'), firstBare])),
|
||||
autoAuthorize: 1
|
||||
};
|
||||
|
||||
// Write the commmand on the connection
|
||||
sendAuthCommand(connection, `${db}.$cmd`, saslStartCmd, (err, r) => {
|
||||
let tmpError = ScramSHA._getError(err, r);
|
||||
if (tmpError) {
|
||||
return callback(tmpError, null);
|
||||
}
|
||||
|
||||
const payload = Buffer.isBuffer(r.payload) ? new Binary(r.payload) : r.payload;
|
||||
const dict = parsePayload(payload.value());
|
||||
|
||||
const iterations = parseInt(dict.i, 10);
|
||||
if (iterations && iterations < 4096) {
|
||||
callback(new MongoError(`Server returned an invalid iteration count ${iterations}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
const salt = dict.s;
|
||||
const rnonce = dict.r;
|
||||
if (rnonce.startsWith('nonce')) {
|
||||
callback(new MongoError(`Server returned an invalid nonce: ${rnonce}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up start of proof
|
||||
const withoutProof = `c=biws,r=${rnonce}`;
|
||||
const saltedPassword = HI(
|
||||
processedPassword,
|
||||
Buffer.from(salt, 'base64'),
|
||||
iterations,
|
||||
cryptoMethod
|
||||
);
|
||||
|
||||
const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key');
|
||||
const serverKey = HMAC(cryptoMethod, saltedPassword, 'Server Key');
|
||||
const storedKey = H(cryptoMethod, clientKey);
|
||||
const authMessage = [firstBare, payload.value().toString('base64'), withoutProof].join(',');
|
||||
|
||||
const clientSignature = HMAC(cryptoMethod, storedKey, authMessage);
|
||||
const clientProof = `p=${xor(clientKey, clientSignature)}`;
|
||||
const clientFinal = [withoutProof, clientProof].join(',');
|
||||
|
||||
const serverSignature = HMAC(cryptoMethod, serverKey, authMessage);
|
||||
|
||||
const saslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: r.conversationId,
|
||||
payload: new Binary(Buffer.from(clientFinal))
|
||||
};
|
||||
|
||||
sendAuthCommand(connection, `${db}.$cmd`, saslContinueCmd, (err, r) => {
|
||||
if (err || (r && typeof r.ok === 'number' && r.ok === 0)) {
|
||||
callback(err, r);
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedResponse = parsePayload(r.payload.value());
|
||||
if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
|
||||
callback(new MongoError('Server returned an invalid signature'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!r || r.done !== false) {
|
||||
return callback(err, r);
|
||||
}
|
||||
|
||||
const retrySaslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: r.conversationId,
|
||||
payload: Buffer.alloc(0)
|
||||
};
|
||||
|
||||
sendAuthCommand(connection, `${db}.$cmd`, retrySaslContinueCmd, callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of authentication for a single connection
|
||||
* @override
|
||||
*/
|
||||
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
||||
// Create a random nonce
|
||||
crypto.randomBytes(24, (err, buff) => {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
return this._executeScram(
|
||||
sendAuthCommand,
|
||||
connection,
|
||||
credentials,
|
||||
buff.toString('base64'),
|
||||
callback
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate
|
||||
* @override
|
||||
* @method
|
||||
*/
|
||||
auth(sendAuthCommand, connections, credentials, callback) {
|
||||
this._checkSaslprep();
|
||||
super.auth(sendAuthCommand, connections, credentials, callback);
|
||||
}
|
||||
|
||||
_checkSaslprep() {
|
||||
const cryptoMethod = this.cryptoMethod;
|
||||
|
||||
if (cryptoMethod === 'sha256') {
|
||||
if (!saslprep) {
|
||||
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
|
||||
}
|
||||
}
|
||||
}
|
||||
const r = result.result;
|
||||
if (r.$err || r.errmsg) return new MongoError(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ScramSHA1 authentication mechanism
|
||||
* @class
|
||||
* @extends ScramSHA
|
||||
*/
|
||||
class ScramSHA1 extends ScramSHA {
|
||||
constructor(bson) {
|
||||
super(bson, 'sha1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ScramSHA256 authentication mechanism
|
||||
* @class
|
||||
* @extends ScramSHA
|
||||
*/
|
||||
class ScramSHA256 extends ScramSHA {
|
||||
constructor(bson) {
|
||||
super(bson, 'sha256');
|
||||
|
||||
41
node_modules/mongodb/lib/core/auth/x509.js
generated
vendored
41
node_modules/mongodb/lib/core/auth/x509.js
generated
vendored
@@ -1,26 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
/**
|
||||
* Creates a new X509 authentication mechanism
|
||||
* @class
|
||||
* @extends AuthProvider
|
||||
*/
|
||||
class X509 extends AuthProvider {
|
||||
/**
|
||||
* Implementation of authentication for a single connection
|
||||
* @override
|
||||
*/
|
||||
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
||||
const username = credentials.username;
|
||||
const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
|
||||
if (username) {
|
||||
command.user = username;
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const credentials = authContext.credentials;
|
||||
Object.assign(handshakeDoc, {
|
||||
speculativeAuthenticate: x509AuthenticateCommand(credentials)
|
||||
});
|
||||
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const response = authContext.response;
|
||||
if (response.speculativeAuthenticate) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
sendAuthCommand(connection, '$external.$cmd', command, callback);
|
||||
connection.command('$external.$cmd', x509AuthenticateCommand(credentials), callback);
|
||||
}
|
||||
}
|
||||
|
||||
function x509AuthenticateCommand(credentials) {
|
||||
const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
|
||||
if (credentials.username) {
|
||||
Object.apply(command, { user: credentials.username });
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
module.exports = X509;
|
||||
|
||||
258
node_modules/mongodb/lib/core/connection/connect.js
generated
vendored
258
node_modules/mongodb/lib/core/connection/connect.js
generated
vendored
@@ -2,10 +2,11 @@
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const Connection = require('./connection');
|
||||
const Query = require('./commands').Query;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const MongoNetworkError = require('../error').MongoNetworkError;
|
||||
const MongoNetworkTimeoutError = require('../error').MongoNetworkTimeoutError;
|
||||
const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders;
|
||||
const AuthContext = require('../auth/auth_provider').AuthContext;
|
||||
const WIRE_CONSTANTS = require('../wireprotocol/constants');
|
||||
const makeClientMetadata = require('../utils').makeClientMetadata;
|
||||
const MAX_SUPPORTED_WIRE_VERSION = WIRE_CONSTANTS.MAX_SUPPORTED_WIRE_VERSION;
|
||||
@@ -37,30 +38,7 @@ function connect(options, cancellationToken, callback) {
|
||||
}
|
||||
|
||||
function isModernConnectionType(conn) {
|
||||
return typeof conn.command === 'function';
|
||||
}
|
||||
|
||||
function getSaslSupportedMechs(options) {
|
||||
if (!(options && options.credentials)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const credentials = options.credentials;
|
||||
|
||||
// TODO: revisit whether or not items like `options.user` and `options.dbName` should be checked here
|
||||
const authMechanism = credentials.mechanism;
|
||||
const authSource = credentials.source || options.dbName || 'admin';
|
||||
const user = credentials.username || options.user;
|
||||
|
||||
if (typeof authMechanism === 'string' && authMechanism.toUpperCase() !== 'DEFAULT') {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { saslSupportedMechs: `${authSource}.${user}` };
|
||||
return !(conn instanceof Connection);
|
||||
}
|
||||
|
||||
function checkSupportedServer(ismaster, options) {
|
||||
@@ -97,77 +75,115 @@ function performInitialHandshake(conn, options, _callback) {
|
||||
_callback(err, ret);
|
||||
};
|
||||
|
||||
let compressors = [];
|
||||
if (options.compression && options.compression.compressors) {
|
||||
compressors = options.compression.compressors;
|
||||
const credentials = options.credentials;
|
||||
if (credentials) {
|
||||
if (!credentials.mechanism.match(/DEFAULT/i) && !AUTH_PROVIDERS[credentials.mechanism]) {
|
||||
callback(new MongoError(`authMechanism '${credentials.mechanism}' not supported`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const handshakeDoc = Object.assign(
|
||||
{
|
||||
ismaster: true,
|
||||
client: options.metadata || makeClientMetadata(options),
|
||||
compression: compressors
|
||||
},
|
||||
getSaslSupportedMechs(options)
|
||||
);
|
||||
|
||||
const handshakeOptions = Object.assign({}, options);
|
||||
|
||||
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
|
||||
if (options.connectTimeoutMS || options.connectionTimeout) {
|
||||
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
|
||||
}
|
||||
|
||||
const start = new Date().getTime();
|
||||
runCommand(conn, 'admin.$cmd', handshakeDoc, handshakeOptions, (err, ismaster) => {
|
||||
const authContext = new AuthContext(conn, credentials, options);
|
||||
prepareHandshakeDocument(authContext, (err, handshakeDoc) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (ismaster.ok === 0) {
|
||||
callback(new MongoError(ismaster));
|
||||
return;
|
||||
const handshakeOptions = Object.assign({}, options);
|
||||
if (options.connectTimeoutMS || options.connectionTimeout) {
|
||||
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
|
||||
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
|
||||
}
|
||||
|
||||
const supportedServerErr = checkSupportedServer(ismaster, options);
|
||||
if (supportedServerErr) {
|
||||
callback(supportedServerErr);
|
||||
return;
|
||||
}
|
||||
const start = new Date().getTime();
|
||||
conn.command('admin.$cmd', handshakeDoc, handshakeOptions, (err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isModernConnectionType(conn)) {
|
||||
// resolve compression
|
||||
if (ismaster.compression) {
|
||||
const agreedCompressors = compressors.filter(
|
||||
compressor => ismaster.compression.indexOf(compressor) !== -1
|
||||
);
|
||||
const response = result.result;
|
||||
if (response.ok === 0) {
|
||||
callback(new MongoError(response));
|
||||
return;
|
||||
}
|
||||
|
||||
if (agreedCompressors.length) {
|
||||
conn.agreedCompressor = agreedCompressors[0];
|
||||
}
|
||||
const supportedServerErr = checkSupportedServer(response, options);
|
||||
if (supportedServerErr) {
|
||||
callback(supportedServerErr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.compression && options.compression.zlibCompressionLevel) {
|
||||
conn.zlibCompressionLevel = options.compression.zlibCompressionLevel;
|
||||
if (!isModernConnectionType(conn)) {
|
||||
// resolve compression
|
||||
if (response.compression) {
|
||||
const agreedCompressors = handshakeDoc.compression.filter(
|
||||
compressor => response.compression.indexOf(compressor) !== -1
|
||||
);
|
||||
|
||||
if (agreedCompressors.length) {
|
||||
conn.agreedCompressor = agreedCompressors[0];
|
||||
}
|
||||
|
||||
if (options.compression && options.compression.zlibCompressionLevel) {
|
||||
conn.zlibCompressionLevel = options.compression.zlibCompressionLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This is metadata attached to the connection while porting away from
|
||||
// handshake being done in the `Server` class. Likely, it should be
|
||||
// relocated, or at very least restructured.
|
||||
conn.ismaster = ismaster;
|
||||
conn.lastIsMasterMS = new Date().getTime() - start;
|
||||
// NOTE: This is metadata attached to the connection while porting away from
|
||||
// handshake being done in the `Server` class. Likely, it should be
|
||||
// relocated, or at very least restructured.
|
||||
conn.ismaster = response;
|
||||
conn.lastIsMasterMS = new Date().getTime() - start;
|
||||
|
||||
const credentials = options.credentials;
|
||||
if (!ismaster.arbiterOnly && credentials) {
|
||||
credentials.resolveAuthMechanism(ismaster);
|
||||
authenticate(conn, credentials, callback);
|
||||
if (!response.arbiterOnly && credentials) {
|
||||
// store the response on auth context
|
||||
Object.assign(authContext, { response });
|
||||
|
||||
const resolvedCredentials = credentials.resolveAuthMechanism(response);
|
||||
const authProvider = AUTH_PROVIDERS[resolvedCredentials.mechanism];
|
||||
authProvider.auth(authContext, err => {
|
||||
if (err) return callback(err);
|
||||
callback(undefined, conn);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
callback(undefined, conn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function prepareHandshakeDocument(authContext, callback) {
|
||||
const options = authContext.options;
|
||||
const compressors =
|
||||
options.compression && options.compression.compressors ? options.compression.compressors : [];
|
||||
|
||||
const handshakeDoc = {
|
||||
ismaster: true,
|
||||
client: options.metadata || makeClientMetadata(options),
|
||||
compression: compressors
|
||||
};
|
||||
|
||||
const credentials = authContext.credentials;
|
||||
if (credentials) {
|
||||
if (credentials.mechanism.match(/DEFAULT/i) && credentials.username) {
|
||||
Object.assign(handshakeDoc, {
|
||||
saslSupportedMechs: `${credentials.source}.${credentials.username}`
|
||||
});
|
||||
|
||||
AUTH_PROVIDERS['scram-sha-256'].prepare(handshakeDoc, authContext, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(undefined, conn);
|
||||
});
|
||||
const authProvider = AUTH_PROVIDERS[credentials.mechanism];
|
||||
authProvider.prepare(handshakeDoc, authContext, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
|
||||
const LEGAL_SSL_SOCKET_OPTIONS = [
|
||||
@@ -239,7 +255,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
||||
const useSsl = typeof options.ssl === 'boolean' ? options.ssl : false;
|
||||
const keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
|
||||
let keepAliveInitialDelay =
|
||||
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
|
||||
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 120000;
|
||||
const noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
|
||||
const connectionTimeout =
|
||||
typeof options.connectionTimeout === 'number'
|
||||
@@ -318,92 +334,12 @@ function makeConnection(family, options, cancellationToken, _callback) {
|
||||
socket.once(connectEvent, connectHandler);
|
||||
}
|
||||
|
||||
const CONNECTION_ERROR_EVENTS = ['error', 'close', 'timeout', 'parseError'];
|
||||
function runCommand(conn, ns, command, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
|
||||
// are we using the new connection type? if so, no need to simulate a rpc `command` method
|
||||
if (isModernConnectionType(conn)) {
|
||||
conn.command(ns, command, options, (err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// NODE-2382: raw wire protocol messages, or command results should not be used anymore
|
||||
callback(undefined, result.result);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
|
||||
const bson = conn.options.bson;
|
||||
const query = new Query(bson, ns, command, {
|
||||
numberToSkip: 0,
|
||||
numberToReturn: 1
|
||||
});
|
||||
|
||||
const noop = () => {};
|
||||
function _callback(err, result) {
|
||||
callback(err, result);
|
||||
callback = noop;
|
||||
}
|
||||
|
||||
function errorHandler(err) {
|
||||
conn.resetSocketTimeout();
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.removeListener(eventName, errorHandler));
|
||||
conn.removeListener('message', messageHandler);
|
||||
|
||||
if (err == null) {
|
||||
err = new MongoError(`runCommand failed for connection to '${conn.address}'`);
|
||||
}
|
||||
|
||||
// ignore all future errors
|
||||
conn.on('error', noop);
|
||||
|
||||
_callback(err);
|
||||
}
|
||||
|
||||
function messageHandler(msg) {
|
||||
if (msg.responseTo !== query.requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
conn.resetSocketTimeout();
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.removeListener(eventName, errorHandler));
|
||||
conn.removeListener('message', messageHandler);
|
||||
|
||||
msg.parse({ promoteValues: true });
|
||||
_callback(undefined, msg.documents[0]);
|
||||
}
|
||||
|
||||
conn.setSocketTimeout(socketTimeout);
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.once(eventName, errorHandler));
|
||||
conn.on('message', messageHandler);
|
||||
conn.write(query.toBin());
|
||||
}
|
||||
|
||||
function authenticate(conn, credentials, callback) {
|
||||
const mechanism = credentials.mechanism;
|
||||
if (!AUTH_PROVIDERS[mechanism]) {
|
||||
callback(new MongoError(`authMechanism '${mechanism}' not supported`));
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = AUTH_PROVIDERS[mechanism];
|
||||
provider.auth(runCommand, [conn], credentials, err => {
|
||||
if (err) return callback(err);
|
||||
callback(undefined, conn);
|
||||
});
|
||||
}
|
||||
|
||||
function connectionFailureError(type, err) {
|
||||
switch (type) {
|
||||
case 'error':
|
||||
return new MongoNetworkError(err);
|
||||
case 'timeout':
|
||||
return new MongoNetworkError(`connection timed out`);
|
||||
return new MongoNetworkTimeoutError(`connection timed out`);
|
||||
case 'close':
|
||||
return new MongoNetworkError(`connection closed`);
|
||||
case 'cancel':
|
||||
|
||||
74
node_modules/mongodb/lib/core/connection/connection.js
generated
vendored
74
node_modules/mongodb/lib/core/connection/connection.js
generated
vendored
@@ -8,12 +8,15 @@ const decompress = require('../wireprotocol/compression').decompress;
|
||||
const Response = require('./commands').Response;
|
||||
const BinMsg = require('./msg').BinMsg;
|
||||
const MongoNetworkError = require('../error').MongoNetworkError;
|
||||
const MongoNetworkTimeoutError = require('../error').MongoNetworkTimeoutError;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const Logger = require('./logger');
|
||||
const OP_COMPRESSED = require('../wireprotocol/shared').opcodes.OP_COMPRESSED;
|
||||
const OP_MSG = require('../wireprotocol/shared').opcodes.OP_MSG;
|
||||
const MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
|
||||
const Buffer = require('safe-buffer').Buffer;
|
||||
const Query = require('./commands').Query;
|
||||
const CommandResult = require('./command_result');
|
||||
|
||||
let _id = 0;
|
||||
|
||||
@@ -64,7 +67,7 @@ class Connection extends EventEmitter {
|
||||
* @param {string} [options.host='localhost'] The host the socket is connected to
|
||||
* @param {number} [options.port=27017] The port used for the socket connection
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
|
||||
* @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
|
||||
* @param {boolean} [options.promoteLongs] Convert Long values from the db into Numbers if they fit into 53 bits
|
||||
@@ -94,7 +97,7 @@ class Connection extends EventEmitter {
|
||||
// These values are inspected directly in tests, but maybe not necessary to keep around
|
||||
this.keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
|
||||
this.keepAliveInitialDelay =
|
||||
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
|
||||
typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 120000;
|
||||
this.connectionTimeout =
|
||||
typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
|
||||
if (this.keepAliveInitialDelay > this.socketTimeout) {
|
||||
@@ -305,8 +308,71 @@ class Connection extends EventEmitter {
|
||||
if (this.destroyed) return false;
|
||||
return !this.socket.destroyed && this.socket.writable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
command(ns, command, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
|
||||
const conn = this;
|
||||
const socketTimeout =
|
||||
typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
|
||||
const bson = conn.options.bson;
|
||||
const query = new Query(bson, ns, command, {
|
||||
numberToSkip: 0,
|
||||
numberToReturn: 1
|
||||
});
|
||||
|
||||
const noop = () => {};
|
||||
function _callback(err, result) {
|
||||
callback(err, result);
|
||||
callback = noop;
|
||||
}
|
||||
|
||||
function errorHandler(err) {
|
||||
conn.resetSocketTimeout();
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.removeListener(eventName, errorHandler));
|
||||
conn.removeListener('message', messageHandler);
|
||||
|
||||
if (err == null) {
|
||||
err = new MongoError(`runCommand failed for connection to '${conn.address}'`);
|
||||
}
|
||||
|
||||
// ignore all future errors
|
||||
conn.on('error', noop);
|
||||
_callback(err);
|
||||
}
|
||||
|
||||
function messageHandler(msg) {
|
||||
if (msg.responseTo !== query.requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
conn.resetSocketTimeout();
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.removeListener(eventName, errorHandler));
|
||||
conn.removeListener('message', messageHandler);
|
||||
|
||||
msg.parse({ promoteValues: true });
|
||||
|
||||
const response = msg.documents[0];
|
||||
if (response.ok === 0 || response.$err || response.errmsg || response.code) {
|
||||
_callback(new MongoError(response));
|
||||
return;
|
||||
}
|
||||
|
||||
_callback(undefined, new CommandResult(response, this, msg));
|
||||
}
|
||||
|
||||
conn.setSocketTimeout(socketTimeout);
|
||||
CONNECTION_ERROR_EVENTS.forEach(eventName => conn.once(eventName, errorHandler));
|
||||
conn.on('message', messageHandler);
|
||||
conn.write(query.toBin());
|
||||
}
|
||||
}
|
||||
|
||||
const CONNECTION_ERROR_EVENTS = ['error', 'close', 'timeout', 'parseError'];
|
||||
|
||||
function deleteConnection(id) {
|
||||
// console.log("=== deleted connection " + id + " :: " + (connections[id] ? connections[id].port : ''))
|
||||
delete connections[id];
|
||||
@@ -352,7 +418,9 @@ function timeoutHandler(conn) {
|
||||
conn.timedOut = true;
|
||||
conn.emit(
|
||||
'timeout',
|
||||
new MongoNetworkError(`connection ${conn.id} to ${conn.address} timed out`),
|
||||
new MongoNetworkTimeoutError(`connection ${conn.id} to ${conn.address} timed out`, {
|
||||
beforeHandshake: conn.ismaster == null
|
||||
}),
|
||||
conn
|
||||
);
|
||||
};
|
||||
|
||||
4
node_modules/mongodb/lib/core/connection/pool.js
generated
vendored
4
node_modules/mongodb/lib/core/connection/pool.js
generated
vendored
@@ -60,7 +60,7 @@ var _id = 0;
|
||||
* @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
|
||||
* @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] Initial delay before TCP keep alive enabled
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
|
||||
* @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
|
||||
@@ -113,7 +113,7 @@ var Pool = function(topology, options) {
|
||||
connectionTimeout: 30000,
|
||||
socketTimeout: 360000,
|
||||
keepAlive: true,
|
||||
keepAliveInitialDelay: 300000,
|
||||
keepAliveInitialDelay: 120000,
|
||||
noDelay: true,
|
||||
// SSL Settings
|
||||
ssl: false,
|
||||
|
||||
67
node_modules/mongodb/lib/core/cursor.js
generated
vendored
67
node_modules/mongodb/lib/core/cursor.js
generated
vendored
@@ -464,50 +464,41 @@ class CoreCursor extends Readable {
|
||||
}
|
||||
|
||||
const result = r.message;
|
||||
if (result.queryFailure) {
|
||||
return done(new MongoError(result.documents[0]), null);
|
||||
}
|
||||
|
||||
// Check if we have a command cursor
|
||||
if (
|
||||
Array.isArray(result.documents) &&
|
||||
result.documents.length === 1 &&
|
||||
(!cursor.cmd.find || (cursor.cmd.find && cursor.cmd.virtual === false)) &&
|
||||
(typeof result.documents[0].cursor !== 'string' ||
|
||||
result.documents[0]['$err'] ||
|
||||
result.documents[0]['errmsg'] ||
|
||||
Array.isArray(result.documents[0].result))
|
||||
) {
|
||||
// We have an error document, return the error
|
||||
if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
|
||||
return done(new MongoError(result.documents[0]), null);
|
||||
if (Array.isArray(result.documents) && result.documents.length === 1) {
|
||||
const document = result.documents[0];
|
||||
|
||||
if (result.queryFailure) {
|
||||
return done(new MongoError(document), null);
|
||||
}
|
||||
|
||||
// We have a cursor document
|
||||
if (result.documents[0].cursor != null && typeof result.documents[0].cursor !== 'string') {
|
||||
const id = result.documents[0].cursor.id;
|
||||
// If we have a namespace change set the new namespace for getmores
|
||||
if (result.documents[0].cursor.ns) {
|
||||
cursor.ns = result.documents[0].cursor.ns;
|
||||
}
|
||||
// Promote id to long if needed
|
||||
cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
|
||||
cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
|
||||
cursor.cursorState.operationTime = result.documents[0].operationTime;
|
||||
|
||||
// If we have a firstBatch set it
|
||||
if (Array.isArray(result.documents[0].cursor.firstBatch)) {
|
||||
cursor.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
|
||||
// Check if we have a command cursor
|
||||
if (!cursor.cmd.find || (cursor.cmd.find && cursor.cmd.virtual === false)) {
|
||||
// We have an error document, return the error
|
||||
if (document.$err || document.errmsg) {
|
||||
return done(new MongoError(document), null);
|
||||
}
|
||||
|
||||
// Return after processing command cursor
|
||||
return done(null, result);
|
||||
}
|
||||
// We have a cursor document
|
||||
if (document.cursor != null && typeof document.cursor !== 'string') {
|
||||
const id = document.cursor.id;
|
||||
// If we have a namespace change set the new namespace for getmores
|
||||
if (document.cursor.ns) {
|
||||
cursor.ns = document.cursor.ns;
|
||||
}
|
||||
// Promote id to long if needed
|
||||
cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
|
||||
cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
|
||||
cursor.cursorState.operationTime = document.operationTime;
|
||||
|
||||
if (Array.isArray(result.documents[0].result)) {
|
||||
cursor.cursorState.documents = result.documents[0].result;
|
||||
cursor.cursorState.cursorId = Long.ZERO;
|
||||
return done(null, result);
|
||||
// If we have a firstBatch set it
|
||||
if (Array.isArray(document.cursor.firstBatch)) {
|
||||
cursor.cursorState.documents = document.cursor.firstBatch; //.reverse();
|
||||
}
|
||||
|
||||
// Return after processing command cursor
|
||||
return done(null, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
90
node_modules/mongodb/lib/core/error.js
generated
vendored
90
node_modules/mongodb/lib/core/error.js
generated
vendored
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const kErrorLabels = Symbol('errorLabels');
|
||||
|
||||
/**
|
||||
* Creates a new MongoError
|
||||
*
|
||||
@@ -18,8 +20,12 @@ class MongoError extends Error {
|
||||
super(message);
|
||||
} else {
|
||||
super(message.message || message.errmsg || message.$err || 'n/a');
|
||||
if (message.errorLabels) {
|
||||
this[kErrorLabels] = new Set(message.errorLabels);
|
||||
}
|
||||
|
||||
for (var name in message) {
|
||||
if (name === 'errmsg') {
|
||||
if (name === 'errorLabels' || name === 'errmsg') {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -57,8 +63,29 @@ class MongoError extends Error {
|
||||
* @returns {boolean} returns true if the error has the provided error label
|
||||
*/
|
||||
hasErrorLabel(label) {
|
||||
return this.errorLabels && this.errorLabels.indexOf(label) !== -1;
|
||||
if (this[kErrorLabels] == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this[kErrorLabels].has(label);
|
||||
}
|
||||
|
||||
addErrorLabel(label) {
|
||||
if (this[kErrorLabels] == null) {
|
||||
this[kErrorLabels] = new Set();
|
||||
}
|
||||
|
||||
this[kErrorLabels].add(label);
|
||||
}
|
||||
|
||||
get errorLabels() {
|
||||
return this[kErrorLabels] ? Array.from(this[kErrorLabels]) : [];
|
||||
}
|
||||
}
|
||||
|
||||
const kBeforeHandshake = Symbol('beforeHandshake');
|
||||
function isNetworkErrorBeforeHandshake(err) {
|
||||
return err[kBeforeHandshake] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,9 +98,28 @@ class MongoError extends Error {
|
||||
* @extends MongoError
|
||||
*/
|
||||
class MongoNetworkError extends MongoError {
|
||||
constructor(message) {
|
||||
constructor(message, options) {
|
||||
super(message);
|
||||
this.name = 'MongoNetworkError';
|
||||
|
||||
if (options && options.beforeHandshake === true) {
|
||||
this[kBeforeHandshake] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error indicating a network timeout occurred
|
||||
*
|
||||
* @param {Error|string|object} message The error message
|
||||
* @property {string} message The error message
|
||||
* @property {object} [options.beforeHandshake] Indicates the timeout happened before a connection handshake completed
|
||||
* @extends MongoError
|
||||
*/
|
||||
class MongoNetworkTimeoutError extends MongoNetworkError {
|
||||
constructor(message, options) {
|
||||
super(message, options);
|
||||
this.name = 'MongoNetworkTimeoutError';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +204,10 @@ class MongoWriteConcernError extends MongoError {
|
||||
super(message);
|
||||
this.name = 'MongoWriteConcernError';
|
||||
|
||||
if (result && Array.isArray(result.errorLabels)) {
|
||||
this[kErrorLabels] = new Set(result.errorLabels);
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
this.result = makeWriteConcernResultObject(result);
|
||||
}
|
||||
@@ -179,6 +229,32 @@ const RETRYABLE_ERROR_CODES = new Set([
|
||||
13436 // NotMasterOrSecondary
|
||||
]);
|
||||
|
||||
const RETRYABLE_WRITE_ERROR_CODES = new Set([
|
||||
11600, // InterruptedAtShutdown
|
||||
11602, // InterruptedDueToReplStateChange
|
||||
10107, // NotMaster
|
||||
13435, // NotMasterNoSlaveOk
|
||||
13436, // NotMasterOrSecondary
|
||||
189, // PrimarySteppedDown
|
||||
91, // ShutdownInProgress
|
||||
7, // HostNotFound
|
||||
6, // HostUnreachable
|
||||
89, // NetworkTimeout
|
||||
9001, // SocketException
|
||||
262 // ExceededTimeLimit
|
||||
]);
|
||||
|
||||
function isRetryableWriteError(error) {
|
||||
if (error instanceof MongoWriteConcernError) {
|
||||
return (
|
||||
RETRYABLE_WRITE_ERROR_CODES.has(error.code) ||
|
||||
RETRYABLE_WRITE_ERROR_CODES.has(error.result.code)
|
||||
);
|
||||
}
|
||||
|
||||
return RETRYABLE_WRITE_ERROR_CODES.has(error.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an error is something the driver should attempt to retry
|
||||
*
|
||||
@@ -259,13 +335,10 @@ function isSDAMUnrecoverableError(error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isNetworkTimeoutError(err) {
|
||||
return err instanceof MongoNetworkError && err.message.match(/timed out/);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MongoError,
|
||||
MongoNetworkError,
|
||||
MongoNetworkTimeoutError,
|
||||
MongoParseError,
|
||||
MongoTimeoutError,
|
||||
MongoServerSelectionError,
|
||||
@@ -273,5 +346,6 @@ module.exports = {
|
||||
isRetryableError,
|
||||
isSDAMUnrecoverableError,
|
||||
isNodeShuttingDownError,
|
||||
isNetworkTimeoutError
|
||||
isRetryableWriteError,
|
||||
isNetworkErrorBeforeHandshake
|
||||
};
|
||||
|
||||
8
node_modules/mongodb/lib/core/sdam/common.js
generated
vendored
8
node_modules/mongodb/lib/core/sdam/common.js
generated
vendored
@@ -28,6 +28,13 @@ const ServerType = {
|
||||
Unknown: 'Unknown'
|
||||
};
|
||||
|
||||
// helper to get a server's type that works for both legacy and unified topologies
|
||||
function serverType(server) {
|
||||
let description = server.s.description || server.s.serverDescription;
|
||||
if (description.topologyType === TopologyType.Single) return description.servers[0].type;
|
||||
return description.type;
|
||||
}
|
||||
|
||||
const TOPOLOGY_DEFAULTS = {
|
||||
useUnifiedTopology: true,
|
||||
localThresholdMS: 15,
|
||||
@@ -54,6 +61,7 @@ module.exports = {
|
||||
TOPOLOGY_DEFAULTS,
|
||||
TopologyType,
|
||||
ServerType,
|
||||
serverType,
|
||||
drainTimerQueue,
|
||||
clearAndRemoveTimerFrom
|
||||
};
|
||||
|
||||
327
node_modules/mongodb/lib/core/sdam/monitor.js
generated
vendored
327
node_modules/mongodb/lib/core/sdam/monitor.js
generated
vendored
@@ -6,7 +6,8 @@ const connect = require('../connection/connect');
|
||||
const Connection = require('../../cmap/connection').Connection;
|
||||
const common = require('./common');
|
||||
const makeStateMachine = require('../utils').makeStateMachine;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const MongoNetworkError = require('../error').MongoNetworkError;
|
||||
const BSON = require('../connection/utils').retrieveBSON();
|
||||
const makeInterruptableAsyncInterval = require('../../utils').makeInterruptableAsyncInterval;
|
||||
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
||||
const now = require('../../utils').now;
|
||||
@@ -20,13 +21,15 @@ const kServer = Symbol('server');
|
||||
const kMonitorId = Symbol('monitorId');
|
||||
const kConnection = Symbol('connection');
|
||||
const kCancellationToken = Symbol('cancellationToken');
|
||||
const kRTTPinger = Symbol('rttPinger');
|
||||
const kRoundTripTime = Symbol('roundTripTime');
|
||||
|
||||
const STATE_CLOSED = common.STATE_CLOSED;
|
||||
const STATE_CLOSING = common.STATE_CLOSING;
|
||||
const STATE_IDLE = 'idle';
|
||||
const STATE_MONITORING = 'monitoring';
|
||||
const stateTransition = makeStateMachine({
|
||||
[STATE_CLOSING]: [STATE_CLOSING, STATE_CLOSED],
|
||||
[STATE_CLOSING]: [STATE_CLOSING, STATE_IDLE, STATE_CLOSED],
|
||||
[STATE_CLOSED]: [STATE_CLOSED, STATE_MONITORING],
|
||||
[STATE_IDLE]: [STATE_IDLE, STATE_MONITORING, STATE_CLOSING],
|
||||
[STATE_MONITORING]: [STATE_MONITORING, STATE_IDLE, STATE_CLOSING]
|
||||
@@ -66,28 +69,29 @@ class Monitor extends EventEmitter {
|
||||
});
|
||||
|
||||
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
|
||||
const addressParts = server.description.address.split(':');
|
||||
this.connectOptions = Object.freeze(
|
||||
Object.assign(
|
||||
{
|
||||
id: '<monitor>',
|
||||
host: addressParts[0],
|
||||
port: parseInt(addressParts[1], 10),
|
||||
bson: server.s.bson,
|
||||
connectionType: Connection
|
||||
},
|
||||
server.s.options,
|
||||
this.options,
|
||||
const connectOptions = Object.assign(
|
||||
{
|
||||
id: '<monitor>',
|
||||
host: server.description.host,
|
||||
port: server.description.port,
|
||||
bson: server.s.bson,
|
||||
connectionType: Connection
|
||||
},
|
||||
server.s.options,
|
||||
this.options,
|
||||
|
||||
// force BSON serialization options
|
||||
{
|
||||
raw: false,
|
||||
promoteLongs: true,
|
||||
promoteValues: true,
|
||||
promoteBuffers: true
|
||||
}
|
||||
)
|
||||
// force BSON serialization options
|
||||
{
|
||||
raw: false,
|
||||
promoteLongs: true,
|
||||
promoteValues: true,
|
||||
promoteBuffers: true
|
||||
}
|
||||
);
|
||||
|
||||
// ensure no authentication is used for monitoring
|
||||
delete connectOptions.credentials;
|
||||
this.connectOptions = Object.freeze(connectOptions);
|
||||
}
|
||||
|
||||
connect() {
|
||||
@@ -113,88 +117,165 @@ class Monitor extends EventEmitter {
|
||||
this[kMonitorId].wake();
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (isInCloseState(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stateTransition(this, STATE_CLOSING);
|
||||
resetMonitorState(this);
|
||||
|
||||
// restart monitor
|
||||
stateTransition(this, STATE_IDLE);
|
||||
|
||||
// restart monitoring
|
||||
const heartbeatFrequencyMS = this.options.heartbeatFrequencyMS;
|
||||
const minHeartbeatFrequencyMS = this.options.minHeartbeatFrequencyMS;
|
||||
this[kMonitorId] = makeInterruptableAsyncInterval(monitorServer(this), {
|
||||
interval: heartbeatFrequencyMS,
|
||||
minInterval: minHeartbeatFrequencyMS
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
if (isInCloseState(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stateTransition(this, STATE_CLOSING);
|
||||
this[kCancellationToken].emit('cancel');
|
||||
if (this[kMonitorId]) {
|
||||
this[kMonitorId].stop();
|
||||
this[kMonitorId] = null;
|
||||
}
|
||||
|
||||
if (this[kConnection]) {
|
||||
this[kConnection].destroy({ force: true });
|
||||
}
|
||||
resetMonitorState(this);
|
||||
|
||||
// close monitor
|
||||
this.emit('close');
|
||||
stateTransition(this, STATE_CLOSED);
|
||||
}
|
||||
}
|
||||
|
||||
function checkServer(monitor, callback) {
|
||||
if (monitor[kConnection] && monitor[kConnection].closed) {
|
||||
monitor[kConnection] = undefined;
|
||||
function resetMonitorState(monitor) {
|
||||
stateTransition(monitor, STATE_CLOSING);
|
||||
if (monitor[kMonitorId]) {
|
||||
monitor[kMonitorId].stop();
|
||||
monitor[kMonitorId] = null;
|
||||
}
|
||||
|
||||
const start = now();
|
||||
if (monitor[kRTTPinger]) {
|
||||
monitor[kRTTPinger].close();
|
||||
monitor[kRTTPinger] = undefined;
|
||||
}
|
||||
|
||||
monitor[kCancellationToken].emit('cancel');
|
||||
if (monitor[kMonitorId]) {
|
||||
clearTimeout(monitor[kMonitorId]);
|
||||
monitor[kMonitorId] = undefined;
|
||||
}
|
||||
|
||||
if (monitor[kConnection]) {
|
||||
monitor[kConnection].destroy({ force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function checkServer(monitor, callback) {
|
||||
let start = now();
|
||||
monitor.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(monitor.address));
|
||||
|
||||
function failureHandler(err) {
|
||||
if (monitor[kConnection]) {
|
||||
monitor[kConnection].destroy({ force: true });
|
||||
monitor[kConnection] = undefined;
|
||||
}
|
||||
|
||||
monitor.emit(
|
||||
'serverHeartbeatFailed',
|
||||
new ServerHeartbeatFailedEvent(calculateDurationInMs(start), err, monitor.address)
|
||||
);
|
||||
|
||||
monitor.emit('resetServer', err);
|
||||
monitor.emit('resetConnectionPool');
|
||||
callback(err);
|
||||
}
|
||||
|
||||
function successHandler(isMaster) {
|
||||
monitor.emit(
|
||||
'serverHeartbeatSucceeded',
|
||||
new ServerHeartbeatSucceededEvent(calculateDurationInMs(start), isMaster, monitor.address)
|
||||
);
|
||||
|
||||
return callback(undefined, isMaster);
|
||||
}
|
||||
|
||||
if (monitor[kConnection] != null) {
|
||||
if (monitor[kConnection] != null && !monitor[kConnection].closed) {
|
||||
const connectTimeoutMS = monitor.options.connectTimeoutMS;
|
||||
monitor[kConnection].command(
|
||||
'admin.$cmd',
|
||||
{ ismaster: true },
|
||||
{ socketTimeout: connectTimeoutMS },
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
failureHandler(err);
|
||||
return;
|
||||
const maxAwaitTimeMS = monitor.options.heartbeatFrequencyMS;
|
||||
const topologyVersion = monitor[kServer].description.topologyVersion;
|
||||
const isAwaitable = topologyVersion != null;
|
||||
|
||||
const cmd = isAwaitable
|
||||
? { ismaster: true, maxAwaitTimeMS, topologyVersion: makeTopologyVersion(topologyVersion) }
|
||||
: { ismaster: true };
|
||||
|
||||
const options = isAwaitable
|
||||
? { socketTimeout: connectTimeoutMS + maxAwaitTimeMS, exhaustAllowed: true }
|
||||
: { socketTimeout: connectTimeoutMS };
|
||||
|
||||
if (isAwaitable && monitor[kRTTPinger] == null) {
|
||||
monitor[kRTTPinger] = new RTTPinger(monitor[kCancellationToken], monitor.connectOptions);
|
||||
}
|
||||
|
||||
monitor[kConnection].command('admin.$cmd', cmd, options, (err, result) => {
|
||||
if (err) {
|
||||
failureHandler(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const isMaster = result.result;
|
||||
const duration = isAwaitable
|
||||
? monitor[kRTTPinger].roundTripTime
|
||||
: calculateDurationInMs(start);
|
||||
|
||||
monitor.emit(
|
||||
'serverHeartbeatSucceeded',
|
||||
new ServerHeartbeatSucceededEvent(duration, isMaster, monitor.address)
|
||||
);
|
||||
|
||||
// if we are using the streaming protocol then we immediately issue another `started`
|
||||
// event, otherwise the "check" is complete and return to the main monitor loop
|
||||
if (isAwaitable && isMaster.topologyVersion) {
|
||||
monitor.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(monitor.address));
|
||||
start = now();
|
||||
} else {
|
||||
if (monitor[kRTTPinger]) {
|
||||
monitor[kRTTPinger].close();
|
||||
monitor[kRTTPinger] = undefined;
|
||||
}
|
||||
|
||||
successHandler(result.result);
|
||||
callback(undefined, isMaster);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// connecting does an implicit `ismaster`
|
||||
connect(monitor.connectOptions, monitor[kCancellationToken], (err, conn) => {
|
||||
if (conn && isInCloseState(monitor)) {
|
||||
conn.destroy({ force: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
monitor[kConnection] = undefined;
|
||||
|
||||
// we already reset the connection pool on network errors in all cases
|
||||
if (!(err instanceof MongoNetworkError)) {
|
||||
monitor.emit('resetConnectionPool');
|
||||
}
|
||||
|
||||
failureHandler(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInCloseState(monitor)) {
|
||||
conn.destroy({ force: true });
|
||||
failureHandler(new MongoError('monitor was destroyed'));
|
||||
return;
|
||||
}
|
||||
|
||||
monitor[kConnection] = conn;
|
||||
successHandler(conn.ismaster);
|
||||
monitor.emit(
|
||||
'serverHeartbeatSucceeded',
|
||||
new ServerHeartbeatSucceededEvent(
|
||||
calculateDurationInMs(start),
|
||||
conn.ismaster,
|
||||
monitor.address
|
||||
)
|
||||
);
|
||||
|
||||
callback(undefined, conn.ismaster);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,33 +293,113 @@ function monitorServer(monitor) {
|
||||
// TODO: the next line is a legacy event, remove in v4
|
||||
process.nextTick(() => monitor.emit('monitoring', monitor[kServer]));
|
||||
|
||||
checkServer(monitor, e0 => {
|
||||
if (e0 == null) {
|
||||
return done();
|
||||
}
|
||||
|
||||
// otherwise an error occured on initial discovery, also bail
|
||||
if (monitor[kServer].description.type === ServerType.Unknown) {
|
||||
monitor.emit('resetServer', e0);
|
||||
return done();
|
||||
}
|
||||
|
||||
// According to the SDAM specification's "Network error during server check" section, if
|
||||
// an ismaster call fails we reset the server's pool. If a server was once connected,
|
||||
// change its type to `Unknown` only after retrying once.
|
||||
monitor.emit('resetConnectionPool');
|
||||
|
||||
checkServer(monitor, e1 => {
|
||||
if (e1) {
|
||||
monitor.emit('resetServer', e1);
|
||||
checkServer(monitor, (err, isMaster) => {
|
||||
if (err) {
|
||||
// otherwise an error occured on initial discovery, also bail
|
||||
if (monitor[kServer].description.type === ServerType.Unknown) {
|
||||
monitor.emit('resetServer', err);
|
||||
return done();
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
// if the check indicates streaming is supported, immediately reschedule monitoring
|
||||
if (isMaster && isMaster.topologyVersion) {
|
||||
setTimeout(() => {
|
||||
if (!isInCloseState(monitor)) {
|
||||
monitor[kMonitorId].wake();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function makeTopologyVersion(tv) {
|
||||
return {
|
||||
processId: tv.processId,
|
||||
counter: BSON.Long.fromNumber(tv.counter)
|
||||
};
|
||||
}
|
||||
|
||||
class RTTPinger {
|
||||
constructor(cancellationToken, options) {
|
||||
this[kConnection] = null;
|
||||
this[kCancellationToken] = cancellationToken;
|
||||
this[kRoundTripTime] = 0;
|
||||
this.closed = false;
|
||||
|
||||
const heartbeatFrequencyMS = options.heartbeatFrequencyMS;
|
||||
this[kMonitorId] = setTimeout(() => measureRoundTripTime(this, options), heartbeatFrequencyMS);
|
||||
}
|
||||
|
||||
get roundTripTime() {
|
||||
return this[kRoundTripTime];
|
||||
}
|
||||
|
||||
close() {
|
||||
this.closed = true;
|
||||
|
||||
clearTimeout(this[kMonitorId]);
|
||||
this[kMonitorId] = undefined;
|
||||
|
||||
if (this[kConnection]) {
|
||||
this[kConnection].destroy({ force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function measureRoundTripTime(rttPinger, options) {
|
||||
const start = now();
|
||||
const cancellationToken = rttPinger[kCancellationToken];
|
||||
const heartbeatFrequencyMS = options.heartbeatFrequencyMS;
|
||||
if (rttPinger.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
function measureAndReschedule(conn) {
|
||||
if (rttPinger.closed) {
|
||||
conn.destroy({ force: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (rttPinger[kConnection] == null) {
|
||||
rttPinger[kConnection] = conn;
|
||||
}
|
||||
|
||||
rttPinger[kRoundTripTime] = calculateDurationInMs(start);
|
||||
rttPinger[kMonitorId] = setTimeout(
|
||||
() => measureRoundTripTime(rttPinger, options),
|
||||
heartbeatFrequencyMS
|
||||
);
|
||||
}
|
||||
|
||||
if (rttPinger[kConnection] == null) {
|
||||
connect(options, cancellationToken, (err, conn) => {
|
||||
if (err) {
|
||||
rttPinger[kConnection] = undefined;
|
||||
rttPinger[kRoundTripTime] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
measureAndReschedule(conn);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rttPinger[kConnection].command('admin.$cmd', { ismaster: 1 }, err => {
|
||||
if (err) {
|
||||
rttPinger[kConnection] = undefined;
|
||||
rttPinger[kRoundTripTime] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
measureAndReschedule();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Monitor
|
||||
};
|
||||
|
||||
99
node_modules/mongodb/lib/core/sdam/server.js
generated
vendored
99
node_modules/mongodb/lib/core/sdam/server.js
generated
vendored
@@ -7,17 +7,22 @@ const relayEvents = require('../utils').relayEvents;
|
||||
const BSON = require('../connection/utils').retrieveBSON();
|
||||
const Logger = require('../connection/logger');
|
||||
const ServerDescription = require('./server_description').ServerDescription;
|
||||
const compareTopologyVersion = require('./server_description').compareTopologyVersion;
|
||||
const ReadPreference = require('../topologies/read_preference');
|
||||
const Monitor = require('./monitor').Monitor;
|
||||
const MongoNetworkError = require('../error').MongoNetworkError;
|
||||
const MongoNetworkTimeoutError = require('../error').MongoNetworkTimeoutError;
|
||||
const collationNotSupported = require('../utils').collationNotSupported;
|
||||
const debugOptions = require('../connection/utils').debugOptions;
|
||||
const isSDAMUnrecoverableError = require('../error').isSDAMUnrecoverableError;
|
||||
const isNetworkTimeoutError = require('../error').isNetworkTimeoutError;
|
||||
const isRetryableWriteError = require('../error').isRetryableWriteError;
|
||||
const isNodeShuttingDownError = require('../error').isNodeShuttingDownError;
|
||||
const isNetworkErrorBeforeHandshake = require('../error').isNetworkErrorBeforeHandshake;
|
||||
const maxWireVersion = require('../utils').maxWireVersion;
|
||||
const makeStateMachine = require('../utils').makeStateMachine;
|
||||
const common = require('./common');
|
||||
const ServerType = common.ServerType;
|
||||
const isTransactionCommand = require('../transactions').isTransactionCommand;
|
||||
|
||||
// Used for filtering out fields for logging
|
||||
const DEBUG_FIELDS = [
|
||||
@@ -110,9 +115,8 @@ class Server extends EventEmitter {
|
||||
|
||||
// create the connection pool
|
||||
// NOTE: this used to happen in `connect`, we supported overriding pool options there
|
||||
const addressParts = this.description.address.split(':');
|
||||
const poolOptions = Object.assign(
|
||||
{ host: addressParts[0], port: parseInt(addressParts[1], 10), bson: this.s.bson },
|
||||
{ host: this.description.host, port: this.description.port, bson: this.s.bson },
|
||||
options
|
||||
);
|
||||
|
||||
@@ -278,7 +282,7 @@ class Server extends EventEmitter {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
conn.command(ns, cmd, options, makeOperationHandler(this, options, cb));
|
||||
conn.command(ns, cmd, options, makeOperationHandler(this, conn, cmd, options, cb));
|
||||
}, callback);
|
||||
}
|
||||
|
||||
@@ -302,7 +306,7 @@ class Server extends EventEmitter {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
conn.query(ns, cmd, cursorState, options, makeOperationHandler(this, options, cb));
|
||||
conn.query(ns, cmd, cursorState, options, makeOperationHandler(this, conn, cmd, options, cb));
|
||||
}, callback);
|
||||
}
|
||||
|
||||
@@ -326,7 +330,13 @@ class Server extends EventEmitter {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
conn.getMore(ns, cursorState, batchSize, options, makeOperationHandler(this, options, cb));
|
||||
conn.getMore(
|
||||
ns,
|
||||
cursorState,
|
||||
batchSize,
|
||||
options,
|
||||
makeOperationHandler(this, conn, null, options, cb)
|
||||
);
|
||||
}, callback);
|
||||
}
|
||||
|
||||
@@ -352,7 +362,7 @@ class Server extends EventEmitter {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
conn.killCursors(ns, cursorState, makeOperationHandler(this, null, cb));
|
||||
conn.killCursors(ns, cursorState, makeOperationHandler(this, conn, null, undefined, cb));
|
||||
}, callback);
|
||||
}
|
||||
|
||||
@@ -414,6 +424,14 @@ Object.defineProperty(Server.prototype, 'clusterTime', {
|
||||
}
|
||||
});
|
||||
|
||||
function supportsRetryableWrites(server) {
|
||||
return (
|
||||
server.description.maxWireVersion >= 6 &&
|
||||
server.description.logicalSessionTimeoutMinutes &&
|
||||
server.description.type !== ServerType.Standalone
|
||||
);
|
||||
}
|
||||
|
||||
function calculateRoundTripTime(oldRtt, duration) {
|
||||
if (oldRtt === -1) {
|
||||
return duration;
|
||||
@@ -448,6 +466,13 @@ function executeWriteOperation(args, options, callback) {
|
||||
callback(new MongoError(`server ${server.name} does not support collation`));
|
||||
return;
|
||||
}
|
||||
const unacknowledgedWrite = options.writeConcern && options.writeConcern.w === 0;
|
||||
if (unacknowledgedWrite || maxWireVersion(server) < 5) {
|
||||
if ((op === 'update' || op === 'remove') && ops.find(o => o.hint)) {
|
||||
callback(new MongoError(`servers < 3.4 do not support hint on ${op}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
server.s.pool.withConnection((err, conn, cb) => {
|
||||
if (err) {
|
||||
@@ -455,38 +480,78 @@ function executeWriteOperation(args, options, callback) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
conn[op](ns, ops, options, makeOperationHandler(server, options, cb));
|
||||
conn[op](ns, ops, options, makeOperationHandler(server, conn, ops, options, cb));
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function markServerUnknown(server, error) {
|
||||
if (error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError)) {
|
||||
server[kMonitor].reset();
|
||||
}
|
||||
|
||||
server.emit(
|
||||
'descriptionReceived',
|
||||
new ServerDescription(server.description.address, null, { error })
|
||||
new ServerDescription(server.description.address, null, {
|
||||
error,
|
||||
topologyVersion:
|
||||
error && error.topologyVersion ? error.topologyVersion : server.description.topologyVersion
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function makeOperationHandler(server, options, callback) {
|
||||
function connectionIsStale(pool, connection) {
|
||||
return connection.generation !== pool.generation;
|
||||
}
|
||||
|
||||
function shouldHandleStateChangeError(server, err) {
|
||||
const etv = err.topologyVersion;
|
||||
const stv = server.description.topologyVersion;
|
||||
|
||||
return compareTopologyVersion(stv, etv) < 0;
|
||||
}
|
||||
|
||||
function inActiveTransaction(session, cmd) {
|
||||
return session && session.inTransaction() && !isTransactionCommand(cmd);
|
||||
}
|
||||
|
||||
function makeOperationHandler(server, connection, cmd, options, callback) {
|
||||
const session = options && options.session;
|
||||
|
||||
return function handleOperationResult(err, result) {
|
||||
if (err) {
|
||||
if (err && !connectionIsStale(server.s.pool, connection)) {
|
||||
if (err instanceof MongoNetworkError) {
|
||||
if (session && !session.hasEnded) {
|
||||
session.serverSession.isDirty = true;
|
||||
}
|
||||
|
||||
if (!isNetworkTimeoutError(err)) {
|
||||
if (supportsRetryableWrites(server) && !inActiveTransaction(session, cmd)) {
|
||||
err.addErrorLabel('RetryableWriteError');
|
||||
}
|
||||
|
||||
if (!(err instanceof MongoNetworkTimeoutError) || isNetworkErrorBeforeHandshake(err)) {
|
||||
markServerUnknown(server, err);
|
||||
server.s.pool.clear();
|
||||
}
|
||||
} else if (isSDAMUnrecoverableError(err)) {
|
||||
if (maxWireVersion(server) <= 7 || isNodeShuttingDownError(err)) {
|
||||
server.s.pool.clear();
|
||||
} else {
|
||||
// if pre-4.4 server, then add error label if its a retryable write error
|
||||
if (
|
||||
maxWireVersion(server) < 9 &&
|
||||
isRetryableWriteError(err) &&
|
||||
!inActiveTransaction(session, cmd)
|
||||
) {
|
||||
err.addErrorLabel('RetryableWriteError');
|
||||
}
|
||||
|
||||
markServerUnknown(server, err);
|
||||
process.nextTick(() => server.requestCheck());
|
||||
if (isSDAMUnrecoverableError(err)) {
|
||||
if (shouldHandleStateChangeError(server, err)) {
|
||||
if (maxWireVersion(server) <= 7 || isNodeShuttingDownError(err)) {
|
||||
server.s.pool.clear();
|
||||
}
|
||||
|
||||
markServerUnknown(server, err);
|
||||
process.nextTick(() => server.requestCheck());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
49
node_modules/mongodb/lib/core/sdam/server_description.js
generated
vendored
49
node_modules/mongodb/lib/core/sdam/server_description.js
generated
vendored
@@ -53,6 +53,8 @@ class ServerDescription {
|
||||
* @param {Object} [ismaster] An optional ismaster response for this server
|
||||
* @param {Object} [options] Optional settings
|
||||
* @param {Number} [options.roundTripTime] The round trip time to ping this server (in ms)
|
||||
* @param {Error} [options.error] An Error used for better reporting debugging
|
||||
* @param {any} [options.topologyVersion] The topologyVersion
|
||||
*/
|
||||
constructor(address, ismaster, options) {
|
||||
options = options || {};
|
||||
@@ -75,6 +77,7 @@ class ServerDescription {
|
||||
this.lastWriteDate = ismaster.lastWrite ? ismaster.lastWrite.lastWriteDate : null;
|
||||
this.opTime = ismaster.lastWrite ? ismaster.lastWrite.opTime : null;
|
||||
this.type = parseServerType(ismaster);
|
||||
this.topologyVersion = options.topologyVersion || ismaster.topologyVersion;
|
||||
|
||||
// direct mappings
|
||||
ISMASTER_FIELDS.forEach(field => {
|
||||
@@ -113,6 +116,16 @@ class ServerDescription {
|
||||
return WRITABLE_SERVER_TYPES.has(this.type);
|
||||
}
|
||||
|
||||
get host() {
|
||||
const chopLength = `:${this.port}`.length;
|
||||
return this.address.slice(0, -chopLength);
|
||||
}
|
||||
|
||||
get port() {
|
||||
const port = this.address.split(':').pop();
|
||||
return port ? Number.parseInt(port, 10) : port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if another `ServerDescription` is equal to this one per the rules defined
|
||||
* in the {@link https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#serverdescription|SDAM spec}
|
||||
@@ -121,6 +134,10 @@ class ServerDescription {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
equals(other) {
|
||||
const topologyVersionsEqual =
|
||||
this.topologyVersion === other.topologyVersion ||
|
||||
compareTopologyVersion(this.topologyVersion, other.topologyVersion) === 0;
|
||||
|
||||
return (
|
||||
other != null &&
|
||||
errorStrictEqual(this.error, other.error) &&
|
||||
@@ -135,7 +152,8 @@ class ServerDescription {
|
||||
? other.electionId && this.electionId.equals(other.electionId)
|
||||
: this.electionId === other.electionId) &&
|
||||
this.primary === other.primary &&
|
||||
this.logicalSessionTimeoutMinutes === other.logicalSessionTimeoutMinutes
|
||||
this.logicalSessionTimeoutMinutes === other.logicalSessionTimeoutMinutes &&
|
||||
topologyVersionsEqual
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -176,7 +194,34 @@ function parseServerType(ismaster) {
|
||||
return ServerType.Standalone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two topology versions.
|
||||
*
|
||||
* @param {object} lhs
|
||||
* @param {object} rhs
|
||||
* @returns A negative number if `lhs` is older than `rhs`; positive if `lhs` is newer than `rhs`; 0 if they are equivalent.
|
||||
*/
|
||||
function compareTopologyVersion(lhs, rhs) {
|
||||
if (lhs == null || rhs == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lhs.processId.equals(rhs.processId)) {
|
||||
// TODO: handle counters as Longs
|
||||
if (lhs.counter === rhs.counter) {
|
||||
return 0;
|
||||
} else if (lhs.counter < rhs.counter) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ServerDescription,
|
||||
parseServerType
|
||||
parseServerType,
|
||||
compareTopologyVersion
|
||||
};
|
||||
|
||||
75
node_modules/mongodb/lib/core/sdam/topology.js
generated
vendored
75
node_modules/mongodb/lib/core/sdam/topology.js
generated
vendored
@@ -14,7 +14,6 @@ const CoreCursor = require('../cursor').CoreCursor;
|
||||
const deprecate = require('util').deprecate;
|
||||
const BSON = require('../connection/utils').retrieveBSON();
|
||||
const createCompressionInfo = require('../topologies/shared').createCompressionInfo;
|
||||
const isRetryableError = require('../error').isRetryableError;
|
||||
const ClientSession = require('../sessions').ClientSession;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const MongoServerSelectionError = require('../error').MongoServerSelectionError;
|
||||
@@ -27,6 +26,7 @@ const emitDeprecationWarning = require('../../utils').emitDeprecationWarning;
|
||||
const ServerSessionPool = require('../sessions').ServerSessionPool;
|
||||
const makeClientMetadata = require('../utils').makeClientMetadata;
|
||||
const CMAP_EVENT_NAMES = require('../../cmap/events').CMAP_EVENT_NAMES;
|
||||
const compareTopologyVersion = require('./server_description').compareTopologyVersion;
|
||||
|
||||
const common = require('./common');
|
||||
const drainTimerQueue = common.drainTimerQueue;
|
||||
@@ -275,9 +275,9 @@ class Topology extends EventEmitter {
|
||||
// connect all known servers, then attempt server selection to connect
|
||||
connectServers(this, Array.from(this.s.description.servers.values()));
|
||||
|
||||
translateReadPreference(options);
|
||||
ReadPreference.translate(options);
|
||||
const readPreference = options.readPreference || ReadPreference.primary;
|
||||
this.selectServer(readPreferenceServerSelector(readPreference), options, err => {
|
||||
const connectHandler = err => {
|
||||
if (err) {
|
||||
this.close();
|
||||
|
||||
@@ -295,7 +295,15 @@ class Topology extends EventEmitter {
|
||||
this.emit('connect', this);
|
||||
|
||||
if (typeof callback === 'function') callback(err, this);
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: NODE-2471
|
||||
if (this.s.credentials) {
|
||||
this.command('admin.$cmd', { ping: 1 }, { readPreference }, connectHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectServer(readPreferenceServerSelector(readPreference), options, connectHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +389,7 @@ class Topology extends EventEmitter {
|
||||
} else if (typeof selector === 'string') {
|
||||
readPreference = new ReadPreference(selector);
|
||||
} else {
|
||||
translateReadPreference(options);
|
||||
ReadPreference.translate(options);
|
||||
readPreference = options.readPreference || ReadPreference.primary;
|
||||
}
|
||||
|
||||
@@ -505,6 +513,11 @@ class Topology extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore this server update if its from an outdated topologyVersion
|
||||
if (isStaleServerDescription(this.s.description, serverDescription)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// these will be used for monitoring events later
|
||||
const previousTopologyDescription = this.s.description;
|
||||
const previousServerDescription = this.s.description.servers.get(serverDescription.address);
|
||||
@@ -647,7 +660,7 @@ class Topology extends EventEmitter {
|
||||
(callback = options), (options = {}), (options = options || {});
|
||||
}
|
||||
|
||||
translateReadPreference(options);
|
||||
ReadPreference.translate(options);
|
||||
const readPreference = options.readPreference || ReadPreference.primary;
|
||||
|
||||
this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
|
||||
@@ -666,7 +679,7 @@ class Topology extends EventEmitter {
|
||||
|
||||
const cb = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err)) {
|
||||
if (!shouldRetryOperation(err)) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
@@ -708,7 +721,7 @@ class Topology extends EventEmitter {
|
||||
options = options || {};
|
||||
const topology = options.topology || this;
|
||||
const CursorClass = options.cursorFactory || this.s.Cursor;
|
||||
translateReadPreference(options);
|
||||
ReadPreference.translate(options);
|
||||
|
||||
return new CursorClass(topology, ns, cmd, options);
|
||||
}
|
||||
@@ -771,6 +784,16 @@ function isWriteCommand(command) {
|
||||
return RETRYABLE_WRITE_OPERATIONS.some(op => command[op]);
|
||||
}
|
||||
|
||||
function isStaleServerDescription(topologyDescription, incomingServerDescription) {
|
||||
const currentServerDescription = topologyDescription.servers.get(
|
||||
incomingServerDescription.address
|
||||
);
|
||||
const currentTopologyVersion = currentServerDescription.topologyVersion;
|
||||
return (
|
||||
compareTopologyVersion(currentTopologyVersion, incomingServerDescription.topologyVersion) > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a server, and removes all event listeners from the instance
|
||||
*
|
||||
@@ -806,10 +829,16 @@ function parseStringSeedlist(seedlist) {
|
||||
}
|
||||
|
||||
function topologyTypeFromSeedlist(seedlist, options) {
|
||||
if (options.directConnection) {
|
||||
return TopologyType.Single;
|
||||
}
|
||||
|
||||
const replicaSet = options.replicaSet || options.setName || options.rs_name;
|
||||
if (seedlist.length === 1 && !replicaSet) return TopologyType.Single;
|
||||
if (replicaSet) return TopologyType.ReplicaSetNoPrimary;
|
||||
return TopologyType.Unknown;
|
||||
if (replicaSet == null) {
|
||||
return TopologyType.Unknown;
|
||||
}
|
||||
|
||||
return TopologyType.ReplicaSetNoPrimary;
|
||||
}
|
||||
|
||||
function randomSelection(array) {
|
||||
@@ -911,7 +940,7 @@ function executeWriteOperation(args, options, callback) {
|
||||
|
||||
const handler = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err)) {
|
||||
if (!shouldRetryOperation(err)) {
|
||||
err = getMMAPError(err);
|
||||
return callback(err);
|
||||
}
|
||||
@@ -939,26 +968,8 @@ function executeWriteOperation(args, options, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function translateReadPreference(options) {
|
||||
if (options.readPreference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let r = options.readPreference;
|
||||
if (typeof r === 'string') {
|
||||
options.readPreference = new ReadPreference(r);
|
||||
} else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
|
||||
const mode = r.mode || r.preference;
|
||||
if (mode && typeof mode === 'string') {
|
||||
options.readPreference = new ReadPreference(mode, r.tags, {
|
||||
maxStalenessSeconds: r.maxStalenessSeconds
|
||||
});
|
||||
}
|
||||
} else if (!(r instanceof ReadPreference)) {
|
||||
throw new TypeError('Invalid read preference: ' + r);
|
||||
}
|
||||
|
||||
return options;
|
||||
function shouldRetryOperation(err) {
|
||||
return err instanceof MongoError && err.hasErrorLabel('RetryableWriteError');
|
||||
}
|
||||
|
||||
function srvPollingHandler(topology) {
|
||||
|
||||
24
node_modules/mongodb/lib/core/sdam/topology_description.js
generated
vendored
24
node_modules/mongodb/lib/core/sdam/topology_description.js
generated
vendored
@@ -132,6 +132,10 @@ class TopologyDescription {
|
||||
let maxElectionId = this.maxElectionId;
|
||||
let commonWireVersion = this.commonWireVersion;
|
||||
|
||||
if (serverDescription.setName && setName && serverDescription.setName !== setName) {
|
||||
serverDescription = new ServerDescription(address, null);
|
||||
}
|
||||
|
||||
const serverType = serverDescription.type;
|
||||
let serverDescriptions = new Map(this.servers);
|
||||
|
||||
@@ -161,7 +165,7 @@ class TopologyDescription {
|
||||
}
|
||||
|
||||
if (topologyType === TopologyType.Unknown) {
|
||||
if (serverType === ServerType.Standalone) {
|
||||
if (serverType === ServerType.Standalone && this.servers.size !== 1) {
|
||||
serverDescriptions.delete(address);
|
||||
} else {
|
||||
topologyType = topologyTypeForServerType(serverType);
|
||||
@@ -274,8 +278,22 @@ class TopologyDescription {
|
||||
}
|
||||
|
||||
function topologyTypeForServerType(serverType) {
|
||||
if (serverType === ServerType.Mongos) return TopologyType.Sharded;
|
||||
if (serverType === ServerType.RSPrimary) return TopologyType.ReplicaSetWithPrimary;
|
||||
if (serverType === ServerType.Standalone) {
|
||||
return TopologyType.Single;
|
||||
}
|
||||
|
||||
if (serverType === ServerType.Mongos) {
|
||||
return TopologyType.Sharded;
|
||||
}
|
||||
|
||||
if (serverType === ServerType.RSPrimary) {
|
||||
return TopologyType.ReplicaSetWithPrimary;
|
||||
}
|
||||
|
||||
if (serverType === ServerType.RSGhost || serverType === ServerType.Unknown) {
|
||||
return TopologyType.Unknown;
|
||||
}
|
||||
|
||||
return TopologyType.ReplicaSetNoPrimary;
|
||||
}
|
||||
|
||||
|
||||
30
node_modules/mongodb/lib/core/sessions.js
generated
vendored
30
node_modules/mongodb/lib/core/sessions.js
generated
vendored
@@ -386,10 +386,7 @@ function attemptTransaction(session, startTime, fn, options) {
|
||||
}
|
||||
|
||||
if (isMaxTimeMSExpiredError(err)) {
|
||||
if (err.errorLabels == null) {
|
||||
err.errorLabels = [];
|
||||
}
|
||||
err.errorLabels.push('UnknownTransactionCommitResult');
|
||||
err.addErrorLabel('UnknownTransactionCommitResult');
|
||||
}
|
||||
|
||||
throw err;
|
||||
@@ -488,17 +485,8 @@ function endTransaction(session, commandName, callback) {
|
||||
isRetryableError(e) ||
|
||||
isMaxTimeMSExpiredError(e))
|
||||
) {
|
||||
if (e.errorLabels) {
|
||||
const idx = e.errorLabels.indexOf('TransientTransactionError');
|
||||
if (idx !== -1) {
|
||||
e.errorLabels.splice(idx, 1);
|
||||
}
|
||||
} else {
|
||||
e.errorLabels = [];
|
||||
}
|
||||
|
||||
if (isUnknownTransactionCommitResult(e)) {
|
||||
e.errorLabels.push('UnknownTransactionCommitResult');
|
||||
e.addErrorLabel('UnknownTransactionCommitResult');
|
||||
|
||||
// per txns spec, must unpin session in this case
|
||||
session.transaction.unpinServer();
|
||||
@@ -685,7 +673,12 @@ function commandSupportsReadConcern(command, options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command.mapReduce && options.out && (options.out.inline === 1 || options.out === 'inline')) {
|
||||
if (
|
||||
command.mapReduce &&
|
||||
options &&
|
||||
options.out &&
|
||||
(options.out.inline === 1 || options.out === 'inline')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -708,6 +701,11 @@ function applySession(session, command, options) {
|
||||
return new MongoError('Cannot use a session that has ended');
|
||||
}
|
||||
|
||||
// SPEC-1019: silently ignore explicit session with unacknowledged write for backwards compatibility
|
||||
if (options && options.writeConcern && options.writeConcern.w === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverSession = session.serverSession;
|
||||
serverSession.lastUse = now();
|
||||
command.lsid = serverSession.id;
|
||||
@@ -715,7 +713,7 @@ function applySession(session, command, options) {
|
||||
// first apply non-transaction-specific sessions data
|
||||
const inTransaction = session.inTransaction() || isTransactionCommand(command);
|
||||
const isRetryableWrite = options.willRetryWrite;
|
||||
const shouldApplyReadConcern = commandSupportsReadConcern(command);
|
||||
const shouldApplyReadConcern = commandSupportsReadConcern(command, options);
|
||||
|
||||
if (serverSession.txnNumber && (isRetryableWrite || inTransaction)) {
|
||||
command.txnNumber = BSON.Long.fromNumber(serverSession.txnNumber);
|
||||
|
||||
24
node_modules/mongodb/lib/core/topologies/mongos.js
generated
vendored
24
node_modules/mongodb/lib/core/topologies/mongos.js
generated
vendored
@@ -13,10 +13,10 @@ const cloneOptions = require('./shared').cloneOptions;
|
||||
const SessionMixins = require('./shared').SessionMixins;
|
||||
const isRetryableWritesSupported = require('./shared').isRetryableWritesSupported;
|
||||
const relayEvents = require('../utils').relayEvents;
|
||||
const isRetryableError = require('../error').isRetryableError;
|
||||
const BSON = retrieveBSON();
|
||||
const getMMAPError = require('./shared').getMMAPError;
|
||||
const makeClientMetadata = require('../utils').makeClientMetadata;
|
||||
const legacyIsRetryableWriteError = require('./shared').legacyIsRetryableWriteError;
|
||||
|
||||
/**
|
||||
* @fileOverview The **Mongos** class is a class that represents a Mongos Proxy topology and is
|
||||
@@ -71,7 +71,7 @@ var handlers = ['connect', 'close', 'error', 'timeout', 'parseError'];
|
||||
* @param {Cursor} [options.cursorFactory=Cursor] The cursor factory class used for all query cursors
|
||||
* @param {number} [options.size=5] Server connection pool size
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=0] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.localThresholdMS=15] Cutoff latency point in MS for MongoS proxy selection
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {number} [options.connectionTimeout=1000] TCP Connection timeout setting
|
||||
@@ -113,6 +113,18 @@ var Mongos = function(seedlist, options) {
|
||||
// Get replSet Id
|
||||
this.id = id++;
|
||||
|
||||
// deduplicate seedlist
|
||||
if (Array.isArray(seedlist)) {
|
||||
seedlist = seedlist.reduce((seeds, seed) => {
|
||||
if (seeds.find(s => s.host === seed.host && s.port === seed.port)) {
|
||||
return seeds;
|
||||
}
|
||||
|
||||
seeds.push(seed);
|
||||
return seeds;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Internal state
|
||||
this.s = {
|
||||
options: Object.assign({ metadata: makeClientMetadata(options) }, options),
|
||||
@@ -911,7 +923,7 @@ function executeWriteOperation(args, options, callback) {
|
||||
|
||||
const handler = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err) || !willRetryWrite) {
|
||||
if (!legacyIsRetryableWriteError(err, self) || !willRetryWrite) {
|
||||
err = getMMAPError(err);
|
||||
return callback(err);
|
||||
}
|
||||
@@ -1107,7 +1119,7 @@ Mongos.prototype.command = function(ns, cmd, options, callback) {
|
||||
|
||||
const cb = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err)) {
|
||||
if (!legacyIsRetryableWriteError(err, self)) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
@@ -1121,8 +1133,8 @@ Mongos.prototype.command = function(ns, cmd, options, callback) {
|
||||
|
||||
// increment and assign txnNumber
|
||||
if (willRetryWrite) {
|
||||
options.session.incrementTransactionNumber();
|
||||
options.willRetryWrite = willRetryWrite;
|
||||
clonedOptions.session.incrementTransactionNumber();
|
||||
clonedOptions.willRetryWrite = willRetryWrite;
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
|
||||
78
node_modules/mongodb/lib/core/topologies/read_preference.js
generated
vendored
78
node_modules/mongodb/lib/core/topologies/read_preference.js
generated
vendored
@@ -8,6 +8,8 @@
|
||||
* @param {array} tags The tags object
|
||||
* @param {object} [options] Additional read preference options
|
||||
* @param {number} [options.maxStalenessSeconds] Max secondary read staleness in seconds, Minimum value is 90 seconds.
|
||||
* @param {object} [options.hedge] Server mode in which the same query is dispatched in parallel to multiple replica set members.
|
||||
* @param {boolean} [options.hedge.enabled] Explicitly enable or disable hedged reads.
|
||||
* @see https://docs.mongodb.com/manual/core/read-preference/
|
||||
* @return {ReadPreference}
|
||||
*/
|
||||
@@ -22,7 +24,10 @@ const ReadPreference = function(mode, tags, options) {
|
||||
'ReadPreference tags must be an array, this will change in the next major version'
|
||||
);
|
||||
|
||||
if (typeof tags.maxStalenessSeconds !== 'undefined') {
|
||||
const tagsHasMaxStalenessSeconds = typeof tags.maxStalenessSeconds !== 'undefined';
|
||||
const tagsHasHedge = typeof tags.hedge !== 'undefined';
|
||||
const tagsHasOptions = tagsHasMaxStalenessSeconds || tagsHasHedge;
|
||||
if (tagsHasOptions) {
|
||||
// this is likely an options object
|
||||
options = tags;
|
||||
tags = undefined;
|
||||
@@ -33,6 +38,7 @@ const ReadPreference = function(mode, tags, options) {
|
||||
|
||||
this.mode = mode;
|
||||
this.tags = tags;
|
||||
this.hedge = options && options.hedge;
|
||||
|
||||
options = options || {};
|
||||
if (options.maxStalenessSeconds != null) {
|
||||
@@ -55,6 +61,10 @@ const ReadPreference = function(mode, tags, options) {
|
||||
if (this.maxStalenessSeconds) {
|
||||
throw new TypeError('Primary read preference cannot be combined with maxStalenessSeconds');
|
||||
}
|
||||
|
||||
if (this.hedge) {
|
||||
throw new TypeError('Primary read preference cannot be combined with hedge');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,20 +101,19 @@ const VALID_MODES = [
|
||||
* @return {ReadPreference}
|
||||
*/
|
||||
ReadPreference.fromOptions = function(options) {
|
||||
if (!options) return null;
|
||||
const readPreference = options.readPreference;
|
||||
if (!readPreference) return null;
|
||||
const readPreferenceTags = options.readPreferenceTags;
|
||||
|
||||
if (readPreference == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const maxStalenessSeconds = options.maxStalenessSeconds;
|
||||
if (typeof readPreference === 'string') {
|
||||
return new ReadPreference(readPreference, readPreferenceTags);
|
||||
} else if (!(readPreference instanceof ReadPreference) && typeof readPreference === 'object') {
|
||||
const mode = readPreference.mode || readPreference.preference;
|
||||
if (mode && typeof mode === 'string') {
|
||||
return new ReadPreference(mode, readPreference.tags, {
|
||||
maxStalenessSeconds: readPreference.maxStalenessSeconds
|
||||
maxStalenessSeconds: readPreference.maxStalenessSeconds || maxStalenessSeconds,
|
||||
hedge: readPreference.hedge
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -112,6 +121,60 @@ ReadPreference.fromOptions = function(options) {
|
||||
return readPreference;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a read preference based on well-defined inheritance rules. This method will not only
|
||||
* determine the read preference (if there is one), but will also ensure the returned value is a
|
||||
* properly constructed instance of `ReadPreference`.
|
||||
*
|
||||
* @param {Collection|Db|MongoClient} parent The parent of the operation on which to determine the read
|
||||
* preference, used for determining the inherited read preference.
|
||||
* @param {object} options The options passed into the method, potentially containing a read preference
|
||||
* @returns {(ReadPreference|null)} The resolved read preference
|
||||
*/
|
||||
ReadPreference.resolve = function(parent, options) {
|
||||
options = options || {};
|
||||
const session = options.session;
|
||||
|
||||
const inheritedReadPreference = parent && parent.readPreference;
|
||||
|
||||
let readPreference;
|
||||
if (options.readPreference) {
|
||||
readPreference = ReadPreference.fromOptions(options);
|
||||
} else if (session && session.inTransaction() && session.transaction.options.readPreference) {
|
||||
// The transaction’s read preference MUST override all other user configurable read preferences.
|
||||
readPreference = session.transaction.options.readPreference;
|
||||
} else if (inheritedReadPreference != null) {
|
||||
readPreference = inheritedReadPreference;
|
||||
} else {
|
||||
readPreference = ReadPreference.primary;
|
||||
}
|
||||
|
||||
return typeof readPreference === 'string' ? new ReadPreference(readPreference) : readPreference;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces options.readPreference with a ReadPreference instance
|
||||
*/
|
||||
ReadPreference.translate = function(options) {
|
||||
if (options.readPreference == null) return options;
|
||||
const r = options.readPreference;
|
||||
|
||||
if (typeof r === 'string') {
|
||||
options.readPreference = new ReadPreference(r);
|
||||
} else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
|
||||
const mode = r.mode || r.preference;
|
||||
if (mode && typeof mode === 'string') {
|
||||
options.readPreference = new ReadPreference(mode, r.tags, {
|
||||
maxStalenessSeconds: r.maxStalenessSeconds
|
||||
});
|
||||
}
|
||||
} else if (!(r instanceof ReadPreference)) {
|
||||
throw new TypeError('Invalid read preference: ' + r);
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate if a mode is legal
|
||||
*
|
||||
@@ -165,6 +228,7 @@ ReadPreference.prototype.toJSON = function() {
|
||||
const readPreference = { mode: this.mode };
|
||||
if (Array.isArray(this.tags)) readPreference.tags = this.tags;
|
||||
if (this.maxStalenessSeconds) readPreference.maxStalenessSeconds = this.maxStalenessSeconds;
|
||||
if (this.hedge) readPreference.hedge = this.hedge;
|
||||
return readPreference;
|
||||
};
|
||||
|
||||
|
||||
8
node_modules/mongodb/lib/core/topologies/replset.js
generated
vendored
8
node_modules/mongodb/lib/core/topologies/replset.js
generated
vendored
@@ -15,10 +15,10 @@ const Interval = require('./shared').Interval;
|
||||
const SessionMixins = require('./shared').SessionMixins;
|
||||
const isRetryableWritesSupported = require('./shared').isRetryableWritesSupported;
|
||||
const relayEvents = require('../utils').relayEvents;
|
||||
const isRetryableError = require('../error').isRetryableError;
|
||||
const BSON = retrieveBSON();
|
||||
const getMMAPError = require('./shared').getMMAPError;
|
||||
const makeClientMetadata = require('../utils').makeClientMetadata;
|
||||
const legacyIsRetryableWriteError = require('./shared').legacyIsRetryableWriteError;
|
||||
const now = require('../../utils').now;
|
||||
const calculateDurationInMs = require('../../utils').calculateDurationInMs;
|
||||
|
||||
@@ -72,7 +72,7 @@ var handlers = ['connect', 'close', 'error', 'timeout', 'parseError'];
|
||||
* @param {Cursor} [options.cursorFactory=Cursor] The cursor factory class used for all query cursors
|
||||
* @param {number} [options.size=5] Server connection pool size
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=0] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] Initial delay before TCP keep alive enabled
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {number} [options.connectionTimeout=10000] TCP Connection timeout setting
|
||||
* @param {number} [options.socketTimeout=0] TCP Socket timeout setting
|
||||
@@ -1202,7 +1202,7 @@ function executeWriteOperation(args, options, callback) {
|
||||
|
||||
const handler = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err)) {
|
||||
if (!legacyIsRetryableWriteError(err, self)) {
|
||||
err = getMMAPError(err);
|
||||
return callback(err);
|
||||
}
|
||||
@@ -1365,7 +1365,7 @@ ReplSet.prototype.command = function(ns, cmd, options, callback) {
|
||||
|
||||
const cb = (err, result) => {
|
||||
if (!err) return callback(null, result);
|
||||
if (!isRetryableError(err)) {
|
||||
if (!legacyIsRetryableWriteError(err, self)) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
2
node_modules/mongodb/lib/core/topologies/server.js
generated
vendored
2
node_modules/mongodb/lib/core/topologies/server.js
generated
vendored
@@ -72,7 +72,7 @@ function topologyId(server) {
|
||||
* @param {number} options.port The server port
|
||||
* @param {number} [options.size=5] Server connection pool size
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] Initial delay before TCP keep alive enabled
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
|
||||
* @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
|
||||
|
||||
55
node_modules/mongodb/lib/core/topologies/shared.js
generated
vendored
55
node_modules/mongodb/lib/core/topologies/shared.js
generated
vendored
@@ -2,7 +2,9 @@
|
||||
const ReadPreference = require('./read_preference');
|
||||
const TopologyType = require('../sdam/common').TopologyType;
|
||||
const MongoError = require('../error').MongoError;
|
||||
|
||||
const isRetryableWriteError = require('../error').isRetryableWriteError;
|
||||
const maxWireVersion = require('../utils').maxWireVersion;
|
||||
const MongoNetworkError = require('../error').MongoNetworkError;
|
||||
const MMAPv1_RETRY_WRITES_ERROR_CODE = 20;
|
||||
|
||||
/**
|
||||
@@ -416,18 +418,39 @@ function getMMAPError(err) {
|
||||
return newErr;
|
||||
}
|
||||
|
||||
module.exports.SessionMixins = SessionMixins;
|
||||
module.exports.resolveClusterTime = resolveClusterTime;
|
||||
module.exports.inquireServerState = inquireServerState;
|
||||
module.exports.getTopologyType = getTopologyType;
|
||||
module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
|
||||
module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
|
||||
module.exports.cloneOptions = cloneOptions;
|
||||
module.exports.createCompressionInfo = createCompressionInfo;
|
||||
module.exports.clone = clone;
|
||||
module.exports.diff = diff;
|
||||
module.exports.Interval = Interval;
|
||||
module.exports.Timeout = Timeout;
|
||||
module.exports.isRetryableWritesSupported = isRetryableWritesSupported;
|
||||
module.exports.getMMAPError = getMMAPError;
|
||||
module.exports.topologyType = topologyType;
|
||||
// NOTE: only used for legacy topology types
|
||||
function legacyIsRetryableWriteError(err, topology) {
|
||||
if (!(err instanceof MongoError)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if pre-4.4 server, then add error label if its a retryable write error
|
||||
if (
|
||||
isRetryableWritesSupported(topology) &&
|
||||
(err instanceof MongoNetworkError ||
|
||||
(maxWireVersion(topology) < 9 && isRetryableWriteError(err)))
|
||||
) {
|
||||
err.addErrorLabel('RetryableWriteError');
|
||||
}
|
||||
|
||||
return err.hasErrorLabel('RetryableWriteError');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SessionMixins,
|
||||
resolveClusterTime,
|
||||
inquireServerState,
|
||||
getTopologyType,
|
||||
emitServerDescriptionChanged,
|
||||
emitTopologyDescriptionChanged,
|
||||
cloneOptions,
|
||||
createCompressionInfo,
|
||||
clone,
|
||||
diff,
|
||||
Interval,
|
||||
Timeout,
|
||||
isRetryableWritesSupported,
|
||||
getMMAPError,
|
||||
topologyType,
|
||||
legacyIsRetryableWriteError
|
||||
};
|
||||
|
||||
47
node_modules/mongodb/lib/core/uri_parser.js
generated
vendored
47
node_modules/mongodb/lib/core/uri_parser.js
generated
vendored
@@ -37,6 +37,10 @@ function matchesParentDomain(srvAddress, parentDomain) {
|
||||
function parseSrvConnectionString(uri, options, callback) {
|
||||
const result = URL.parse(uri, true);
|
||||
|
||||
if (options.directConnection || options.directconnection) {
|
||||
return callback(new MongoParseError('directConnection not supported with SRV URI'));
|
||||
}
|
||||
|
||||
if (result.hostname.split('.').length < 3) {
|
||||
return callback(new MongoParseError('URI does not have hostname, domain name and tld'));
|
||||
}
|
||||
@@ -171,6 +175,7 @@ const STRING_OPTIONS = new Set(['authsource', 'replicaset']);
|
||||
// NOTE: this list exists in native already, if it is merged here we should deduplicate
|
||||
const AUTH_MECHANISMS = new Set([
|
||||
'GSSAPI',
|
||||
'MONGODB-AWS',
|
||||
'MONGODB-X509',
|
||||
'MONGODB-CR',
|
||||
'DEFAULT',
|
||||
@@ -214,7 +219,8 @@ const CASE_TRANSLATION = {
|
||||
tlscertificatekeyfile: 'tlsCertificateKeyFile',
|
||||
tlscertificatekeyfilepassword: 'tlsCertificateKeyFilePassword',
|
||||
wtimeout: 'wTimeoutMS',
|
||||
j: 'journal'
|
||||
j: 'journal',
|
||||
directconnection: 'directConnection'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -257,7 +263,9 @@ function applyConnectionStringOption(obj, key, value, options) {
|
||||
|
||||
if (key === 'authmechanism' && !AUTH_MECHANISMS.has(value)) {
|
||||
throw new MongoParseError(
|
||||
'Value for `authMechanism` must be one of: `DEFAULT`, `GSSAPI`, `PLAIN`, `MONGODB-X509`, `SCRAM-SHA-1`, `SCRAM-SHA-256`'
|
||||
`Value for authMechanism must be one of: ${Array.from(AUTH_MECHANISMS).join(
|
||||
', '
|
||||
)}, found: ${value}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -357,6 +365,16 @@ function applyAuthExpectations(parsed) {
|
||||
parsed.auth = Object.assign({}, parsed.auth, { db: '$external' });
|
||||
}
|
||||
|
||||
if (authMechanism === 'MONGODB-AWS') {
|
||||
if (authSource != null && authSource !== '$external') {
|
||||
throw new MongoParseError(
|
||||
`Invalid source \`${authSource}\` for mechanism \`${authMechanism}\` specified.`
|
||||
);
|
||||
}
|
||||
|
||||
parsed.auth = Object.assign({}, parsed.auth, { db: '$external' });
|
||||
}
|
||||
|
||||
if (authMechanism === 'MONGODB-X509') {
|
||||
if (parsed.auth && parsed.auth.password != null) {
|
||||
throw new MongoParseError(`Password not allowed for mechanism \`${authMechanism}\``);
|
||||
@@ -552,10 +570,6 @@ function parseConnectionString(uri, options, callback) {
|
||||
return callback(new MongoParseError('Invalid protocol provided'));
|
||||
}
|
||||
|
||||
if (protocol === PROTOCOL_MONGODB_SRV) {
|
||||
return parseSrvConnectionString(uri, options, callback);
|
||||
}
|
||||
|
||||
const dbAndQuery = cap[4].split('?');
|
||||
const db = dbAndQuery.length > 0 ? dbAndQuery[0] : null;
|
||||
const query = dbAndQuery.length > 1 ? dbAndQuery[1] : null;
|
||||
@@ -568,6 +582,11 @@ function parseConnectionString(uri, options, callback) {
|
||||
}
|
||||
|
||||
parsedOptions = Object.assign({}, parsedOptions, options);
|
||||
|
||||
if (protocol === PROTOCOL_MONGODB_SRV) {
|
||||
return parseSrvConnectionString(uri, parsedOptions, callback);
|
||||
}
|
||||
|
||||
const auth = { username: null, password: null, db: db && db !== '' ? qs.unescape(db) : null };
|
||||
if (parsedOptions.auth) {
|
||||
// maintain support for legacy options passed into `MongoClient`
|
||||
@@ -661,6 +680,22 @@ function parseConnectionString(uri, options, callback) {
|
||||
return callback(new MongoParseError('No hostname or hostnames provided in connection string'));
|
||||
}
|
||||
|
||||
const directConnection = !!parsedOptions.directConnection;
|
||||
if (directConnection && hosts.length !== 1) {
|
||||
// If the option is set to true, the driver MUST validate that there is exactly one host given
|
||||
// in the host list in the URI, and fail client creation otherwise.
|
||||
return callback(new MongoParseError('directConnection option requires exactly one host'));
|
||||
}
|
||||
|
||||
// NOTE: this behavior will go away in v4.0, we will always auto discover there
|
||||
if (
|
||||
parsedOptions.directConnection == null &&
|
||||
hosts.length === 1 &&
|
||||
parsedOptions.replicaSet == null
|
||||
) {
|
||||
parsedOptions.directConnection = true;
|
||||
}
|
||||
|
||||
const result = {
|
||||
hosts: hosts,
|
||||
auth: auth.db || auth.username ? auth : null,
|
||||
|
||||
30
node_modules/mongodb/lib/core/utils.js
generated
vendored
30
node_modules/mongodb/lib/core/utils.js
generated
vendored
@@ -149,6 +149,35 @@ function eachAsync(arr, eachFn, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function eachAsyncSeries(arr, eachFn, callback) {
|
||||
arr = arr || [];
|
||||
|
||||
let idx = 0;
|
||||
let awaiting = arr.length;
|
||||
if (awaiting === 0) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
function eachCallback(err) {
|
||||
idx++;
|
||||
awaiting--;
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx === arr.length && awaiting <= 0) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
eachFn(arr[idx], eachCallback);
|
||||
}
|
||||
|
||||
eachFn(arr[idx], eachCallback);
|
||||
}
|
||||
|
||||
function isUnifiedTopology(topology) {
|
||||
return topology.description != null;
|
||||
}
|
||||
@@ -257,6 +286,7 @@ module.exports = {
|
||||
maxWireVersion,
|
||||
isPromiseLike,
|
||||
eachAsync,
|
||||
eachAsyncSeries,
|
||||
isUnifiedTopology,
|
||||
arrayStrictEqual,
|
||||
tagsStrictEqual,
|
||||
|
||||
12
node_modules/mongodb/lib/core/wireprotocol/command.js
generated
vendored
12
node_modules/mongodb/lib/core/wireprotocol/command.js
generated
vendored
@@ -66,12 +66,7 @@ function _command(server, ns, cmd, options, callback) {
|
||||
finalCmd.$clusterTime = clusterTime;
|
||||
}
|
||||
|
||||
if (
|
||||
isSharded(server) &&
|
||||
!shouldUseOpMsg &&
|
||||
readPreference &&
|
||||
readPreference.preference !== 'primary'
|
||||
) {
|
||||
if (isSharded(server) && !shouldUseOpMsg && readPreference && readPreference.mode !== 'primary') {
|
||||
finalCmd = {
|
||||
$query: finalCmd,
|
||||
$readPreference: readPreference.toJSON()
|
||||
@@ -105,10 +100,7 @@ function _command(server, ns, cmd, options, callback) {
|
||||
err instanceof MongoNetworkError &&
|
||||
!err.hasErrorLabel('TransientTransactionError')
|
||||
) {
|
||||
if (err.errorLabels == null) {
|
||||
err.errorLabels = [];
|
||||
}
|
||||
err.errorLabels.push('TransientTransactionError');
|
||||
err.addErrorLabel('TransientTransactionError');
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
4
node_modules/mongodb/lib/core/wireprotocol/constants.js
generated
vendored
4
node_modules/mongodb/lib/core/wireprotocol/constants.js
generated
vendored
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const MIN_SUPPORTED_SERVER_VERSION = '2.6';
|
||||
const MAX_SUPPORTED_SERVER_VERSION = '4.2';
|
||||
const MAX_SUPPORTED_SERVER_VERSION = '4.4';
|
||||
const MIN_SUPPORTED_WIRE_VERSION = 2;
|
||||
const MAX_SUPPORTED_WIRE_VERSION = 8;
|
||||
const MAX_SUPPORTED_WIRE_VERSION = 9;
|
||||
|
||||
module.exports = {
|
||||
MIN_SUPPORTED_SERVER_VERSION,
|
||||
|
||||
4
node_modules/mongodb/lib/core/wireprotocol/query.js
generated
vendored
4
node_modules/mongodb/lib/core/wireprotocol/query.js
generated
vendored
@@ -100,6 +100,10 @@ function prepareFindCommand(server, ns, cmd, cursorState) {
|
||||
sortValue = sortObject;
|
||||
}
|
||||
|
||||
if (typeof cmd.allowDiskUse === 'boolean') {
|
||||
findCmd.allowDiskUse = cmd.allowDiskUse;
|
||||
}
|
||||
|
||||
if (cmd.sort) findCmd.sort = sortValue;
|
||||
if (cmd.fields) findCmd.projection = cmd.fields;
|
||||
if (cmd.hint) findCmd.hint = cmd.hint;
|
||||
|
||||
1
node_modules/mongodb/lib/cursor.js
generated
vendored
1
node_modules/mongodb/lib/cursor.js
generated
vendored
@@ -81,7 +81,6 @@ const fields = ['numberOfRetries', 'tailableRetryInterval'];
|
||||
* collection.find({}).filter({a:1}) // Set query on the cursor
|
||||
* collection.find({}).comment('add a comment') // Add a comment to the query, allowing to correlate queries
|
||||
* collection.find({}).addCursorFlag('tailable', true) // Set cursor as tailable
|
||||
* collection.find({}).addCursorFlag('oplogReplay', true) // Set cursor as oplogReplay
|
||||
* collection.find({}).addCursorFlag('noCursorTimeout', true) // Set cursor as noCursorTimeout
|
||||
* collection.find({}).addCursorFlag('awaitData', true) // Set cursor as awaitData
|
||||
* collection.find({}).addCursorFlag('partial', true) // Set cursor as partial
|
||||
|
||||
13
node_modules/mongodb/lib/db.js
generated
vendored
13
node_modules/mongodb/lib/db.js
generated
vendored
@@ -14,7 +14,6 @@ const Logger = require('./core').Logger;
|
||||
const Collection = require('./collection');
|
||||
const mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern;
|
||||
const executeLegacyOperation = require('./utils').executeLegacyOperation;
|
||||
const resolveReadPreference = require('./utils').resolveReadPreference;
|
||||
const ChangeStream = require('./change_stream');
|
||||
const deprecate = require('util').deprecate;
|
||||
const deprecateOptions = require('./utils').deprecateOptions;
|
||||
@@ -35,8 +34,9 @@ const AggregateOperation = require('./operations/aggregate');
|
||||
const AddUserOperation = require('./operations/add_user');
|
||||
const CollectionsOperation = require('./operations/collections');
|
||||
const CommandOperation = require('./operations/command');
|
||||
const RunCommandOperation = require('./operations/run_command');
|
||||
const CreateCollectionOperation = require('./operations/create_collection');
|
||||
const CreateIndexOperation = require('./operations/create_index');
|
||||
const CreateIndexesOperation = require('./operations/create_indexes');
|
||||
const DropCollectionOperation = require('./operations/drop').DropCollectionOperation;
|
||||
const DropDatabaseOperation = require('./operations/drop').DropDatabaseOperation;
|
||||
const ExecuteDbAdminCommandOperation = require('./operations/execute_db_admin_command');
|
||||
@@ -290,7 +290,7 @@ Db.prototype.command = function(command, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = Object.assign({}, options);
|
||||
|
||||
const commandOperation = new CommandOperation(this, options, null, command);
|
||||
const commandOperation = new RunCommandOperation(this, command, options);
|
||||
|
||||
return executeOperation(this.s.topology, commandOperation, callback);
|
||||
};
|
||||
@@ -709,7 +709,7 @@ Db.prototype.collections = function(options, callback) {
|
||||
Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
options.readPreference = resolveReadPreference(this, options);
|
||||
options.readPreference = ReadPreference.resolve(this, options);
|
||||
|
||||
const executeDbAdminCommandOperation = new ExecuteDbAdminCommandOperation(
|
||||
this,
|
||||
@@ -740,6 +740,7 @@ Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
|
||||
* @param {string} [options.name] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
|
||||
* @param {object} [options.partialFilterExpression] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
|
||||
* @param {ClientSession} [options.session] optional session to use for this operation
|
||||
* @param {(number|string)} [options.commitQuorum] (MongoDB 4.4. or higher) Specifies how many data-bearing members of a replica set, including the primary, must complete the index builds successfully before the primary marks the indexes as ready. This option accepts the same values for the "w" field in a write concern plus "votingMembers", which indicates all voting data-bearing nodes.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
* @return {Promise} returns Promise if no callback passed
|
||||
*/
|
||||
@@ -747,9 +748,9 @@ Db.prototype.createIndex = function(name, fieldOrSpec, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options ? Object.assign({}, options) : {};
|
||||
|
||||
const createIndexOperation = new CreateIndexOperation(this, name, fieldOrSpec, options);
|
||||
const createIndexesOperation = new CreateIndexesOperation(this, name, fieldOrSpec, options);
|
||||
|
||||
return executeOperation(this.s.topology, createIndexOperation, callback);
|
||||
return executeOperation(this.s.topology, createIndexesOperation, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
7
node_modules/mongodb/lib/error.js
generated
vendored
7
node_modules/mongodb/lib/error.js
generated
vendored
@@ -20,7 +20,8 @@ const GET_MORE_RESUMABLE_CODES = new Set([
|
||||
150, // StaleEpoch
|
||||
13388, // StaleConfig
|
||||
234, // RetryChangeStream
|
||||
133 // FailedToSatisfyReadPreference
|
||||
133, // FailedToSatisfyReadPreference
|
||||
43 // CursorNotFound
|
||||
]);
|
||||
|
||||
function isResumableError(error, wireVersion) {
|
||||
@@ -29,6 +30,10 @@ function isResumableError(error, wireVersion) {
|
||||
}
|
||||
|
||||
if (wireVersion >= 9) {
|
||||
// DRIVERS-1308: For 4.4 drivers running against 4.4 servers, drivers will add a special case to treat the CursorNotFound error code as resumable
|
||||
if (error.code === 43) {
|
||||
return true;
|
||||
}
|
||||
return error.hasErrorLabel('ResumableChangeStreamError');
|
||||
}
|
||||
|
||||
|
||||
15
node_modules/mongodb/lib/gridfs-stream/download.js
generated
vendored
15
node_modules/mongodb/lib/gridfs-stream/download.js
generated
vendored
@@ -278,6 +278,7 @@ function init(self) {
|
||||
if (error) {
|
||||
return __handleError(self, error);
|
||||
}
|
||||
|
||||
if (!doc) {
|
||||
var identifier = self.s.filter._id ? self.s.filter._id.toString() : self.s.filter.filename;
|
||||
var errmsg = 'FileNotFound: file ' + identifier + ' was not found';
|
||||
@@ -301,7 +302,11 @@ function init(self) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.s.bytesToSkip = handleStartOption(self, doc, self.s.options);
|
||||
try {
|
||||
self.s.bytesToSkip = handleStartOption(self, doc, self.s.options);
|
||||
} catch (error) {
|
||||
return __handleError(self, error);
|
||||
}
|
||||
|
||||
var filter = { files_id: doc._id };
|
||||
|
||||
@@ -322,7 +327,13 @@ function init(self) {
|
||||
|
||||
self.s.expectedEnd = Math.ceil(doc.length / doc.chunkSize);
|
||||
self.s.file = doc;
|
||||
self.s.bytesToTrim = handleEndOption(self, doc, self.s.cursor, self.s.options);
|
||||
|
||||
try {
|
||||
self.s.bytesToTrim = handleEndOption(self, doc, self.s.cursor, self.s.options);
|
||||
} catch (error) {
|
||||
return __handleError(self, error);
|
||||
}
|
||||
|
||||
self.emit('file', doc);
|
||||
});
|
||||
}
|
||||
|
||||
10
node_modules/mongodb/lib/mongo_client.js
generated
vendored
10
node_modules/mongodb/lib/mongo_client.js
generated
vendored
@@ -95,7 +95,7 @@ const validOptions = require('./operations/connect').validOptions;
|
||||
* @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
|
||||
* @param {number} [options.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
|
||||
* @param {number} [options.family] Version of IP stack. Can be 4, 6 or null (default).
|
||||
@@ -133,7 +133,7 @@ const validOptions = require('./operations/connect').validOptions;
|
||||
* @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
|
||||
* @param {string} [options.auth.user=undefined] The username for auth
|
||||
* @param {string} [options.auth.password=undefined] The password for auth
|
||||
* @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, or SCRAM-SHA-1
|
||||
* @param {string} [options.authMechanism] An authentication mechanism to use for connection authentication, see the {@link https://docs.mongodb.com/manual/reference/connection-string/#urioption.authMechanism|authMechanism} reference for supported options.
|
||||
* @param {object} [options.compression] Type of compression to use: snappy or zlib
|
||||
* @param {boolean} [options.fsync=false] Specify a file sync write concern
|
||||
* @param {array} [options.readPreferenceTags] Read preference tags
|
||||
@@ -152,6 +152,7 @@ const validOptions = require('./operations/connect').validOptions;
|
||||
* @param {number} [options.waitQueueTimeoutMS=0] **Only applies to the unified topology** The maximum amount of time operation execution should wait for a connection to become available. The default is 0 which means there is no limit.
|
||||
* @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
|
||||
* @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver
|
||||
* @param {boolean} [options.directConnection=false] Enable directConnection
|
||||
* @param {MongoClient~connectCallback} [callback] The command result callback
|
||||
* @return {MongoClient} a MongoClient instance
|
||||
*/
|
||||
@@ -368,7 +369,7 @@ MongoClient.prototype.isConnected = function(options) {
|
||||
* @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
|
||||
* @param {boolean} [options.noDelay=true] TCP Connection no delay
|
||||
* @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
|
||||
* @param {number} [options.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
|
||||
* @param {number} [options.family] Version of IP stack. Can be 4, 6 or null (default).
|
||||
@@ -406,7 +407,7 @@ MongoClient.prototype.isConnected = function(options) {
|
||||
* @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
|
||||
* @param {string} [options.auth.user=undefined] The username for auth
|
||||
* @param {string} [options.auth.password=undefined] The password for auth
|
||||
* @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, or SCRAM-SHA-1
|
||||
* @param {string} [options.authMechanism] An authentication mechanism to use for connection authentication, see the {@link https://docs.mongodb.com/manual/reference/connection-string/#urioption.authMechanism|authMechanism} reference for supported options.
|
||||
* @param {object} [options.compression] Type of compression to use: snappy or zlib
|
||||
* @param {boolean} [options.fsync=false] Specify a file sync write concern
|
||||
* @param {array} [options.readPreferenceTags] Read preference tags
|
||||
@@ -414,6 +415,7 @@ MongoClient.prototype.isConnected = function(options) {
|
||||
* @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
|
||||
* @param {boolean} [options.monitorCommands=false] Enable command monitoring for this client
|
||||
* @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
|
||||
* @param {boolean} [options.directConnection=false] Enable directConnection
|
||||
* @param {boolean} [options.useNewUrlParser=true] Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future. Defaults to true, and must be explicitly set to false to use the legacy url parser.
|
||||
* @param {boolean} [options.useUnifiedTopology] Enables the new unified topology layer
|
||||
* @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
|
||||
|
||||
23
node_modules/mongodb/lib/operations/collection_ops.js
generated
vendored
23
node_modules/mongodb/lib/operations/collection_ops.js
generated
vendored
@@ -8,13 +8,11 @@ const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
|
||||
const ensureIndexDb = require('./db_ops').ensureIndex;
|
||||
const evaluate = require('./db_ops').evaluate;
|
||||
const executeCommand = require('./db_ops').executeCommand;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const indexInformationDb = require('./db_ops').indexInformation;
|
||||
const Long = require('../core').BSON.Long;
|
||||
const MongoError = require('../core').MongoError;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const toError = require('../utils').toError;
|
||||
const insertDocuments = require('./common_functions').insertDocuments;
|
||||
const updateDocuments = require('./common_functions').updateDocuments;
|
||||
|
||||
@@ -52,24 +50,6 @@ const updateDocuments = require('./common_functions').updateDocuments;
|
||||
const groupFunction =
|
||||
'function () {\nvar c = db[ns].find(condition);\nvar map = new Map();\nvar reduce_function = reduce;\n\nwhile (c.hasNext()) {\nvar obj = c.next();\nvar key = {};\n\nfor (var i = 0, len = keys.length; i < len; ++i) {\nvar k = keys[i];\nkey[k] = obj[k];\n}\n\nvar aggObj = map.get(key);\n\nif (aggObj == null) {\nvar newObj = Object.extend({}, key);\naggObj = Object.extend(newObj, initial);\nmap.put(key, aggObj);\n}\n\nreduce_function(obj, aggObj);\n}\n\nreturn { "result": map.values() };\n}';
|
||||
|
||||
// Check the update operation to ensure it has atomic operators.
|
||||
function checkForAtomicOperators(update) {
|
||||
if (Array.isArray(update)) {
|
||||
return update.reduce((err, u) => err || checkForAtomicOperators(u), null);
|
||||
}
|
||||
|
||||
const keys = Object.keys(update);
|
||||
|
||||
// same errors as the server would give for update doc lacking atomic operators
|
||||
if (keys.length === 0) {
|
||||
return toError('The update operation document must contain at least one atomic operator.');
|
||||
}
|
||||
|
||||
if (keys[0][0] !== '$') {
|
||||
return toError('the update operation document must contain atomic operators.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index on the db and collection.
|
||||
*
|
||||
@@ -188,7 +168,7 @@ function group(coll, keys, condition, initial, reduce, finalize, command, option
|
||||
|
||||
options = Object.assign({}, options);
|
||||
// Ensure we have the right read preference inheritance
|
||||
options.readPreference = resolveReadPreference(coll, options);
|
||||
options.readPreference = ReadPreference.resolve(coll, options);
|
||||
|
||||
// Do we have a readConcern specified
|
||||
decorateWithReadConcern(selector, coll, options);
|
||||
@@ -361,7 +341,6 @@ function save(coll, doc, options, callback) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkForAtomicOperators,
|
||||
createIndex,
|
||||
createIndexes,
|
||||
ensureIndex,
|
||||
|
||||
5
node_modules/mongodb/lib/operations/command.js
generated
vendored
5
node_modules/mongodb/lib/operations/command.js
generated
vendored
@@ -7,7 +7,6 @@ const debugOptions = require('../utils').debugOptions;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const MongoError = require('../core').MongoError;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const MongoDBNamespace = require('../utils').MongoDBNamespace;
|
||||
|
||||
const debugFields = [
|
||||
@@ -38,9 +37,9 @@ class CommandOperation extends OperationBase {
|
||||
|
||||
if (!this.hasAspect(Aspect.WRITE_OPERATION)) {
|
||||
if (collection != null) {
|
||||
this.options.readPreference = resolveReadPreference(collection, options);
|
||||
this.options.readPreference = ReadPreference.resolve(collection, options);
|
||||
} else {
|
||||
this.options.readPreference = resolveReadPreference(db, options);
|
||||
this.options.readPreference = ReadPreference.resolve(db, options);
|
||||
}
|
||||
} else {
|
||||
if (collection != null) {
|
||||
|
||||
15
node_modules/mongodb/lib/operations/command_v2.js
generated
vendored
15
node_modules/mongodb/lib/operations/command_v2.js
generated
vendored
@@ -2,12 +2,12 @@
|
||||
|
||||
const Aspect = require('./operation').Aspect;
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const ReadConcern = require('../read_concern');
|
||||
const WriteConcern = require('../write_concern');
|
||||
const maxWireVersion = require('../core/utils').maxWireVersion;
|
||||
const commandSupportsReadConcern = require('../core/sessions').commandSupportsReadConcern;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const MongoError = require('../core/error').MongoError;
|
||||
|
||||
const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
|
||||
|
||||
@@ -16,9 +16,10 @@ class CommandOperationV2 extends OperationBase {
|
||||
super(options);
|
||||
|
||||
this.ns = parent.s.namespace.withCollection('$cmd');
|
||||
this.readPreference = resolveReadPreference(parent, this.options);
|
||||
this.readConcern = resolveReadConcern(parent, this.options);
|
||||
this.writeConcern = resolveWriteConcern(parent, this.options);
|
||||
const propertyProvider = this.hasAspect(Aspect.NO_INHERIT_OPTIONS) ? undefined : parent;
|
||||
this.readPreference = ReadPreference.resolve(propertyProvider, this.options);
|
||||
this.readConcern = resolveReadConcern(propertyProvider, this.options);
|
||||
this.writeConcern = resolveWriteConcern(propertyProvider, this.options);
|
||||
this.explain = false;
|
||||
|
||||
if (operationOptions && typeof operationOptions.fullResponse === 'boolean') {
|
||||
@@ -97,11 +98,11 @@ class CommandOperationV2 extends OperationBase {
|
||||
}
|
||||
|
||||
function resolveWriteConcern(parent, options) {
|
||||
return WriteConcern.fromOptions(options) || parent.writeConcern;
|
||||
return WriteConcern.fromOptions(options) || (parent && parent.writeConcern);
|
||||
}
|
||||
|
||||
function resolveReadConcern(parent, options) {
|
||||
return ReadConcern.fromOptions(options) || parent.readConcern;
|
||||
return ReadConcern.fromOptions(options) || (parent && parent.readConcern);
|
||||
}
|
||||
|
||||
module.exports = CommandOperationV2;
|
||||
|
||||
3
node_modules/mongodb/lib/operations/common_functions.js
generated
vendored
3
node_modules/mongodb/lib/operations/common_functions.js
generated
vendored
@@ -297,6 +297,9 @@ function removeDocuments(coll, selector, options, callback) {
|
||||
} else if (finalOptions.retryWrites) {
|
||||
finalOptions.retryWrites = false;
|
||||
}
|
||||
if (options.hint) {
|
||||
op.hint = options.hint;
|
||||
}
|
||||
|
||||
// Have we specified collation
|
||||
try {
|
||||
|
||||
25
node_modules/mongodb/lib/operations/connect.js
generated
vendored
25
node_modules/mongodb/lib/operations/connect.js
generated
vendored
@@ -33,9 +33,11 @@ const legacyParse = deprecate(
|
||||
|
||||
const AUTH_MECHANISM_INTERNAL_MAP = {
|
||||
DEFAULT: 'default',
|
||||
'MONGODB-CR': 'mongocr',
|
||||
PLAIN: 'plain',
|
||||
GSSAPI: 'gssapi',
|
||||
'MONGODB-CR': 'mongocr',
|
||||
'MONGODB-X509': 'x509',
|
||||
'MONGODB-AWS': 'mongodb-aws',
|
||||
'SCRAM-SHA-1': 'scram-sha-1',
|
||||
'SCRAM-SHA-256': 'scram-sha-256'
|
||||
};
|
||||
@@ -66,12 +68,13 @@ const monitoringEvents = [
|
||||
|
||||
const VALID_AUTH_MECHANISMS = new Set([
|
||||
'DEFAULT',
|
||||
'MONGODB-CR',
|
||||
'PLAIN',
|
||||
'GSSAPI',
|
||||
'MONGODB-CR',
|
||||
'MONGODB-X509',
|
||||
'MONGODB-AWS',
|
||||
'SCRAM-SHA-1',
|
||||
'SCRAM-SHA-256',
|
||||
'GSSAPI'
|
||||
'SCRAM-SHA-256'
|
||||
]);
|
||||
|
||||
const validOptionNames = [
|
||||
@@ -151,6 +154,8 @@ const validOptionNames = [
|
||||
'tlsCertificateKeyFilePassword',
|
||||
'minHeartbeatFrequencyMS',
|
||||
'heartbeatFrequencyMS',
|
||||
'directConnection',
|
||||
'appName',
|
||||
|
||||
// CMAP options
|
||||
'maxPoolSize',
|
||||
@@ -644,6 +649,7 @@ function generateCredentials(client, username, password, options) {
|
||||
// authMechanism
|
||||
const authMechanismRaw = options.authMechanism || 'DEFAULT';
|
||||
const authMechanism = authMechanismRaw.toUpperCase();
|
||||
const mechanismProperties = options.authMechanismProperties;
|
||||
|
||||
if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
|
||||
throw MongoError.create({
|
||||
@@ -652,18 +658,9 @@ function generateCredentials(client, username, password, options) {
|
||||
});
|
||||
}
|
||||
|
||||
if (authMechanism === 'GSSAPI') {
|
||||
return new MongoCredentials({
|
||||
mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
|
||||
mechanismProperties: options,
|
||||
source,
|
||||
username,
|
||||
password
|
||||
});
|
||||
}
|
||||
|
||||
return new MongoCredentials({
|
||||
mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
|
||||
mechanismProperties,
|
||||
source,
|
||||
username,
|
||||
password
|
||||
|
||||
93
node_modules/mongodb/lib/operations/create_collection.js
generated
vendored
93
node_modules/mongodb/lib/operations/create_collection.js
generated
vendored
@@ -4,13 +4,11 @@ const Aspect = require('./operation').Aspect;
|
||||
const defineAspects = require('./operation').defineAspects;
|
||||
const CommandOperation = require('./command');
|
||||
const applyWriteConcern = require('../utils').applyWriteConcern;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const loadCollection = require('../dynamic_loaders').loadCollection;
|
||||
const MongoError = require('../core').MongoError;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
|
||||
// Filter out any write concern options
|
||||
const illegalCommandFields = [
|
||||
const ILLEGAL_COMMAND_FIELDS = new Set([
|
||||
'w',
|
||||
'wtimeout',
|
||||
'j',
|
||||
@@ -24,12 +22,11 @@ const illegalCommandFields = [
|
||||
'session',
|
||||
'readConcern',
|
||||
'writeConcern'
|
||||
];
|
||||
]);
|
||||
|
||||
class CreateCollectionOperation extends CommandOperation {
|
||||
constructor(db, name, options) {
|
||||
super(db, options);
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -37,14 +34,12 @@ class CreateCollectionOperation extends CommandOperation {
|
||||
const name = this.name;
|
||||
const options = this.options;
|
||||
|
||||
// Create collection command
|
||||
const cmd = { create: name };
|
||||
// Add all optional parameters
|
||||
for (let n in options) {
|
||||
if (
|
||||
options[n] != null &&
|
||||
typeof options[n] !== 'function' &&
|
||||
illegalCommandFields.indexOf(n) === -1
|
||||
!ILLEGAL_COMMAND_FIELDS.has(n)
|
||||
) {
|
||||
cmd[n] = options[n];
|
||||
}
|
||||
@@ -57,61 +52,51 @@ class CreateCollectionOperation extends CommandOperation {
|
||||
const db = this.db;
|
||||
const name = this.name;
|
||||
const options = this.options;
|
||||
const Collection = loadCollection();
|
||||
|
||||
let Collection = loadCollection();
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed()) {
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
}
|
||||
|
||||
let listCollectionOptions = Object.assign({}, options, { nameOnly: true });
|
||||
let listCollectionOptions = Object.assign({ nameOnly: true, strict: false }, options);
|
||||
listCollectionOptions = applyWriteConcern(listCollectionOptions, { db }, listCollectionOptions);
|
||||
|
||||
// Check if we have the name
|
||||
db.listCollections({ name }, listCollectionOptions)
|
||||
.setReadPreference(ReadPreference.PRIMARY)
|
||||
.toArray((err, collections) => {
|
||||
if (err != null) return handleCallback(callback, err, null);
|
||||
if (collections.length > 0 && listCollectionOptions.strict) {
|
||||
return handleCallback(
|
||||
callback,
|
||||
MongoError.create({
|
||||
message: `Collection ${name} already exists. Currently in strict mode.`,
|
||||
driver: true
|
||||
}),
|
||||
null
|
||||
);
|
||||
} else if (collections.length > 0) {
|
||||
try {
|
||||
return handleCallback(
|
||||
callback,
|
||||
null,
|
||||
new Collection(db, db.s.topology, db.databaseName, name, db.s.pkFactory, options)
|
||||
);
|
||||
} catch (err) {
|
||||
return handleCallback(callback, err);
|
||||
}
|
||||
}
|
||||
function done(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Execute command
|
||||
super.execute(err => {
|
||||
if (err) return handleCallback(callback, err);
|
||||
try {
|
||||
callback(
|
||||
null,
|
||||
new Collection(db, db.s.topology, db.databaseName, name, db.s.pkFactory, options)
|
||||
);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return handleCallback(
|
||||
callback,
|
||||
null,
|
||||
new Collection(db, db.s.topology, db.databaseName, name, db.s.pkFactory, options)
|
||||
);
|
||||
} catch (err) {
|
||||
return handleCallback(callback, err);
|
||||
const strictMode = listCollectionOptions.strict;
|
||||
if (strictMode) {
|
||||
db.listCollections({ name }, listCollectionOptions)
|
||||
.setReadPreference(ReadPreference.PRIMARY)
|
||||
.toArray((err, collections) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (collections.length > 0) {
|
||||
return callback(
|
||||
new MongoError(`Collection ${name} already exists. Currently in strict mode.`)
|
||||
);
|
||||
}
|
||||
|
||||
super.execute(done);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise just execute the command
|
||||
super.execute(done);
|
||||
}
|
||||
}
|
||||
|
||||
defineAspects(CreateCollectionOperation, Aspect.WRITE_OPERATION);
|
||||
|
||||
module.exports = CreateCollectionOperation;
|
||||
|
||||
124
node_modules/mongodb/lib/operations/create_indexes.js
generated
vendored
124
node_modules/mongodb/lib/operations/create_indexes.js
generated
vendored
@@ -2,60 +2,116 @@
|
||||
|
||||
const Aspect = require('./operation').Aspect;
|
||||
const defineAspects = require('./operation').defineAspects;
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const executeCommand = require('./db_ops').executeCommand;
|
||||
const CommandOperationV2 = require('./command_v2');
|
||||
const MongoError = require('../core').MongoError;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const parseIndexOptions = require('../utils').parseIndexOptions;
|
||||
const maxWireVersion = require('../core/utils').maxWireVersion;
|
||||
|
||||
class CreateIndexesOperation extends OperationBase {
|
||||
constructor(collection, indexSpecs, options) {
|
||||
super(options);
|
||||
const validIndexOptions = new Set([
|
||||
'unique',
|
||||
'partialFilterExpression',
|
||||
'sparse',
|
||||
'background',
|
||||
'expireAfterSeconds',
|
||||
'storageEngine',
|
||||
'collation',
|
||||
'bucketSize'
|
||||
]);
|
||||
|
||||
class CreateIndexesOperation extends CommandOperationV2 {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
constructor(parent, collection, indexes, options) {
|
||||
super(parent, options);
|
||||
this.collection = collection;
|
||||
this.indexSpecs = indexSpecs;
|
||||
|
||||
// createIndex can be called with a variety of styles:
|
||||
// coll.createIndex('a');
|
||||
// coll.createIndex({ a: 1 });
|
||||
// coll.createIndex([['a', 1]]);
|
||||
// createIndexes is always called with an array of index spec objects
|
||||
if (!Array.isArray(indexes) || Array.isArray(indexes[0])) {
|
||||
this.onlyReturnNameOfCreatedIndex = true;
|
||||
// TODO: remove in v4 (breaking change); make createIndex return full response as createIndexes does
|
||||
|
||||
const indexParameters = parseIndexOptions(indexes);
|
||||
// Generate the index name
|
||||
const name = typeof options.name === 'string' ? options.name : indexParameters.name;
|
||||
// Set up the index
|
||||
const indexSpec = { name, key: indexParameters.fieldHash };
|
||||
// merge valid index options into the index spec
|
||||
for (let optionName in options) {
|
||||
if (validIndexOptions.has(optionName)) {
|
||||
indexSpec[optionName] = options[optionName];
|
||||
}
|
||||
}
|
||||
this.indexes = [indexSpec];
|
||||
return;
|
||||
}
|
||||
|
||||
this.indexes = indexes;
|
||||
}
|
||||
|
||||
execute(callback) {
|
||||
const coll = this.collection;
|
||||
const indexSpecs = this.indexSpecs;
|
||||
let options = this.options;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
execute(server, callback) {
|
||||
const options = this.options;
|
||||
const indexes = this.indexes;
|
||||
|
||||
const capabilities = coll.s.topology.capabilities();
|
||||
const serverWireVersion = maxWireVersion(server);
|
||||
|
||||
// Ensure we generate the correct name if the parameter is not set
|
||||
for (let i = 0; i < indexSpecs.length; i++) {
|
||||
if (indexSpecs[i].name == null) {
|
||||
for (let i = 0; i < indexes.length; i++) {
|
||||
// Did the user pass in a collation, check if our write server supports it
|
||||
if (indexes[i].collation && serverWireVersion < 5) {
|
||||
callback(
|
||||
new MongoError(
|
||||
`Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation`
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexes[i].name == null) {
|
||||
const keys = [];
|
||||
|
||||
// Did the user pass in a collation, check if our write server supports it
|
||||
if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
|
||||
return callback(new MongoError('server/primary/mongos does not support collation'));
|
||||
}
|
||||
|
||||
for (let name in indexSpecs[i].key) {
|
||||
keys.push(`${name}_${indexSpecs[i].key[name]}`);
|
||||
for (let name in indexes[i].key) {
|
||||
keys.push(`${name}_${indexes[i].key[name]}`);
|
||||
}
|
||||
|
||||
// Set the name
|
||||
indexSpecs[i].name = keys.join('_');
|
||||
indexes[i].name = keys.join('_');
|
||||
}
|
||||
}
|
||||
|
||||
options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
|
||||
const cmd = { createIndexes: this.collection, indexes };
|
||||
|
||||
// Execute the index
|
||||
executeCommand(
|
||||
coll.s.db,
|
||||
{
|
||||
createIndexes: coll.collectionName,
|
||||
indexes: indexSpecs
|
||||
},
|
||||
options,
|
||||
callback
|
||||
);
|
||||
if (options.commitQuorum != null) {
|
||||
if (serverWireVersion < 9) {
|
||||
callback(
|
||||
new MongoError('`commitQuorum` option for `createIndexes` not supported on servers < 4.4')
|
||||
);
|
||||
return;
|
||||
}
|
||||
cmd.commitQuorum = options.commitQuorum;
|
||||
}
|
||||
|
||||
// collation is set on each index, it should not be defined at the root
|
||||
this.options.collation = undefined;
|
||||
|
||||
super.executeCommand(server, cmd, (err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, this.onlyReturnNameOfCreatedIndex ? indexes[0].name : result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineAspects(CreateIndexesOperation, Aspect.WRITE_OPERATION);
|
||||
defineAspects(CreateIndexesOperation, [Aspect.WRITE_OPERATION, Aspect.EXECUTE_WITH_SELECTION]);
|
||||
|
||||
module.exports = CreateIndexesOperation;
|
||||
|
||||
3
node_modules/mongodb/lib/operations/db_ops.js
generated
vendored
3
node_modules/mongodb/lib/operations/db_ops.js
generated
vendored
@@ -2,7 +2,6 @@
|
||||
|
||||
const applyWriteConcern = require('../utils').applyWriteConcern;
|
||||
const Code = require('../core').BSON.Code;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const debugOptions = require('../utils').debugOptions;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const MongoError = require('../core').MongoError;
|
||||
@@ -225,7 +224,7 @@ function executeCommand(db, command, options, callback) {
|
||||
const dbName = options.dbName || options.authdb || db.databaseName;
|
||||
|
||||
// Convert the readPreference if its not a write
|
||||
options.readPreference = resolveReadPreference(db, options);
|
||||
options.readPreference = ReadPreference.resolve(db, options);
|
||||
|
||||
// Debug information
|
||||
if (db.s.logger.isDebug())
|
||||
|
||||
12
node_modules/mongodb/lib/operations/find.js
generated
vendored
12
node_modules/mongodb/lib/operations/find.js
generated
vendored
@@ -3,7 +3,9 @@
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const Aspect = require('./operation').Aspect;
|
||||
const defineAspects = require('./operation').defineAspects;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const maxWireVersion = require('../core/utils').maxWireVersion;
|
||||
const MongoError = require('../core/error').MongoError;
|
||||
|
||||
class FindOperation extends OperationBase {
|
||||
constructor(collection, ns, command, options) {
|
||||
@@ -11,16 +13,20 @@ class FindOperation extends OperationBase {
|
||||
|
||||
this.ns = ns;
|
||||
this.cmd = command;
|
||||
this.readPreference = resolveReadPreference(collection, this.options);
|
||||
this.readPreference = ReadPreference.resolve(collection, this.options);
|
||||
}
|
||||
|
||||
execute(server, callback) {
|
||||
// copied from `CommandOperationV2`, to be subclassed in the future
|
||||
this.server = server;
|
||||
|
||||
const cursorState = this.cursorState || {};
|
||||
if (typeof this.cmd.allowDiskUse !== 'undefined' && maxWireVersion(server) < 4) {
|
||||
callback(new MongoError('The `allowDiskUse` option is not supported on MongoDB < 3.2'));
|
||||
return;
|
||||
}
|
||||
|
||||
// TOOD: use `MongoDBNamespace` through and through
|
||||
const cursorState = this.cursorState || {};
|
||||
server.query(this.ns.toString(), this.cmd, cursorState, this.options, callback);
|
||||
}
|
||||
}
|
||||
|
||||
17
node_modules/mongodb/lib/operations/find_and_modify.js
generated
vendored
17
node_modules/mongodb/lib/operations/find_and_modify.js
generated
vendored
@@ -8,6 +8,8 @@ const executeCommand = require('./db_ops').executeCommand;
|
||||
const formattedOrderClause = require('../utils').formattedOrderClause;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const maxWireVersion = require('../core/utils').maxWireVersion;
|
||||
const MongoError = require('../error').MongoError;
|
||||
|
||||
class FindAndModifyOperation extends OperationBase {
|
||||
constructor(collection, query, sort, doc, options) {
|
||||
@@ -86,6 +88,21 @@ class FindAndModifyOperation extends OperationBase {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
if (options.hint) {
|
||||
// TODO: once this method becomes a CommandOperationV2 we will have the server
|
||||
// in place to check.
|
||||
const unacknowledgedWrite = options.writeConcern && options.writeConcern.w === 0;
|
||||
if (unacknowledgedWrite || maxWireVersion(coll.s.topology) < 8) {
|
||||
callback(
|
||||
new MongoError('The current topology does not support a hint on findAndModify commands')
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
queryObject.hint = options.hint;
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
executeCommand(coll.s.db, queryObject, options, (err, result) => {
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
|
||||
5
node_modules/mongodb/lib/operations/find_one_and_delete.js
generated
vendored
5
node_modules/mongodb/lib/operations/find_one_and_delete.js
generated
vendored
@@ -9,6 +9,11 @@ class FindOneAndDeleteOperation extends FindAndModifyOperation {
|
||||
finalOptions.fields = options.projection;
|
||||
finalOptions.remove = true;
|
||||
|
||||
// Basic validation
|
||||
if (filter == null || typeof filter !== 'object') {
|
||||
throw new TypeError('Filter parameter must be an object');
|
||||
}
|
||||
|
||||
super(collection, filter, finalOptions.sort, null, finalOptions);
|
||||
}
|
||||
}
|
||||
|
||||
13
node_modules/mongodb/lib/operations/find_one_and_replace.js
generated
vendored
13
node_modules/mongodb/lib/operations/find_one_and_replace.js
generated
vendored
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const FindAndModifyOperation = require('./find_and_modify');
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
|
||||
class FindOneAndReplaceOperation extends FindAndModifyOperation {
|
||||
constructor(collection, filter, replacement, options) {
|
||||
@@ -11,6 +12,18 @@ class FindOneAndReplaceOperation extends FindAndModifyOperation {
|
||||
finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
|
||||
finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;
|
||||
|
||||
if (filter == null || typeof filter !== 'object') {
|
||||
throw new TypeError('Filter parameter must be an object');
|
||||
}
|
||||
|
||||
if (replacement == null || typeof replacement !== 'object') {
|
||||
throw new TypeError('Replacement parameter must be an object');
|
||||
}
|
||||
|
||||
if (hasAtomicOperators(replacement)) {
|
||||
throw new TypeError('Replacement document must not contain atomic operators');
|
||||
}
|
||||
|
||||
super(collection, filter, finalOptions.sort, replacement, finalOptions);
|
||||
}
|
||||
}
|
||||
|
||||
13
node_modules/mongodb/lib/operations/find_one_and_update.js
generated
vendored
13
node_modules/mongodb/lib/operations/find_one_and_update.js
generated
vendored
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const FindAndModifyOperation = require('./find_and_modify');
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
|
||||
class FindOneAndUpdateOperation extends FindAndModifyOperation {
|
||||
constructor(collection, filter, update, options) {
|
||||
@@ -12,6 +13,18 @@ class FindOneAndUpdateOperation extends FindAndModifyOperation {
|
||||
typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
|
||||
finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;
|
||||
|
||||
if (filter == null || typeof filter !== 'object') {
|
||||
throw new TypeError('Filter parameter must be an object');
|
||||
}
|
||||
|
||||
if (update == null || typeof update !== 'object') {
|
||||
throw new TypeError('Update parameter must be an object');
|
||||
}
|
||||
|
||||
if (!hasAtomicOperators(update)) {
|
||||
throw new TypeError('Update document requires atomic operators');
|
||||
}
|
||||
|
||||
super(collection, filter, finalOptions.sort, update, finalOptions);
|
||||
}
|
||||
}
|
||||
|
||||
4
node_modules/mongodb/lib/operations/geo_haystack_search.js
generated
vendored
4
node_modules/mongodb/lib/operations/geo_haystack_search.js
generated
vendored
@@ -7,7 +7,7 @@ const decorateCommand = require('../utils').decorateCommand;
|
||||
const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
|
||||
const executeCommand = require('./db_ops').executeCommand;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const toError = require('../utils').toError;
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ class GeoHaystackSearchOperation extends OperationBase {
|
||||
|
||||
options = Object.assign({}, options);
|
||||
// Ensure we have the right read preference inheritance
|
||||
options.readPreference = resolveReadPreference(coll, options);
|
||||
options.readPreference = ReadPreference.resolve(coll, options);
|
||||
|
||||
// Do we have a readConcern specified
|
||||
decorateWithReadConcern(commandObject, coll, options);
|
||||
|
||||
6
node_modules/mongodb/lib/operations/map_reduce.js
generated
vendored
6
node_modules/mongodb/lib/operations/map_reduce.js
generated
vendored
@@ -9,7 +9,7 @@ const handleCallback = require('../utils').handleCallback;
|
||||
const isObject = require('../utils').isObject;
|
||||
const loadDb = require('../dynamic_loaders').loadDb;
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const ReadPreference = require('../core').ReadPreference;
|
||||
const toError = require('../utils').toError;
|
||||
|
||||
const exclusionList = [
|
||||
@@ -60,7 +60,7 @@ class MapReduceOperation extends OperationBase {
|
||||
let options = this.options;
|
||||
|
||||
const mapCommandHash = {
|
||||
mapreduce: coll.collectionName,
|
||||
mapReduce: coll.collectionName,
|
||||
map: map,
|
||||
reduce: reduce
|
||||
};
|
||||
@@ -80,7 +80,7 @@ class MapReduceOperation extends OperationBase {
|
||||
options = Object.assign({}, options);
|
||||
|
||||
// Ensure we have the right read preference inheritance
|
||||
options.readPreference = resolveReadPreference(coll, options);
|
||||
options.readPreference = ReadPreference.resolve(coll, options);
|
||||
|
||||
// If we have a read preference and inline is not set as output fail hard
|
||||
if (
|
||||
|
||||
3
node_modules/mongodb/lib/operations/operation.js
generated
vendored
3
node_modules/mongodb/lib/operations/operation.js
generated
vendored
@@ -4,7 +4,8 @@ const Aspect = {
|
||||
READ_OPERATION: Symbol('READ_OPERATION'),
|
||||
WRITE_OPERATION: Symbol('WRITE_OPERATION'),
|
||||
RETRYABLE: Symbol('RETRYABLE'),
|
||||
EXECUTE_WITH_SELECTION: Symbol('EXECUTE_WITH_SELECTION')
|
||||
EXECUTE_WITH_SELECTION: Symbol('EXECUTE_WITH_SELECTION'),
|
||||
NO_INHERIT_OPTIONS: Symbol('NO_INHERIT_OPTIONS')
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
39
node_modules/mongodb/lib/operations/re_index.js
generated
vendored
39
node_modules/mongodb/lib/operations/re_index.js
generated
vendored
@@ -1,28 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const CommandOperation = require('./command');
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const Aspect = require('./operation').Aspect;
|
||||
const defineAspects = require('./operation').defineAspects;
|
||||
const CommandOperationV2 = require('./command_v2');
|
||||
const serverType = require('../core/sdam/common').serverType;
|
||||
const ServerType = require('../core/sdam/common').ServerType;
|
||||
const MongoError = require('../core').MongoError;
|
||||
|
||||
class ReIndexOperation extends CommandOperation {
|
||||
class ReIndexOperation extends CommandOperationV2 {
|
||||
constructor(collection, options) {
|
||||
super(collection.s.db, options, collection);
|
||||
super(collection, options);
|
||||
this.collectionName = collection.collectionName;
|
||||
}
|
||||
|
||||
_buildCommand() {
|
||||
const collection = this.collection;
|
||||
|
||||
const cmd = { reIndex: collection.collectionName };
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
execute(callback) {
|
||||
super.execute((err, result) => {
|
||||
if (callback == null) return;
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
handleCallback(callback, null, result.ok ? true : false);
|
||||
execute(server, callback) {
|
||||
if (serverType(server) !== ServerType.Standalone) {
|
||||
callback(new MongoError(`reIndex can only be executed on standalone servers.`));
|
||||
return;
|
||||
}
|
||||
super.executeCommand(server, { reIndex: this.collectionName }, (err, result) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
callback(null, !!result.ok);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineAspects(ReIndexOperation, [Aspect.EXECUTE_WITH_SELECTION]);
|
||||
|
||||
module.exports = ReIndexOperation;
|
||||
|
||||
15
node_modules/mongodb/lib/operations/replace_one.js
generated
vendored
15
node_modules/mongodb/lib/operations/replace_one.js
generated
vendored
@@ -2,27 +2,34 @@
|
||||
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const updateDocuments = require('./common_functions').updateDocuments;
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
|
||||
class ReplaceOneOperation extends OperationBase {
|
||||
constructor(collection, filter, doc, options) {
|
||||
constructor(collection, filter, replacement, options) {
|
||||
super(options);
|
||||
|
||||
if (hasAtomicOperators(replacement)) {
|
||||
throw new TypeError('Replacement document must not contain atomic operators');
|
||||
}
|
||||
|
||||
this.collection = collection;
|
||||
this.filter = filter;
|
||||
this.doc = doc;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
execute(callback) {
|
||||
const coll = this.collection;
|
||||
const filter = this.filter;
|
||||
const doc = this.doc;
|
||||
const replacement = this.replacement;
|
||||
const options = this.options;
|
||||
|
||||
// Set single document update
|
||||
options.multi = false;
|
||||
|
||||
// Execute update
|
||||
updateDocuments(coll, filter, doc, options, (err, r) => replaceCallback(err, r, doc, callback));
|
||||
updateDocuments(coll, filter, replacement, options, (err, r) =>
|
||||
replaceCallback(err, r, replacement, callback)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
node_modules/mongodb/lib/operations/update_many.js
generated
vendored
5
node_modules/mongodb/lib/operations/update_many.js
generated
vendored
@@ -3,11 +3,16 @@
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const updateCallback = require('./common_functions').updateCallback;
|
||||
const updateDocuments = require('./common_functions').updateDocuments;
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
|
||||
class UpdateManyOperation extends OperationBase {
|
||||
constructor(collection, filter, update, options) {
|
||||
super(options);
|
||||
|
||||
if (!hasAtomicOperators(update)) {
|
||||
throw new TypeError('Update document requires atomic operators');
|
||||
}
|
||||
|
||||
this.collection = collection;
|
||||
this.filter = filter;
|
||||
this.update = update;
|
||||
|
||||
5
node_modules/mongodb/lib/operations/update_one.js
generated
vendored
5
node_modules/mongodb/lib/operations/update_one.js
generated
vendored
@@ -2,11 +2,16 @@
|
||||
|
||||
const OperationBase = require('./operation').OperationBase;
|
||||
const updateDocuments = require('./common_functions').updateDocuments;
|
||||
const hasAtomicOperators = require('../utils').hasAtomicOperators;
|
||||
|
||||
class UpdateOneOperation extends OperationBase {
|
||||
constructor(collection, filter, update, options) {
|
||||
super(options);
|
||||
|
||||
if (!hasAtomicOperators(update)) {
|
||||
throw new TypeError('Update document requires atomic operators');
|
||||
}
|
||||
|
||||
this.collection = collection;
|
||||
this.filter = filter;
|
||||
this.update = update;
|
||||
|
||||
3
node_modules/mongodb/lib/operations/validate_collection.js
generated
vendored
3
node_modules/mongodb/lib/operations/validate_collection.js
generated
vendored
@@ -14,8 +14,7 @@ class ValidateCollectionOperation extends CommandOperation {
|
||||
}
|
||||
|
||||
super(admin.s.db, options, null, command);
|
||||
|
||||
this.collectionName;
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
execute(callback) {
|
||||
|
||||
2
node_modules/mongodb/lib/topologies/mongos.js
generated
vendored
2
node_modules/mongodb/lib/topologies/mongos.js
generated
vendored
@@ -82,7 +82,7 @@ var legalOptionNames = [
|
||||
* @param {object} [options.socketOptions] Socket options
|
||||
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
|
||||
* @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
|
||||
* @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
|
||||
* @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
|
||||
|
||||
2
node_modules/mongodb/lib/topologies/replset.js
generated
vendored
2
node_modules/mongodb/lib/topologies/replset.js
generated
vendored
@@ -92,7 +92,7 @@ var legalOptionNames = [
|
||||
* @param {object} [options.socketOptions] Socket options
|
||||
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
|
||||
* @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
|
||||
* @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
|
||||
* @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
|
||||
|
||||
2
node_modules/mongodb/lib/topologies/server.js
generated
vendored
2
node_modules/mongodb/lib/topologies/server.js
generated
vendored
@@ -84,7 +84,7 @@ var legalOptionNames = [
|
||||
* @param {boolean} [options.socketOptions.autoReconnect=true] Reconnect on error.
|
||||
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
|
||||
* @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
|
||||
* @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
|
||||
* @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
|
||||
* @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
|
||||
|
||||
8
node_modules/mongodb/lib/topologies/topology_base.js
generated
vendored
8
node_modules/mongodb/lib/topologies/topology_base.js
generated
vendored
@@ -3,7 +3,7 @@
|
||||
const EventEmitter = require('events'),
|
||||
MongoError = require('../core').MongoError,
|
||||
f = require('util').format,
|
||||
translateReadPreference = require('../utils').translateReadPreference,
|
||||
ReadPreference = require('../core').ReadPreference,
|
||||
ClientSession = require('../core').Sessions.ClientSession;
|
||||
|
||||
// The store of ops
|
||||
@@ -293,7 +293,7 @@ class TopologyBase extends EventEmitter {
|
||||
|
||||
// Command
|
||||
command(ns, cmd, options, callback) {
|
||||
this.s.coreTopology.command(ns.toString(), cmd, translateReadPreference(options), callback);
|
||||
this.s.coreTopology.command(ns.toString(), cmd, ReadPreference.translate(options), callback);
|
||||
}
|
||||
|
||||
// Insert
|
||||
@@ -314,7 +314,7 @@ class TopologyBase extends EventEmitter {
|
||||
// IsConnected
|
||||
isConnected(options) {
|
||||
options = options || {};
|
||||
options = translateReadPreference(options);
|
||||
options = ReadPreference.translate(options);
|
||||
|
||||
return this.s.coreTopology.isConnected(options);
|
||||
}
|
||||
@@ -327,7 +327,7 @@ class TopologyBase extends EventEmitter {
|
||||
// Cursor
|
||||
cursor(ns, cmd, options) {
|
||||
options = options || {};
|
||||
options = translateReadPreference(options);
|
||||
options = ReadPreference.translate(options);
|
||||
options.disconnectHandler = this.s.store;
|
||||
options.topology = this;
|
||||
|
||||
|
||||
78
node_modules/mongodb/lib/utils.js
generated
vendored
78
node_modules/mongodb/lib/utils.js
generated
vendored
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
const MongoError = require('./core/error').MongoError;
|
||||
const ReadPreference = require('./core/topologies/read_preference');
|
||||
const WriteConcern = require('./write_concern');
|
||||
|
||||
var shallowClone = function(obj) {
|
||||
@@ -9,31 +8,6 @@ var shallowClone = function(obj) {
|
||||
return copy;
|
||||
};
|
||||
|
||||
// Figure out the read preference
|
||||
var translateReadPreference = function(options) {
|
||||
var r = null;
|
||||
if (options.readPreference) {
|
||||
r = options.readPreference;
|
||||
} else {
|
||||
return options;
|
||||
}
|
||||
|
||||
if (typeof r === 'string') {
|
||||
options.readPreference = new ReadPreference(r);
|
||||
} else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
|
||||
const mode = r.mode || r.preference;
|
||||
if (mode && typeof mode === 'string') {
|
||||
options.readPreference = new ReadPreference(mode, r.tags, {
|
||||
maxStalenessSeconds: r.maxStalenessSeconds
|
||||
});
|
||||
}
|
||||
} else if (!(r instanceof ReadPreference)) {
|
||||
throw new TypeError('Invalid read preference: ' + r);
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
// Set simple property
|
||||
var getSingleProperty = function(obj, name, value) {
|
||||
Object.defineProperty(obj, name, {
|
||||
@@ -490,37 +464,6 @@ function applyWriteConcern(target, sources, options) {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a read preference based on well-defined inheritance rules. This method will not only
|
||||
* determine the read preference (if there is one), but will also ensure the returned value is a
|
||||
* properly constructed instance of `ReadPreference`.
|
||||
*
|
||||
* @param {Collection|Db|MongoClient} parent The parent of the operation on which to determine the read
|
||||
* preference, used for determining the inherited read preference.
|
||||
* @param {Object} options The options passed into the method, potentially containing a read preference
|
||||
* @returns {(ReadPreference|null)} The resolved read preference
|
||||
*/
|
||||
function resolveReadPreference(parent, options) {
|
||||
options = options || {};
|
||||
const session = options.session;
|
||||
|
||||
const inheritedReadPreference = parent.readPreference;
|
||||
|
||||
let readPreference;
|
||||
if (options.readPreference) {
|
||||
readPreference = ReadPreference.fromOptions(options);
|
||||
} else if (session && session.inTransaction() && session.transaction.options.readPreference) {
|
||||
// The transaction’s read preference MUST override all other user configurable read preferences.
|
||||
readPreference = session.transaction.options.readPreference;
|
||||
} else if (inheritedReadPreference != null) {
|
||||
readPreference = inheritedReadPreference;
|
||||
} else {
|
||||
throw new Error('No readPreference was provided or inherited.');
|
||||
}
|
||||
|
||||
return typeof readPreference === 'string' ? new ReadPreference(readPreference) : readPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given value is a Promise
|
||||
*
|
||||
@@ -778,6 +721,12 @@ function makeInterruptableAsyncInterval(fn, options) {
|
||||
const timeUntilNextCall = Math.max(interval - timeSinceLastCall, 0);
|
||||
lastWakeTime = currentTime;
|
||||
|
||||
// For the streaming protocol: there is nothing obviously stopping this
|
||||
// interval from being woken up again while we are waiting "infinitely"
|
||||
// for `fn` to be called again`. Since the function effectively
|
||||
// never completes, the `timeUntilNextCall` will continue to grow
|
||||
// negatively unbounded, so it will never trigger a reschedule here.
|
||||
|
||||
// debounce multiple calls to wake within the `minInterval`
|
||||
if (timeSinceLastWake < minInterval) {
|
||||
return;
|
||||
@@ -810,6 +759,7 @@ function makeInterruptableAsyncInterval(fn, options) {
|
||||
function executeAndReschedule() {
|
||||
lastWakeTime = 0;
|
||||
lastCallTime = now();
|
||||
|
||||
fn(err => {
|
||||
if (err) throw err;
|
||||
reschedule(interval);
|
||||
@@ -826,6 +776,15 @@ function makeInterruptableAsyncInterval(fn, options) {
|
||||
return { wake, stop };
|
||||
}
|
||||
|
||||
function hasAtomicOperators(doc) {
|
||||
if (Array.isArray(doc)) {
|
||||
return doc.reduce((err, u) => err || hasAtomicOperators(u), null);
|
||||
}
|
||||
|
||||
const keys = Object.keys(doc);
|
||||
return keys.length > 0 && keys[0][0] === '$';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterOptions,
|
||||
mergeOptions,
|
||||
@@ -843,7 +802,6 @@ module.exports = {
|
||||
debugOptions,
|
||||
MAX_JS_INT: Number.MAX_SAFE_INTEGER + 1,
|
||||
mergeOptionsAndWriteConcern,
|
||||
translateReadPreference,
|
||||
executeLegacyOperation,
|
||||
applyRetryableWrites,
|
||||
applyWriteConcern,
|
||||
@@ -853,11 +811,11 @@ module.exports = {
|
||||
deprecateOptions,
|
||||
SUPPORTS,
|
||||
MongoDBNamespace,
|
||||
resolveReadPreference,
|
||||
emitDeprecationWarning,
|
||||
makeCounter,
|
||||
maybePromise,
|
||||
now,
|
||||
calculateDurationInMs,
|
||||
makeInterruptableAsyncInterval
|
||||
makeInterruptableAsyncInterval,
|
||||
hasAtomicOperators
|
||||
};
|
||||
|
||||
10
node_modules/mongodb/lib/write_concern.js
generated
vendored
10
node_modules/mongodb/lib/write_concern.js
generated
vendored
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const kWriteConcernKeys = new Set(['w', 'wtimeout', 'j', 'fsync']);
|
||||
|
||||
/**
|
||||
* The **WriteConcern** class is a class that represents a MongoDB WriteConcern.
|
||||
* @class
|
||||
@@ -51,6 +53,14 @@ class WriteConcern {
|
||||
}
|
||||
|
||||
if (options.writeConcern) {
|
||||
if (typeof options.writeConcern === 'string') {
|
||||
return new WriteConcern(options.writeConcern);
|
||||
}
|
||||
|
||||
if (!Object.keys(options.writeConcern).some(key => kWriteConcernKeys.has(key))) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new WriteConcern(
|
||||
options.writeConcern.w,
|
||||
options.writeConcern.wtimeout,
|
||||
|
||||
Reference in New Issue
Block a user