Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

connection.js 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. 'use strict';
  2. var inherits = require('util').inherits,
  3. EventEmitter = require('events').EventEmitter,
  4. net = require('net'),
  5. tls = require('tls'),
  6. crypto = require('crypto'),
  7. f = require('util').format,
  8. debugOptions = require('./utils').debugOptions,
  9. parseHeader = require('../wireprotocol/shared').parseHeader,
  10. decompress = require('../wireprotocol/compression').decompress,
  11. Response = require('./commands').Response,
  12. MongoNetworkError = require('../error').MongoNetworkError,
  13. Logger = require('./logger'),
  14. OP_COMPRESSED = require('../wireprotocol/shared').opcodes.OP_COMPRESSED,
  15. MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE,
  16. Buffer = require('safe-buffer').Buffer;
  17. var _id = 0;
  18. var debugFields = [
  19. 'host',
  20. 'port',
  21. 'size',
  22. 'keepAlive',
  23. 'keepAliveInitialDelay',
  24. 'noDelay',
  25. 'connectionTimeout',
  26. 'socketTimeout',
  27. 'singleBufferSerializtion',
  28. 'ssl',
  29. 'ca',
  30. 'crl',
  31. 'cert',
  32. 'rejectUnauthorized',
  33. 'promoteLongs',
  34. 'promoteValues',
  35. 'promoteBuffers',
  36. 'checkServerIdentity'
  37. ];
  38. var connectionAccountingSpy = undefined;
  39. var connectionAccounting = false;
  40. var connections = {};
  41. /**
  42. * Creates a new Connection instance
  43. * @class
  44. * @param {string} options.host The server host
  45. * @param {number} options.port The server port
  46. * @param {number} [options.family=null] IP version for DNS lookup, passed down to Node's [`dns.lookup()` function](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback). If set to `6`, will only look for ipv6 addresses.
  47. * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
  48. * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
  49. * @param {boolean} [options.noDelay=true] TCP Connection no delay
  50. * @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
  51. * @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
  52. * @param {boolean} [options.singleBufferSerializtion=true] Serialize into single buffer, trade of peak memory for serialization speed
  53. * @param {boolean} [options.ssl=false] Use SSL for connection
  54. * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
  55. * @param {Buffer} [options.ca] SSL Certificate store binary buffer
  56. * @param {Buffer} [options.crl] SSL Certificate revocation store binary buffer
  57. * @param {Buffer} [options.cert] SSL Certificate binary buffer
  58. * @param {Buffer} [options.key] SSL Key file binary buffer
  59. * @param {string} [options.passphrase] SSL Certificate pass phrase
  60. * @param {boolean} [options.rejectUnauthorized=true] Reject unauthorized server certificates
  61. * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
  62. * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
  63. * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
  64. * @fires Connection#connect
  65. * @fires Connection#close
  66. * @fires Connection#error
  67. * @fires Connection#timeout
  68. * @fires Connection#parseError
  69. * @return {Connection} A cursor instance
  70. */
  71. var Connection = function(messageHandler, options) {
  72. // Add event listener
  73. EventEmitter.call(this);
  74. // Set empty if no options passed
  75. this.options = options || {};
  76. // Identification information
  77. this.id = _id++;
  78. // Logger instance
  79. this.logger = Logger('Connection', options);
  80. // No bson parser passed in
  81. if (!options.bson) throw new Error('must pass in valid bson parser');
  82. // Get bson parser
  83. this.bson = options.bson;
  84. // Grouping tag used for debugging purposes
  85. this.tag = options.tag;
  86. // Message handler
  87. this.messageHandler = messageHandler;
  88. // Max BSON message size
  89. this.maxBsonMessageSize = options.maxBsonMessageSize || 1024 * 1024 * 16 * 4;
  90. // Debug information
  91. if (this.logger.isDebug())
  92. this.logger.debug(
  93. f(
  94. 'creating connection %s with options [%s]',
  95. this.id,
  96. JSON.stringify(debugOptions(debugFields, options))
  97. )
  98. );
  99. // Default options
  100. this.port = options.port || 27017;
  101. this.host = options.host || 'localhost';
  102. this.family = typeof options.family === 'number' ? options.family : void 0;
  103. this.keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
  104. this.keepAliveInitialDelay =
  105. typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
  106. this.noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
  107. this.connectionTimeout =
  108. typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
  109. this.socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
  110. // Is the keepAliveInitialDelay > socketTimeout set it to half of socketTimeout
  111. if (this.keepAliveInitialDelay > this.socketTimeout) {
  112. this.keepAliveInitialDelay = Math.round(this.socketTimeout / 2);
  113. }
  114. // If connection was destroyed
  115. this.destroyed = false;
  116. // Check if we have a domain socket
  117. this.domainSocket = this.host.indexOf('/') !== -1;
  118. // Serialize commands using function
  119. this.singleBufferSerializtion =
  120. typeof options.singleBufferSerializtion === 'boolean' ? options.singleBufferSerializtion : true;
  121. this.serializationFunction = this.singleBufferSerializtion ? 'toBinUnified' : 'toBin';
  122. // SSL options
  123. this.ca = options.ca || null;
  124. this.crl = options.crl || null;
  125. this.cert = options.cert || null;
  126. this.key = options.key || null;
  127. this.passphrase = options.passphrase || null;
  128. this.ciphers = options.ciphers || null;
  129. this.ecdhCurve = options.ecdhCurve || null;
  130. this.ssl = typeof options.ssl === 'boolean' ? options.ssl : false;
  131. this.rejectUnauthorized =
  132. typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
  133. this.checkServerIdentity =
  134. typeof options.checkServerIdentity === 'boolean' ||
  135. typeof options.checkServerIdentity === 'function'
  136. ? options.checkServerIdentity
  137. : true;
  138. // If ssl not enabled
  139. if (!this.ssl) this.rejectUnauthorized = false;
  140. // Response options
  141. this.responseOptions = {
  142. promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
  143. promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
  144. promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false
  145. };
  146. // Flushing
  147. this.flushing = false;
  148. this.queue = [];
  149. // Internal state
  150. this.connection = null;
  151. this.writeStream = null;
  152. // Create hash method
  153. var hash = crypto.createHash('sha1');
  154. hash.update(f('%s:%s', this.host, this.port));
  155. // Create a hash name
  156. this.hashedName = hash.digest('hex');
  157. // All operations in flight on the connection
  158. this.workItems = [];
  159. };
  160. inherits(Connection, EventEmitter);
  161. Connection.prototype.setSocketTimeout = function(value) {
  162. if (this.connection) {
  163. this.connection.setTimeout(value);
  164. }
  165. };
  166. Connection.prototype.resetSocketTimeout = function() {
  167. if (this.connection) {
  168. this.connection.setTimeout(this.socketTimeout);
  169. }
  170. };
  171. Connection.enableConnectionAccounting = function(spy) {
  172. if (spy) {
  173. connectionAccountingSpy = spy;
  174. }
  175. connectionAccounting = true;
  176. connections = {};
  177. };
  178. Connection.disableConnectionAccounting = function() {
  179. connectionAccounting = false;
  180. connectionAccountingSpy = undefined;
  181. };
  182. Connection.connections = function() {
  183. return connections;
  184. };
  185. function deleteConnection(id) {
  186. // console.log("=== deleted connection " + id + " :: " + (connections[id] ? connections[id].port : ''))
  187. delete connections[id];
  188. if (connectionAccountingSpy) {
  189. connectionAccountingSpy.deleteConnection(id);
  190. }
  191. }
  192. function addConnection(id, connection) {
  193. // console.log("=== added connection " + id + " :: " + connection.port)
  194. connections[id] = connection;
  195. if (connectionAccountingSpy) {
  196. connectionAccountingSpy.addConnection(id, connection);
  197. }
  198. }
  199. //
  200. // Connection handlers
  201. var errorHandler = function(self) {
  202. return function(err) {
  203. if (connectionAccounting) deleteConnection(self.id);
  204. // Debug information
  205. if (self.logger.isDebug())
  206. self.logger.debug(
  207. f(
  208. 'connection %s for [%s:%s] errored out with [%s]',
  209. self.id,
  210. self.host,
  211. self.port,
  212. JSON.stringify(err)
  213. )
  214. );
  215. // Emit the error
  216. if (self.listeners('error').length > 0) self.emit('error', new MongoNetworkError(err), self);
  217. };
  218. };
  219. var timeoutHandler = function(self) {
  220. return function() {
  221. if (connectionAccounting) deleteConnection(self.id);
  222. // Debug information
  223. if (self.logger.isDebug())
  224. self.logger.debug(f('connection %s for [%s:%s] timed out', self.id, self.host, self.port));
  225. // Emit timeout error
  226. self.emit(
  227. 'timeout',
  228. new MongoNetworkError(f('connection %s to %s:%s timed out', self.id, self.host, self.port)),
  229. self
  230. );
  231. };
  232. };
  233. var closeHandler = function(self) {
  234. return function(hadError) {
  235. if (connectionAccounting) deleteConnection(self.id);
  236. // Debug information
  237. if (self.logger.isDebug())
  238. self.logger.debug(f('connection %s with for [%s:%s] closed', self.id, self.host, self.port));
  239. // Emit close event
  240. if (!hadError) {
  241. self.emit(
  242. 'close',
  243. new MongoNetworkError(f('connection %s to %s:%s closed', self.id, self.host, self.port)),
  244. self
  245. );
  246. }
  247. };
  248. };
  249. // Handle a message once it is received
  250. var emitMessageHandler = function(self, message) {
  251. var msgHeader = parseHeader(message);
  252. if (msgHeader.opCode === OP_COMPRESSED) {
  253. msgHeader.fromCompressed = true;
  254. var index = MESSAGE_HEADER_SIZE;
  255. msgHeader.opCode = message.readInt32LE(index);
  256. index += 4;
  257. msgHeader.length = message.readInt32LE(index);
  258. index += 4;
  259. var compressorID = message[index];
  260. index++;
  261. decompress(compressorID, message.slice(index), function(err, decompressedMsgBody) {
  262. if (err) {
  263. throw err;
  264. }
  265. if (decompressedMsgBody.length !== msgHeader.length) {
  266. throw new Error(
  267. 'Decompressing a compressed message from the server failed. The message is corrupt.'
  268. );
  269. }
  270. self.messageHandler(
  271. new Response(self.bson, message, msgHeader, decompressedMsgBody, self.responseOptions),
  272. self
  273. );
  274. });
  275. } else {
  276. self.messageHandler(
  277. new Response(
  278. self.bson,
  279. message,
  280. msgHeader,
  281. message.slice(MESSAGE_HEADER_SIZE),
  282. self.responseOptions
  283. ),
  284. self
  285. );
  286. }
  287. };
  288. var dataHandler = function(self) {
  289. return function(data) {
  290. // Parse until we are done with the data
  291. while (data.length > 0) {
  292. // If we still have bytes to read on the current message
  293. if (self.bytesRead > 0 && self.sizeOfMessage > 0) {
  294. // Calculate the amount of remaining bytes
  295. var remainingBytesToRead = self.sizeOfMessage - self.bytesRead;
  296. // Check if the current chunk contains the rest of the message
  297. if (remainingBytesToRead > data.length) {
  298. // Copy the new data into the exiting buffer (should have been allocated when we know the message size)
  299. data.copy(self.buffer, self.bytesRead);
  300. // Adjust the number of bytes read so it point to the correct index in the buffer
  301. self.bytesRead = self.bytesRead + data.length;
  302. // Reset state of buffer
  303. data = Buffer.alloc(0);
  304. } else {
  305. // Copy the missing part of the data into our current buffer
  306. data.copy(self.buffer, self.bytesRead, 0, remainingBytesToRead);
  307. // Slice the overflow into a new buffer that we will then re-parse
  308. data = data.slice(remainingBytesToRead);
  309. // Emit current complete message
  310. try {
  311. var emitBuffer = self.buffer;
  312. // Reset state of buffer
  313. self.buffer = null;
  314. self.sizeOfMessage = 0;
  315. self.bytesRead = 0;
  316. self.stubBuffer = null;
  317. emitMessageHandler(self, emitBuffer);
  318. } catch (err) {
  319. var errorObject = {
  320. err: 'socketHandler',
  321. trace: err,
  322. bin: self.buffer,
  323. parseState: {
  324. sizeOfMessage: self.sizeOfMessage,
  325. bytesRead: self.bytesRead,
  326. stubBuffer: self.stubBuffer
  327. }
  328. };
  329. // We got a parse Error fire it off then keep going
  330. self.emit('parseError', errorObject, self);
  331. }
  332. }
  333. } else {
  334. // Stub buffer is kept in case we don't get enough bytes to determine the
  335. // size of the message (< 4 bytes)
  336. if (self.stubBuffer != null && self.stubBuffer.length > 0) {
  337. // If we have enough bytes to determine the message size let's do it
  338. if (self.stubBuffer.length + data.length > 4) {
  339. // Prepad the data
  340. var newData = Buffer.alloc(self.stubBuffer.length + data.length);
  341. self.stubBuffer.copy(newData, 0);
  342. data.copy(newData, self.stubBuffer.length);
  343. // Reassign for parsing
  344. data = newData;
  345. // Reset state of buffer
  346. self.buffer = null;
  347. self.sizeOfMessage = 0;
  348. self.bytesRead = 0;
  349. self.stubBuffer = null;
  350. } else {
  351. // Add the the bytes to the stub buffer
  352. var newStubBuffer = Buffer.alloc(self.stubBuffer.length + data.length);
  353. // Copy existing stub buffer
  354. self.stubBuffer.copy(newStubBuffer, 0);
  355. // Copy missing part of the data
  356. data.copy(newStubBuffer, self.stubBuffer.length);
  357. // Exit parsing loop
  358. data = Buffer.alloc(0);
  359. }
  360. } else {
  361. if (data.length > 4) {
  362. // Retrieve the message size
  363. // var sizeOfMessage = data.readUInt32LE(0);
  364. var sizeOfMessage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
  365. // If we have a negative sizeOfMessage emit error and return
  366. if (sizeOfMessage < 0 || sizeOfMessage > self.maxBsonMessageSize) {
  367. errorObject = {
  368. err: 'socketHandler',
  369. trace: '',
  370. bin: self.buffer,
  371. parseState: {
  372. sizeOfMessage: sizeOfMessage,
  373. bytesRead: self.bytesRead,
  374. stubBuffer: self.stubBuffer
  375. }
  376. };
  377. // We got a parse Error fire it off then keep going
  378. self.emit('parseError', errorObject, self);
  379. return;
  380. }
  381. // Ensure that the size of message is larger than 0 and less than the max allowed
  382. if (
  383. sizeOfMessage > 4 &&
  384. sizeOfMessage < self.maxBsonMessageSize &&
  385. sizeOfMessage > data.length
  386. ) {
  387. self.buffer = Buffer.alloc(sizeOfMessage);
  388. // Copy all the data into the buffer
  389. data.copy(self.buffer, 0);
  390. // Update bytes read
  391. self.bytesRead = data.length;
  392. // Update sizeOfMessage
  393. self.sizeOfMessage = sizeOfMessage;
  394. // Ensure stub buffer is null
  395. self.stubBuffer = null;
  396. // Exit parsing loop
  397. data = Buffer.alloc(0);
  398. } else if (
  399. sizeOfMessage > 4 &&
  400. sizeOfMessage < self.maxBsonMessageSize &&
  401. sizeOfMessage === data.length
  402. ) {
  403. try {
  404. emitBuffer = data;
  405. // Reset state of buffer
  406. self.buffer = null;
  407. self.sizeOfMessage = 0;
  408. self.bytesRead = 0;
  409. self.stubBuffer = null;
  410. // Exit parsing loop
  411. data = Buffer.alloc(0);
  412. // Emit the message
  413. emitMessageHandler(self, emitBuffer);
  414. } catch (err) {
  415. self.emit('parseError', err, self);
  416. }
  417. } else if (sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonMessageSize) {
  418. errorObject = {
  419. err: 'socketHandler',
  420. trace: null,
  421. bin: data,
  422. parseState: {
  423. sizeOfMessage: sizeOfMessage,
  424. bytesRead: 0,
  425. buffer: null,
  426. stubBuffer: null
  427. }
  428. };
  429. // We got a parse Error fire it off then keep going
  430. self.emit('parseError', errorObject, self);
  431. // Clear out the state of the parser
  432. self.buffer = null;
  433. self.sizeOfMessage = 0;
  434. self.bytesRead = 0;
  435. self.stubBuffer = null;
  436. // Exit parsing loop
  437. data = Buffer.alloc(0);
  438. } else {
  439. emitBuffer = data.slice(0, sizeOfMessage);
  440. // Reset state of buffer
  441. self.buffer = null;
  442. self.sizeOfMessage = 0;
  443. self.bytesRead = 0;
  444. self.stubBuffer = null;
  445. // Copy rest of message
  446. data = data.slice(sizeOfMessage);
  447. // Emit the message
  448. emitMessageHandler(self, emitBuffer);
  449. }
  450. } else {
  451. // Create a buffer that contains the space for the non-complete message
  452. self.stubBuffer = Buffer.alloc(data.length);
  453. // Copy the data to the stub buffer
  454. data.copy(self.stubBuffer, 0);
  455. // Exit parsing loop
  456. data = Buffer.alloc(0);
  457. }
  458. }
  459. }
  460. }
  461. };
  462. };
  463. // List of socket level valid ssl options
  464. var legalSslSocketOptions = [
  465. 'pfx',
  466. 'key',
  467. 'passphrase',
  468. 'cert',
  469. 'ca',
  470. 'ciphers',
  471. 'NPNProtocols',
  472. 'ALPNProtocols',
  473. 'servername',
  474. 'ecdhCurve',
  475. 'secureProtocol',
  476. 'secureContext',
  477. 'session',
  478. 'minDHSize'
  479. ];
  480. function merge(options1, options2) {
  481. // Merge in any allowed ssl options
  482. for (var name in options2) {
  483. if (options2[name] != null && legalSslSocketOptions.indexOf(name) !== -1) {
  484. options1[name] = options2[name];
  485. }
  486. }
  487. }
  488. function makeSSLConnection(self, _options) {
  489. let sslOptions = {
  490. socket: self.connection,
  491. rejectUnauthorized: self.rejectUnauthorized
  492. };
  493. // Merge in options
  494. merge(sslOptions, self.options);
  495. merge(sslOptions, _options);
  496. // Set options for ssl
  497. if (self.ca) sslOptions.ca = self.ca;
  498. if (self.crl) sslOptions.crl = self.crl;
  499. if (self.cert) sslOptions.cert = self.cert;
  500. if (self.key) sslOptions.key = self.key;
  501. if (self.passphrase) sslOptions.passphrase = self.passphrase;
  502. // Override checkServerIdentity behavior
  503. if (self.checkServerIdentity === false) {
  504. // Skip the identiy check by retuning undefined as per node documents
  505. // https://nodejs.org/api/tls.html#tls_tls_connect_options_callback
  506. sslOptions.checkServerIdentity = function() {
  507. return undefined;
  508. };
  509. } else if (typeof self.checkServerIdentity === 'function') {
  510. sslOptions.checkServerIdentity = self.checkServerIdentity;
  511. }
  512. // Set default sni servername to be the same as host
  513. if (sslOptions.servername == null) {
  514. sslOptions.servername = self.host;
  515. }
  516. // Attempt SSL connection
  517. const connection = tls.connect(self.port, self.host, sslOptions, function() {
  518. // Error on auth or skip
  519. if (connection.authorizationError && self.rejectUnauthorized) {
  520. return self.emit('error', connection.authorizationError, self, { ssl: true });
  521. }
  522. // Set socket timeout instead of connection timeout
  523. connection.setTimeout(self.socketTimeout);
  524. // We are done emit connect
  525. self.emit('connect', self);
  526. });
  527. // Set the options for the connection
  528. connection.setKeepAlive(self.keepAlive, self.keepAliveInitialDelay);
  529. connection.setTimeout(self.connectionTimeout);
  530. connection.setNoDelay(self.noDelay);
  531. return connection;
  532. }
  533. function makeUnsecureConnection(self, family) {
  534. // Create new connection instance
  535. let connection_options;
  536. if (self.domainSocket) {
  537. connection_options = { path: self.host };
  538. } else {
  539. connection_options = { port: self.port, host: self.host };
  540. connection_options.family = family;
  541. }
  542. const connection = net.createConnection(connection_options);
  543. // Set the options for the connection
  544. connection.setKeepAlive(self.keepAlive, self.keepAliveInitialDelay);
  545. connection.setTimeout(self.connectionTimeout);
  546. connection.setNoDelay(self.noDelay);
  547. connection.once('connect', function() {
  548. // Set socket timeout instead of connection timeout
  549. connection.setTimeout(self.socketTimeout);
  550. // Emit connect event
  551. self.emit('connect', self);
  552. });
  553. return connection;
  554. }
  555. function doConnect(self, family, _options, _errorHandler) {
  556. self.connection = self.ssl
  557. ? makeSSLConnection(self, _options)
  558. : makeUnsecureConnection(self, family);
  559. // Add handlers for events
  560. self.connection.once('error', _errorHandler);
  561. self.connection.once('timeout', timeoutHandler(self));
  562. self.connection.once('close', closeHandler(self));
  563. self.connection.on('data', dataHandler(self));
  564. }
  565. /**
  566. * Connect
  567. * @method
  568. */
  569. Connection.prototype.connect = function(_options) {
  570. _options = _options || {};
  571. // Set the connections
  572. if (connectionAccounting) addConnection(this.id, this);
  573. // Check if we are overriding the promoteLongs
  574. if (typeof _options.promoteLongs === 'boolean') {
  575. this.responseOptions.promoteLongs = _options.promoteLongs;
  576. this.responseOptions.promoteValues = _options.promoteValues;
  577. this.responseOptions.promoteBuffers = _options.promoteBuffers;
  578. }
  579. const _errorHandler = errorHandler(this);
  580. if (this.family !== void 0) {
  581. return doConnect(this, this.family, _options, _errorHandler);
  582. }
  583. return doConnect(this, 6, _options, err => {
  584. if (this.logger.isDebug()) {
  585. this.logger.debug(
  586. f(
  587. 'connection %s for [%s:%s] errored out with [%s]',
  588. this.id,
  589. this.host,
  590. this.port,
  591. JSON.stringify(err)
  592. )
  593. );
  594. }
  595. // clean up existing event handlers
  596. this.connection.removeAllListeners('error');
  597. this.connection.removeAllListeners('timeout');
  598. this.connection.removeAllListeners('close');
  599. this.connection.removeAllListeners('data');
  600. this.connection = undefined;
  601. return doConnect(this, 4, _options, _errorHandler);
  602. });
  603. };
  604. /**
  605. * Unref this connection
  606. * @method
  607. * @return {boolean}
  608. */
  609. Connection.prototype.unref = function() {
  610. if (this.connection) this.connection.unref();
  611. else {
  612. var self = this;
  613. this.once('connect', function() {
  614. self.connection.unref();
  615. });
  616. }
  617. };
  618. /**
  619. * Destroy connection
  620. * @method
  621. */
  622. Connection.prototype.destroy = function() {
  623. // Set the connections
  624. if (connectionAccounting) deleteConnection(this.id);
  625. if (this.connection) {
  626. // Catch posssible exception thrown by node 0.10.x
  627. try {
  628. this.connection.end();
  629. } catch (err) {} // eslint-disable-line
  630. // Destroy connection
  631. this.connection.destroy();
  632. }
  633. this.destroyed = true;
  634. };
  635. /**
  636. * Write to connection
  637. * @method
  638. * @param {Command} command Command to write out need to implement toBin and toBinUnified
  639. */
  640. Connection.prototype.write = function(buffer) {
  641. var i;
  642. // Debug Log
  643. if (this.logger.isDebug()) {
  644. if (!Array.isArray(buffer)) {
  645. this.logger.debug(
  646. f('writing buffer [%s] to %s:%s', buffer.toString('hex'), this.host, this.port)
  647. );
  648. } else {
  649. for (i = 0; i < buffer.length; i++)
  650. this.logger.debug(
  651. f('writing buffer [%s] to %s:%s', buffer[i].toString('hex'), this.host, this.port)
  652. );
  653. }
  654. }
  655. // Double check that the connection is not destroyed
  656. if (this.connection.destroyed === false) {
  657. // Write out the command
  658. if (!Array.isArray(buffer)) {
  659. this.connection.write(buffer, 'binary');
  660. return true;
  661. }
  662. // Iterate over all buffers and write them in order to the socket
  663. for (i = 0; i < buffer.length; i++) this.connection.write(buffer[i], 'binary');
  664. return true;
  665. }
  666. // Connection is destroyed return write failed
  667. return false;
  668. };
  669. /**
  670. * Return id of connection as a string
  671. * @method
  672. * @return {string}
  673. */
  674. Connection.prototype.toString = function() {
  675. return '' + this.id;
  676. };
  677. /**
  678. * Return json object of connection
  679. * @method
  680. * @return {object}
  681. */
  682. Connection.prototype.toJSON = function() {
  683. return { id: this.id, host: this.host, port: this.port };
  684. };
  685. /**
  686. * Is the connection connected
  687. * @method
  688. * @return {boolean}
  689. */
  690. Connection.prototype.isConnected = function() {
  691. if (this.destroyed) return false;
  692. return !this.connection.destroyed && this.connection.writable;
  693. };
  694. /**
  695. * A server connect event, used to verify that the connection is up and running
  696. *
  697. * @event Connection#connect
  698. * @type {Connection}
  699. */
  700. /**
  701. * The server connection closed, all pool connections closed
  702. *
  703. * @event Connection#close
  704. * @type {Connection}
  705. */
  706. /**
  707. * The server connection caused an error, all pool connections closed
  708. *
  709. * @event Connection#error
  710. * @type {Connection}
  711. */
  712. /**
  713. * The server connection timed out, all pool connections closed
  714. *
  715. * @event Connection#timeout
  716. * @type {Connection}
  717. */
  718. /**
  719. * The driver experienced an invalid message, all pool connections closed
  720. *
  721. * @event Connection#parseError
  722. * @type {Connection}
  723. */
  724. module.exports = Connection;