PageRenderTime 33ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/node_modules/mongodb/lib/mongodb/connection/base.js

https://bitbucket.org/james_gibson/goo-api
JavaScript | 465 lines | 267 code | 58 blank | 140 comment | 85 complexity | 677792c17aea0479e69d124b760d7061 MD5 | raw file
  1. var EventEmitter = require('events').EventEmitter
  2. , inherits = require('util').inherits
  3. , mongodb_cr_authenticate = require('../auth/mongodb_cr.js').authenticate
  4. , mongodb_gssapi_authenticate = require('../auth/mongodb_gssapi.js').authenticate
  5. , mongodb_sspi_authenticate = require('../auth/mongodb_sspi.js').authenticate;
  6. var id = 0;
  7. /**
  8. * Internal class for callback storage
  9. * @ignore
  10. */
  11. var CallbackStore = function() {
  12. // Make class an event emitter
  13. EventEmitter.call(this);
  14. // Add a info about call variable
  15. this._notReplied = {};
  16. this.id = id++;
  17. }
  18. /**
  19. * @ignore
  20. */
  21. inherits(CallbackStore, EventEmitter);
  22. CallbackStore.prototype.notRepliedToIds = function() {
  23. return Object.keys(this._notReplied);
  24. }
  25. CallbackStore.prototype.callbackInfo = function(id) {
  26. return this._notReplied[id];
  27. }
  28. /**
  29. * Internal class for holding non-executed commands
  30. * @ignore
  31. */
  32. var NonExecutedOperationStore = function(config) {
  33. this.config = config;
  34. this.commands = {
  35. read: []
  36. , write_reads: []
  37. , write: []
  38. };
  39. }
  40. NonExecutedOperationStore.prototype.write = function(op) {
  41. this.commands.write.push(op);
  42. }
  43. NonExecutedOperationStore.prototype.read_from_writer = function(op) {
  44. this.commands.write_reads.push(op);
  45. }
  46. NonExecutedOperationStore.prototype.read = function(op) {
  47. this.commands.read.push(op);
  48. }
  49. NonExecutedOperationStore.prototype.execute_queries = function(executeInsertCommand) {
  50. var connection = this.config.checkoutReader();
  51. if(connection == null || connection instanceof Error) return;
  52. // Write out all the queries
  53. while(this.commands.read.length > 0) {
  54. // Get the next command
  55. var command = this.commands.read.shift();
  56. // command['options'].connection = this.config.checkoutReader();
  57. command.options.connection = connection;
  58. // Execute the next command
  59. command.executeQueryCommand(command.db, command.db_command, command.options, command.callback);
  60. }
  61. }
  62. NonExecutedOperationStore.prototype.execute_writes = function() {
  63. var connection = this.config.checkoutWriter();
  64. if(connection == null || connection instanceof Error) return;
  65. // Write out all the queries to the primary
  66. while(this.commands.write_reads.length > 0) {
  67. // Get the next command
  68. var command = this.commands.write_reads.shift();
  69. command.options.connection = connection;
  70. // Execute the next command
  71. command.executeQueryCommand(command.db, command.db_command, command.options, command.callback);
  72. }
  73. // Execute all write operations
  74. while(this.commands.write.length > 0) {
  75. // Get the next command
  76. var command = this.commands.write.shift();
  77. // Set the connection
  78. command.options.connection = connection;
  79. // Execute the next command
  80. command.executeInsertCommand(command.db, command.db_command, command.options, command.callback);
  81. }
  82. }
  83. /**
  84. * Internal class for authentication storage
  85. * @ignore
  86. */
  87. var AuthStore = function() {
  88. this._auths = [];
  89. }
  90. AuthStore.prototype.add = function(authMechanism, dbName, username, password, authdbName) {
  91. // Check for duplicates
  92. if(!this.contains(dbName)) {
  93. // Base config
  94. var config = {
  95. 'username':username
  96. , 'password':password
  97. , 'db': dbName
  98. , 'authMechanism': authMechanism
  99. };
  100. // Add auth source if passed in
  101. if(typeof authdbName == 'string') {
  102. config['authdb'] = authdbName;
  103. }
  104. // Push the config
  105. this._auths.push(config);
  106. }
  107. }
  108. AuthStore.prototype.contains = function(dbName) {
  109. for(var i = 0; i < this._auths.length; i++) {
  110. if(this._auths[i].db == dbName) return true;
  111. }
  112. return false;
  113. }
  114. AuthStore.prototype.remove = function(dbName) {
  115. var newAuths = [];
  116. // Filter out all the login details
  117. for(var i = 0; i < this._auths.length; i++) {
  118. if(this._auths[i].db != dbName) newAuths.push(this._auths[i]);
  119. }
  120. // Set the filtered list
  121. this._auths = newAuths;
  122. }
  123. AuthStore.prototype.get = function(index) {
  124. return this._auths[index];
  125. }
  126. AuthStore.prototype.length = function() {
  127. return this._auths.length;
  128. }
  129. AuthStore.prototype.toArray = function() {
  130. return this._auths.slice(0);
  131. }
  132. /**
  133. * Internal class for storing db references
  134. * @ignore
  135. */
  136. var DbStore = function() {
  137. this._dbs = [];
  138. }
  139. DbStore.prototype.add = function(db) {
  140. // this._dbs.push(db);
  141. var found = false;
  142. // Only add if it does not exist already
  143. for(var i = 0; i < this._dbs.length; i++) {
  144. if(db.databaseName == this._dbs[i].databaseName) found = true;
  145. }
  146. if(!found) this._dbs.push(db);
  147. }
  148. DbStore.prototype.reset = function() {
  149. this._dbs = [];
  150. }
  151. DbStore.prototype.emit = function(event, message, object, reset, filterDb) {
  152. if(reset) {
  153. while(this._dbs.length > 0) {
  154. var db = this._dbs.shift();
  155. // Only emit if there is a listener
  156. if(db.listeners(event).length > 0) {
  157. if(filterDb == null || filterDb.databaseName !== db.databaseName
  158. || filterDb.tag !== db.tag) {
  159. db.emit(event, message, object);
  160. }
  161. }
  162. }
  163. } else {
  164. for(var i = 0; i < this._dbs.length; i++) {
  165. if(this._dbs[i].listeners(event).length > 0) {
  166. if(filterDb == null || filterDb.databaseName !== this._dbs[i].databaseName
  167. || filterDb.tag !== this._dbs[i].tag) {
  168. this._dbs[i].emit(event, message, object);
  169. }
  170. }
  171. }
  172. }
  173. }
  174. var Base = function Base() {
  175. EventEmitter.call(this);
  176. // Callback store is part of connection specification
  177. if(Base._callBackStore == null) {
  178. Base._callBackStore = new CallbackStore();
  179. }
  180. // Create a new callback store
  181. this._callBackStore = new CallbackStore();
  182. // All commands not being executed
  183. this._commandsStore = new NonExecutedOperationStore(this);
  184. // Create a new auth store
  185. this.auth = new AuthStore();
  186. // Contains all the dbs attached to this server config
  187. this._dbStore = new DbStore();
  188. }
  189. /**
  190. * @ignore
  191. */
  192. inherits(Base, EventEmitter);
  193. /**
  194. * @ignore
  195. */
  196. Base.prototype._apply_auths = function(db, callback) {
  197. _apply_auths_serially(this, db, this.auth.toArray(), callback);
  198. }
  199. var _apply_auths_serially = function(self, db, auths, callback) {
  200. if(auths.length == 0) return callback(null, null);
  201. // Get the first auth
  202. var auth = auths.shift();
  203. var connections = self.allRawConnections();
  204. var connectionsLeft = connections.length;
  205. // Let's apply it to all raw connections
  206. for(var i = 0; i < connections.length; i++) {
  207. if(auth.authMechanism == 'GSSAPI') {
  208. var options = {connection: connections[i]};
  209. var connectionHandler = function(err, result) {
  210. connectionsLeft = connectionsLeft - 1;
  211. // If no more connections are left return
  212. if(connectionsLeft == 0) {
  213. return _apply_auths_serially(self, db, auths, callback);
  214. }
  215. }
  216. // We have the kerberos library, execute auth process
  217. if(process.platform == 'win32') {
  218. mongodb_sspi_authenticate(db, auth.username, auth.password, auth.authdb, options, callback);
  219. } else {
  220. mongodb_gssapi_authenticate(db, auth.username, auth.password, auth.authdb, options, callback);
  221. }
  222. } else if(auth.authMechanism == 'MONGODB-CR') {
  223. mongodb_cr_authenticate(db, auth.username, auth.password, auth.authdb, options, callback);
  224. }
  225. }
  226. }
  227. /**
  228. * Fire all the errors
  229. * @ignore
  230. */
  231. Base.prototype.__executeAllCallbacksWithError = function(err) {
  232. // Check all callbacks
  233. var keys = Object.keys(this._callBackStore._notReplied);
  234. // For each key check if it's a callback that needs to be returned
  235. for(var j = 0; j < keys.length; j++) {
  236. var info = this._callBackStore._notReplied[keys[j]];
  237. // Check if we have a chained command (findAndModify)
  238. if(info && info['chained'] && Array.isArray(info['chained']) && info['chained'].length > 0) {
  239. var chained = info['chained'];
  240. // Only callback once and the last one is the right one
  241. var finalCallback = chained.pop();
  242. // Emit only the last event
  243. this._callBackStore.emit(finalCallback, err, null);
  244. // Put back the final callback to ensure we don't call all commands in the chain
  245. chained.push(finalCallback);
  246. // Remove all chained callbacks
  247. for(var i = 0; i < chained.length; i++) {
  248. delete this._callBackStore._notReplied[chained[i]];
  249. }
  250. // Remove the key
  251. delete this._callBackStore._notReplied[keys[j]];
  252. } else {
  253. this._callBackStore.emit(keys[j], err, null);
  254. // Remove the key
  255. delete this._callBackStore._notReplied[keys[j]];
  256. }
  257. }
  258. }
  259. /**
  260. * Fire all the errors
  261. * @ignore
  262. */
  263. Base.prototype.__executeAllServerSpecificErrorCallbacks = function(host, port, err) {
  264. // Check all callbacks
  265. var keys = Object.keys(this._callBackStore._notReplied);
  266. // For each key check if it's a callback that needs to be returned
  267. for(var j = 0; j < keys.length; j++) {
  268. var info = this._callBackStore._notReplied[keys[j]];
  269. if(info.connection) {
  270. // Unpack the connection settings
  271. var _host = info.connection.socketOptions.host;
  272. var _port = info.connection.socketOptions.port;
  273. // Check if we have a chained command (findAndModify)
  274. if(info && info['chained']
  275. && Array.isArray(info['chained'])
  276. && info['chained'].length > 0
  277. && _port == port && _host == host) {
  278. var chained = info['chained'];
  279. // Only callback once and the last one is the right one
  280. var finalCallback = chained.pop();
  281. // Emit only the last event
  282. this._callBackStore.emit(finalCallback, err, null);
  283. // Put back the final callback to ensure we don't call all commands in the chain
  284. chained.push(finalCallback);
  285. // Remove all chained callbacks
  286. for(var i = 0; i < chained.length; i++) {
  287. delete this._callBackStore._notReplied[chained[i]];
  288. }
  289. // Remove the key
  290. delete this._callBackStore._notReplied[keys[j]];
  291. } else if(_port == port && _host == host) {
  292. this._callBackStore.emit(keys[j], err, null);
  293. // Remove the key
  294. delete this._callBackStore._notReplied[keys[j]];
  295. }
  296. }
  297. }
  298. }
  299. /**
  300. * Register a handler
  301. * @ignore
  302. * @api private
  303. */
  304. Base.prototype._registerHandler = function(db_command, raw, connection, exhaust, callback) {
  305. // If we have an array of commands, chain them
  306. var chained = Array.isArray(db_command);
  307. // Check if we have exhausted
  308. if(typeof exhaust == 'function') {
  309. callback = exhaust;
  310. exhaust = false;
  311. }
  312. // If they are chained we need to add a special handler situation
  313. if(chained) {
  314. // List off chained id's
  315. var chainedIds = [];
  316. // Add all id's
  317. for(var i = 0; i < db_command.length; i++) chainedIds.push(db_command[i].getRequestId().toString());
  318. // Register all the commands together
  319. for(var i = 0; i < db_command.length; i++) {
  320. var command = db_command[i];
  321. // Add the callback to the store
  322. this._callBackStore.once(command.getRequestId(), callback);
  323. // Add the information about the reply
  324. this._callBackStore._notReplied[command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, chained:chainedIds, connection:connection, exhaust:false};
  325. }
  326. } else {
  327. // Add the callback to the list of handlers
  328. this._callBackStore.once(db_command.getRequestId(), callback);
  329. // Add the information about the reply
  330. this._callBackStore._notReplied[db_command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, connection:connection, exhaust:exhaust};
  331. }
  332. }
  333. /**
  334. * Re-Register a handler, on the cursor id f.ex
  335. * @ignore
  336. * @api private
  337. */
  338. Base.prototype._reRegisterHandler = function(newId, object, callback) {
  339. // Add the callback to the list of handlers
  340. this._callBackStore.once(newId, object.callback.listener);
  341. // Add the information about the reply
  342. this._callBackStore._notReplied[newId] = object.info;
  343. }
  344. /**
  345. *
  346. * @ignore
  347. * @api private
  348. */
  349. Base.prototype._callHandler = function(id, document, err) {
  350. // If there is a callback peform it
  351. if(this._callBackStore.listeners(id).length >= 1) {
  352. // Get info object
  353. var info = this._callBackStore._notReplied[id];
  354. // Delete the current object
  355. delete this._callBackStore._notReplied[id];
  356. // Emit to the callback of the object
  357. this._callBackStore.emit(id, err, document, info.connection);
  358. }
  359. }
  360. /**
  361. *
  362. * @ignore
  363. * @api private
  364. */
  365. Base.prototype._hasHandler = function(id) {
  366. // If there is a callback peform it
  367. return this._callBackStore.listeners(id).length >= 1;
  368. }
  369. /**
  370. *
  371. * @ignore
  372. * @api private
  373. */
  374. Base.prototype._removeHandler = function(id) {
  375. // Remove the information
  376. if(this._callBackStore._notReplied[id] != null) delete this._callBackStore._notReplied[id];
  377. // Remove the callback if it's registered
  378. this._callBackStore.removeAllListeners(id);
  379. // Force cleanup _events, node.js seems to set it as a null value
  380. if(this._callBackStore._events != null) delete this._callBackStore._events[id];
  381. }
  382. /**
  383. *
  384. * @ignore
  385. * @api private
  386. */
  387. Base.prototype._findHandler = function(id) {
  388. var info = this._callBackStore._notReplied[id];
  389. // Return the callback
  390. return {info:info, callback:(this._callBackStore.listeners(id).length >= 1) ? this._callBackStore.listeners(id)[0] : null}
  391. }
  392. /**
  393. *
  394. * @ignore
  395. * @api private
  396. */
  397. Base.prototype._emitAcrossAllDbInstances = function(server, filterDb, event, message, object, resetConnection) {
  398. if(resetConnection) {
  399. for(var i = 0; i < this._dbStore._dbs.length; i++) {
  400. if(typeof this._dbStore._dbs[i].openCalled != 'undefined')
  401. this._dbStore._dbs[i].openCalled = false;
  402. }
  403. }
  404. // Fire event
  405. this._dbStore.emit(event, message, object, resetConnection, filterDb);
  406. }
  407. exports.Base = Base;