123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- "use strict";
- var parse = require('./url_parser')
- , Server = require('./server')
- , Mongos = require('./mongos')
- , ReplSet = require('./replset')
- , ReadPreference = require('./read_preference')
- , Db = require('./db');
- /**
- * @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
- *
- * @example
- * var MongoClient = require('mongodb').MongoClient,
- * test = require('assert');
- * // Connection url
- * var url = 'mongodb://localhost:27017/test';
- * // Connect using MongoClient
- * MongoClient.connect(url, function(err, db) {
- * // Get an additional db
- * db.close();
- * });
- */
- /**
- * Creates a new MongoClient instance
- * @class
- * @return {MongoClient} a MongoClient instance.
- */
- function MongoClient() {
- /**
- * The callback format for results
- * @callback MongoClient~connectCallback
- * @param {MongoError} error An error instance representing the error during the execution.
- * @param {Db} db The connected database.
- */
- /**
- * Connect to MongoDB using a url as documented at
- *
- * docs.mongodb.org/manual/reference/connection-string/
- *
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
- *
- * @method
- * @param {string} url The connection URI string
- * @param {object} [options=null] Optional settings.
- * @param {boolean} [options.uri_decode_auth=false] Uri decode the user name and password for authentication
- * @param {object} [options.db=null] A hash of options to set on the db object, see **Db constructor**
- * @param {object} [options.server=null] A hash of options to set on the server objects, see **Server** constructor**
- * @param {object} [options.replSet=null] A hash of options to set on the replSet object, see **ReplSet** constructor**
- * @param {object} [options.mongos=null] A hash of options to set on the mongos object, see **Mongos** constructor**
- * @param {MongoClient~connectCallback} callback The command result callback
- * @return {null}
- */
- this.connect = MongoClient.connect;
- }
- /**
- * Connect to MongoDB using a url as documented at
- *
- * docs.mongodb.org/manual/reference/connection-string/
- *
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
- *
- * @method
- * @static
- * @param {string} url The connection URI string
- * @param {object} [options=null] Optional settings.
- * @param {boolean} [options.uri_decode_auth=false] Uri decode the user name and password for authentication
- * @param {object} [options.db=null] A hash of options to set on the db object, see **Db constructor**
- * @param {object} [options.server=null] A hash of options to set on the server objects, see **Server** constructor**
- * @param {object} [options.replSet=null] A hash of options to set on the replSet object, see **ReplSet** constructor**
- * @param {object} [options.mongos=null] A hash of options to set on the mongos object, see **Mongos** constructor**
- * @param {MongoClient~connectCallback} callback The command result callback
- * @return {null}
- */
- MongoClient.connect = function(url, options, callback) {
- var args = Array.prototype.slice.call(arguments, 1);
- callback = typeof args[args.length - 1] == 'function' ? args.pop() : null;
- options = args.length ? args.shift() : null;
- options = options || {};
- // Set default empty server options
- var serverOptions = options.server || {};
- var mongosOptions = options.mongos || {};
- var replSetServersOptions = options.replSet || options.replSetServers || {};
- var dbOptions = options.db || {};
- // If callback is null throw an exception
- if(callback == null)
- throw new Error("no callback function provided");
- // Parse the string
- var object = parse(url, options);
- // Merge in any options for db in options object
- if(dbOptions) {
- for(var name in dbOptions) object.db_options[name] = dbOptions[name];
- }
- // Added the url to the options
- object.db_options.url = url;
- // Merge in any options for server in options object
- if(serverOptions) {
- for(var name in serverOptions) object.server_options[name] = serverOptions[name];
- }
- // Merge in any replicaset server options
- if(replSetServersOptions) {
- for(var name in replSetServersOptions) object.rs_options[name] = replSetServersOptions[name];
- }
- if(replSetServersOptions.ssl
- || replSetServersOptions.sslValidate
- || replSetServersOptions.sslCA
- || replSetServersOptions.sslCert
- || replSetServersOptions.sslKey
- || replSetServersOptions.sslPass) {
- object.server_options.ssl = replSetServersOptions.ssl;
- object.server_options.sslValidate = replSetServersOptions.sslValidate;
- object.server_options.sslCA = replSetServersOptions.sslCA;
- object.server_options.sslCert = replSetServersOptions.sslCert;
- object.server_options.sslKey = replSetServersOptions.sslKey;
- object.server_options.sslPass = replSetServersOptions.sslPass;
- }
- // Merge in any replicaset server options
- if(mongosOptions) {
- for(var name in mongosOptions) object.mongos_options[name] = mongosOptions[name];
- }
- if(typeof object.server_options.poolSize == 'number') {
- if(!object.mongos_options.poolSize) object.mongos_options.poolSize = object.server_options.poolSize;
- if(!object.rs_options.poolSize) object.rs_options.poolSize = object.server_options.poolSize;
- }
- if(mongosOptions.ssl
- || mongosOptions.sslValidate
- || mongosOptions.sslCA
- || mongosOptions.sslCert
- || mongosOptions.sslKey
- || mongosOptions.sslPass) {
- object.server_options.ssl = mongosOptions.ssl;
- object.server_options.sslValidate = mongosOptions.sslValidate;
- object.server_options.sslCA = mongosOptions.sslCA;
- object.server_options.sslCert = mongosOptions.sslCert;
- object.server_options.sslKey = mongosOptions.sslKey;
- object.server_options.sslPass = mongosOptions.sslPass;
- }
- // We need to ensure that the list of servers are only either direct members or mongos
- // they cannot be a mix of monogs and mongod's
- var totalNumberOfServers = object.servers.length;
- var totalNumberOfMongosServers = 0;
- var totalNumberOfMongodServers = 0;
- var serverConfig = null;
- var errorServers = {};
- // Failure modes
- if(object.servers.length == 0) throw new Error("connection string must contain at least one seed host");
- // If we have no db setting for the native parser try to set the c++ one first
- object.db_options.native_parser = _setNativeParser(object.db_options);
- // If no auto_reconnect is set, set it to true as default for single servers
- if(typeof object.server_options.auto_reconnect != 'boolean') {
- object.server_options.auto_reconnect = true;
- }
- // If we have more than a server, it could be replicaset or mongos list
- // need to verify that it's one or the other and fail if it's a mix
- // Connect to all servers and run ismaster
- for(var i = 0; i < object.servers.length; i++) {
- // Set up socket options
- var providedSocketOptions = object.server_options.socketOptions || {};
- var _server_options = {
- poolSize:1
- , socketOptions: {
- connectTimeoutMS: providedSocketOptions.connectTimeoutMS || 30000
- , socketTimeoutMS: providedSocketOptions.socketTimeoutMS || 30000
- }
- , auto_reconnect:false};
- // Ensure we have ssl setup for the servers
- if(object.server_options.ssl) {
- _server_options.ssl = object.server_options.ssl;
- _server_options.sslValidate = object.server_options.sslValidate;
- _server_options.sslCA = object.server_options.sslCA;
- _server_options.sslCert = object.server_options.sslCert;
- _server_options.sslKey = object.server_options.sslKey;
- _server_options.sslPass = object.server_options.sslPass;
- } else if(object.rs_options.ssl) {
- _server_options.ssl = object.rs_options.ssl;
- _server_options.sslValidate = object.rs_options.sslValidate;
- _server_options.sslCA = object.rs_options.sslCA;
- _server_options.sslCert = object.rs_options.sslCert;
- _server_options.sslKey = object.rs_options.sslKey;
- _server_options.sslPass = object.rs_options.sslPass;
- }
- // Error
- var error = null;
- // Set up the Server object
- var _server = object.servers[i].domain_socket
- ? new Server(object.servers[i].domain_socket, _server_options)
- : new Server(object.servers[i].host, object.servers[i].port, _server_options);
- var setName;
- var connectFunction = function(__server) {
- // Attempt connect
- new Db(object.dbName, __server, {w:1, native_parser:false}).open(function(err, db) {
- // Update number of servers
- totalNumberOfServers = totalNumberOfServers - 1;
- // If no error do the correct checks
- if(!err) {
- // Close the connection
- db.close();
- var isMasterDoc = db.serverConfig.isMasterDoc;
- // Check what type of server we have
- if(isMasterDoc.setName) {
- totalNumberOfMongodServers++;
- setName = isMasterDoc.setName;
- }
- if(isMasterDoc.msg && isMasterDoc.msg == "isdbgrid") totalNumberOfMongosServers++;
- } else {
- error = err;
- errorServers[__server.host + ":" + __server.port] = __server;
- }
- if(totalNumberOfServers == 0) {
- // Error out
- if(totalNumberOfMongodServers == 0 && totalNumberOfMongosServers == 0 && error) {
- return callback(error, null);
- }
- // If we have a mix of mongod and mongos, throw an error
- if(totalNumberOfMongosServers > 0 && totalNumberOfMongodServers > 0) {
- if(db) db.close();
- return process.nextTick(function() {
- try {
- callback(new Error("cannot combine a list of replicaset seeds and mongos seeds"));
- } catch (err) {
- throw err
- }
- })
- }
- if(totalNumberOfMongodServers == 0
- && totalNumberOfMongosServers == 0
- && object.servers.length == 1
- && (!object.rs_options.replicaSet || !object.rs_options.rs_name)) {
- var obj = object.servers[0];
- serverConfig = obj.domain_socket ?
- new Server(obj.domain_socket, object.server_options)
- : new Server(obj.host, obj.port, object.server_options);
- } else if(totalNumberOfMongodServers > 0
- || totalNumberOfMongosServers > 0
- || object.rs_options.replicaSet || object.rs_options.rs_name) {
- var finalServers = object.servers
- .filter(function(serverObj) {
- return errorServers[serverObj.host + ":" + serverObj.port] == null;
- })
- .map(function(serverObj) {
- return new Server(serverObj.host, serverObj.port, object.server_options);
- });
- // Clean out any error servers
- errorServers = {};
- // Set up the final configuration
- if(totalNumberOfMongodServers > 0) {
- try {
- if (totalNumberOfMongodServers == 1) {
- object.rs_options.replicaSet = object.rs_options.replicaSet || setName;
- }
- serverConfig = new ReplSet(finalServers, object.rs_options);
- } catch(err) {
- return callback(err, null);
- }
- } else {
- serverConfig = new Mongos(finalServers, object.mongos_options);
- }
- }
- if(serverConfig == null) {
- return process.nextTick(function() {
- try {
- callback(new Error("Could not locate any valid servers in initial seed list"));
- } catch (err) {
- if(db) db.close();
- throw err
- }
- });
- }
- // Ensure no firing of open event before we are ready
- serverConfig.emitOpen = false;
- // Set up all options etc and connect to the database
- _finishConnecting(serverConfig, object, options, callback)
- }
- });
- }
- // Wrap the context of the call
- connectFunction(_server);
- }
- }
- var _setNativeParser = function(db_options) {
- if(typeof db_options.native_parser == 'boolean') return db_options.native_parser;
- try {
- require('mongodb-core').BSON.BSONNative.BSON;
- return true;
- } catch(err) {
- return false;
- }
- }
- var _finishConnecting = function(serverConfig, object, options, callback) {
- // If we have a readPreference passed in by the db options
- if(typeof object.db_options.readPreference == 'string') {
- object.db_options.readPreference = new ReadPreference(object.db_options.readPreference);
- } else if(typeof object.db_options.read_preference == 'string') {
- object.db_options.readPreference = new ReadPreference(object.db_options.read_preference);
- }
- // Do we have readPreference tags
- if(object.db_options.readPreference && object.db_options.readPreferenceTags) {
- object.db_options.readPreference.tags = object.db_options.readPreferenceTags;
- } else if(object.db_options.readPreference && object.db_options.read_preference_tags) {
- object.db_options.readPreference.tags = object.db_options.read_preference_tags;
- }
- // Get the socketTimeoutMS
- var socketTimeoutMS = object.server_options.socketOptions.socketTimeoutMS || 0;
- // If we have a replset, override with replicaset socket timeout option if available
- if(serverConfig instanceof ReplSet) {
- socketTimeoutMS = object.rs_options.socketOptions.socketTimeoutMS || socketTimeoutMS;
- }
- // Set socketTimeout to the same as the connectTimeoutMS or 30 sec
- serverConfig.connectTimeoutMS = serverConfig.connectTimeoutMS || 30000;
- serverConfig.socketTimeoutMS = serverConfig.connectTimeoutMS;
- // Set up the db options
- var db = new Db(object.dbName, serverConfig, object.db_options);
- // Open the db
- db.open(function(err, db){
- if(err) {
- return process.nextTick(function() {
- try {
- callback(err, null);
- } catch (err) {
- if(db) db.close();
- throw err
- }
- });
- }
- // Reset the socket timeout
- serverConfig.socketTimeoutMS = socketTimeoutMS || 0;
- // Return object
- if(err == null && object.auth){
- // What db to authenticate against
- var authentication_db = db;
- if(object.db_options && object.db_options.authSource) {
- authentication_db = db.db(object.db_options.authSource);
- }
- // Build options object
- var options = {};
- if(object.db_options.authMechanism) options.authMechanism = object.db_options.authMechanism;
- if(object.db_options.gssapiServiceName) options.gssapiServiceName = object.db_options.gssapiServiceName;
- // Authenticate
- authentication_db.authenticate(object.auth.user, object.auth.password, options, function(err, success){
- if(success){
- process.nextTick(function() {
- try {
- callback(null, db);
- } catch (err) {
- if(db) db.close();
- throw err
- }
- });
- } else {
- if(db) db.close();
- process.nextTick(function() {
- try {
- callback(err ? err : new Error('Could not authenticate user ' + object.auth[0]), null);
- } catch (err) {
- if(db) db.close();
- throw err
- }
- });
- }
- });
- } else {
- process.nextTick(function() {
- try {
- callback(err, db);
- } catch (err) {
- if(db) db.close();
- throw err
- }
- })
- }
- });
- }
- module.exports = MongoClient
|