mongodb 3.2.5 → 3.3.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,624 @@
1
+ 'use strict';
2
+
3
+ const EventEmitter = require('events').EventEmitter;
4
+ const crypto = require('crypto');
5
+ const debugOptions = require('./utils').debugOptions;
6
+ const parseHeader = require('../wireprotocol/shared').parseHeader;
7
+ const decompress = require('../wireprotocol/compression').decompress;
8
+ const Response = require('./commands').Response;
9
+ const BinMsg = require('./msg').BinMsg;
10
+ const MongoNetworkError = require('../error').MongoNetworkError;
11
+ const MongoError = require('../error').MongoError;
12
+ const Logger = require('./logger');
13
+ const OP_COMPRESSED = require('../wireprotocol/shared').opcodes.OP_COMPRESSED;
14
+ const OP_MSG = require('../wireprotocol/shared').opcodes.OP_MSG;
15
+ const MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
16
+ const Buffer = require('safe-buffer').Buffer;
17
+
18
+ let _id = 0;
19
+
20
+ const DEFAULT_MAX_BSON_MESSAGE_SIZE = 1024 * 1024 * 16 * 4;
21
+ const DEBUG_FIELDS = [
22
+ 'host',
23
+ 'port',
24
+ 'size',
25
+ 'keepAlive',
26
+ 'keepAliveInitialDelay',
27
+ 'noDelay',
28
+ 'connectionTimeout',
29
+ 'socketTimeout',
30
+ 'ssl',
31
+ 'ca',
32
+ 'crl',
33
+ 'cert',
34
+ 'rejectUnauthorized',
35
+ 'promoteLongs',
36
+ 'promoteValues',
37
+ 'promoteBuffers',
38
+ 'checkServerIdentity'
39
+ ];
40
+
41
+ let connectionAccountingSpy = undefined;
42
+ let connectionAccounting = false;
43
+ let connections = {};
44
+
45
+ /**
46
+ * A class representing a single connection to a MongoDB server
47
+ *
48
+ * @fires Connection#connect
49
+ * @fires Connection#close
50
+ * @fires Connection#error
51
+ * @fires Connection#timeout
52
+ * @fires Connection#parseError
53
+ * @fires Connection#message
54
+ */
55
+ class Connection extends EventEmitter {
56
+ /**
57
+ * Creates a new Connection instance
58
+ *
59
+ * @param {Socket} socket The socket this connection wraps
60
+ * @param {Object} [options] Optional settings
61
+ * @param {string} [options.host] The host the socket is connected to
62
+ * @param {number} [options.port] The port used for the socket connection
63
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
64
+ * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
65
+ * @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
66
+ * @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
67
+ * @param {boolean} [options.promoteLongs] Convert Long values from the db into Numbers if they fit into 53 bits
68
+ * @param {boolean} [options.promoteValues] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
69
+ * @param {boolean} [options.promoteBuffers] Promotes Binary BSON values to native Node Buffers.
70
+ */
71
+ constructor(socket, options) {
72
+ super();
73
+
74
+ options = options || {};
75
+ if (!options.bson) {
76
+ throw new TypeError('must pass in valid bson parser');
77
+ }
78
+
79
+ this.id = _id++;
80
+ this.options = options;
81
+ this.logger = Logger('Connection', options);
82
+ this.bson = options.bson;
83
+ this.tag = options.tag;
84
+ this.maxBsonMessageSize = options.maxBsonMessageSize || DEFAULT_MAX_BSON_MESSAGE_SIZE;
85
+
86
+ this.port = options.port || 27017;
87
+ this.host = options.host || 'localhost';
88
+ this.socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
89
+
90
+ // These values are inspected directly in tests, but maybe not necessary to keep around
91
+ this.keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
92
+ this.keepAliveInitialDelay =
93
+ typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
94
+ this.connectionTimeout =
95
+ typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
96
+ if (this.keepAliveInitialDelay > this.socketTimeout) {
97
+ this.keepAliveInitialDelay = Math.round(this.socketTimeout / 2);
98
+ }
99
+
100
+ // Debug information
101
+ if (this.logger.isDebug()) {
102
+ this.logger.debug(
103
+ `creating connection ${this.id} with options [${JSON.stringify(
104
+ debugOptions(DEBUG_FIELDS, options)
105
+ )}]`
106
+ );
107
+ }
108
+
109
+ // Response options
110
+ this.responseOptions = {
111
+ promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
112
+ promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
113
+ promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false
114
+ };
115
+
116
+ // Flushing
117
+ this.flushing = false;
118
+ this.queue = [];
119
+
120
+ // Internal state
121
+ this.writeStream = null;
122
+ this.destroyed = false;
123
+
124
+ // Create hash method
125
+ const hash = crypto.createHash('sha1');
126
+ hash.update(this.address);
127
+ this.hashedName = hash.digest('hex');
128
+
129
+ // All operations in flight on the connection
130
+ this.workItems = [];
131
+
132
+ // setup socket
133
+ this.socket = socket;
134
+ this.socket.once('error', errorHandler(this));
135
+ this.socket.once('timeout', timeoutHandler(this));
136
+ this.socket.once('close', closeHandler(this));
137
+ this.socket.on('data', dataHandler(this));
138
+
139
+ if (connectionAccounting) {
140
+ addConnection(this.id, this);
141
+ }
142
+ }
143
+
144
+ setSocketTimeout(value) {
145
+ if (this.socket) {
146
+ this.socket.setTimeout(value);
147
+ }
148
+ }
149
+
150
+ resetSocketTimeout() {
151
+ if (this.socket) {
152
+ this.socket.setTimeout(this.socketTimeout);
153
+ }
154
+ }
155
+
156
+ static enableConnectionAccounting(spy) {
157
+ if (spy) {
158
+ connectionAccountingSpy = spy;
159
+ }
160
+
161
+ connectionAccounting = true;
162
+ connections = {};
163
+ }
164
+
165
+ static disableConnectionAccounting() {
166
+ connectionAccounting = false;
167
+ connectionAccountingSpy = undefined;
168
+ }
169
+
170
+ static connections() {
171
+ return connections;
172
+ }
173
+
174
+ get address() {
175
+ return `${this.host}:${this.port}`;
176
+ }
177
+
178
+ /**
179
+ * Unref this connection
180
+ * @method
181
+ * @return {boolean}
182
+ */
183
+ unref() {
184
+ if (this.socket == null) {
185
+ this.once('connect', () => this.socket.unref());
186
+ return;
187
+ }
188
+
189
+ this.socket.unref();
190
+ }
191
+
192
+ /**
193
+ * Destroy connection
194
+ * @method
195
+ */
196
+ destroy(options, callback) {
197
+ if (typeof options === 'function') {
198
+ callback = options;
199
+ options = {};
200
+ }
201
+
202
+ options = Object.assign({ force: false }, options);
203
+
204
+ if (connectionAccounting) {
205
+ deleteConnection(this.id);
206
+ }
207
+
208
+ if (this.socket == null) {
209
+ this.destroyed = true;
210
+ return;
211
+ }
212
+
213
+ if (options.force) {
214
+ this.socket.destroy();
215
+ this.destroyed = true;
216
+ if (typeof callback === 'function') callback(null, null);
217
+ return;
218
+ }
219
+
220
+ this.socket.end(err => {
221
+ this.destroyed = true;
222
+ if (typeof callback === 'function') callback(err, null);
223
+ });
224
+ }
225
+
226
+ /**
227
+ * Write to connection
228
+ * @method
229
+ * @param {Command} command Command to write out need to implement toBin and toBinUnified
230
+ */
231
+ write(buffer) {
232
+ // Debug Log
233
+ if (this.logger.isDebug()) {
234
+ if (!Array.isArray(buffer)) {
235
+ this.logger.debug(`writing buffer [${buffer.toString('hex')}] to ${this.address}`);
236
+ } else {
237
+ for (let i = 0; i < buffer.length; i++)
238
+ this.logger.debug(`writing buffer [${buffer[i].toString('hex')}] to ${this.address}`);
239
+ }
240
+ }
241
+
242
+ // Double check that the connection is not destroyed
243
+ if (this.socket.destroyed === false) {
244
+ // Write out the command
245
+ if (!Array.isArray(buffer)) {
246
+ this.socket.write(buffer, 'binary');
247
+ return true;
248
+ }
249
+
250
+ // Iterate over all buffers and write them in order to the socket
251
+ for (let i = 0; i < buffer.length; i++) {
252
+ this.socket.write(buffer[i], 'binary');
253
+ }
254
+
255
+ return true;
256
+ }
257
+
258
+ // Connection is destroyed return write failed
259
+ return false;
260
+ }
261
+
262
+ /**
263
+ * Return id of connection as a string
264
+ * @method
265
+ * @return {string}
266
+ */
267
+ toString() {
268
+ return '' + this.id;
269
+ }
270
+
271
+ /**
272
+ * Return json object of connection
273
+ * @method
274
+ * @return {object}
275
+ */
276
+ toJSON() {
277
+ return { id: this.id, host: this.host, port: this.port };
278
+ }
279
+
280
+ /**
281
+ * Is the connection connected
282
+ * @method
283
+ * @return {boolean}
284
+ */
285
+ isConnected() {
286
+ if (this.destroyed) return false;
287
+ return !this.socket.destroyed && this.socket.writable;
288
+ }
289
+ }
290
+
291
+ function deleteConnection(id) {
292
+ // console.log("=== deleted connection " + id + " :: " + (connections[id] ? connections[id].port : ''))
293
+ delete connections[id];
294
+
295
+ if (connectionAccountingSpy) {
296
+ connectionAccountingSpy.deleteConnection(id);
297
+ }
298
+ }
299
+
300
+ function addConnection(id, connection) {
301
+ // console.log("=== added connection " + id + " :: " + connection.port)
302
+ connections[id] = connection;
303
+
304
+ if (connectionAccountingSpy) {
305
+ connectionAccountingSpy.addConnection(id, connection);
306
+ }
307
+ }
308
+
309
+ //
310
+ // Connection handlers
311
+ function errorHandler(conn) {
312
+ return function(err) {
313
+ if (connectionAccounting) deleteConnection(conn.id);
314
+ // Debug information
315
+ if (conn.logger.isDebug()) {
316
+ conn.logger.debug(
317
+ `connection ${conn.id} for [${conn.address}] errored out with [${JSON.stringify(err)}]`
318
+ );
319
+ }
320
+
321
+ conn.emit('error', new MongoNetworkError(err), conn);
322
+ };
323
+ }
324
+
325
+ function timeoutHandler(conn) {
326
+ return function() {
327
+ if (connectionAccounting) deleteConnection(conn.id);
328
+
329
+ if (conn.logger.isDebug()) {
330
+ conn.logger.debug(`connection ${conn.id} for [${conn.address}] timed out`);
331
+ }
332
+
333
+ conn.emit(
334
+ 'timeout',
335
+ new MongoNetworkError(`connection ${conn.id} to ${conn.address} timed out`),
336
+ conn
337
+ );
338
+ };
339
+ }
340
+
341
+ function closeHandler(conn) {
342
+ return function(hadError) {
343
+ if (connectionAccounting) deleteConnection(conn.id);
344
+
345
+ if (conn.logger.isDebug()) {
346
+ conn.logger.debug(`connection ${conn.id} with for [${conn.address}] closed`);
347
+ }
348
+
349
+ if (!hadError) {
350
+ conn.emit(
351
+ 'close',
352
+ new MongoNetworkError(`connection ${conn.id} to ${conn.address} closed`),
353
+ conn
354
+ );
355
+ }
356
+ };
357
+ }
358
+
359
+ // Handle a message once it is received
360
+ function processMessage(conn, message) {
361
+ const msgHeader = parseHeader(message);
362
+ if (msgHeader.opCode !== OP_COMPRESSED) {
363
+ const ResponseConstructor = msgHeader.opCode === OP_MSG ? BinMsg : Response;
364
+ conn.emit(
365
+ 'message',
366
+ new ResponseConstructor(
367
+ conn.bson,
368
+ message,
369
+ msgHeader,
370
+ message.slice(MESSAGE_HEADER_SIZE),
371
+ conn.responseOptions
372
+ ),
373
+ conn
374
+ );
375
+
376
+ return;
377
+ }
378
+
379
+ msgHeader.fromCompressed = true;
380
+ let index = MESSAGE_HEADER_SIZE;
381
+ msgHeader.opCode = message.readInt32LE(index);
382
+ index += 4;
383
+ msgHeader.length = message.readInt32LE(index);
384
+ index += 4;
385
+ const compressorID = message[index];
386
+ index++;
387
+
388
+ decompress(compressorID, message.slice(index), (err, decompressedMsgBody) => {
389
+ if (err) {
390
+ conn.emit('error', err);
391
+ return;
392
+ }
393
+
394
+ if (decompressedMsgBody.length !== msgHeader.length) {
395
+ conn.emit(
396
+ 'error',
397
+ new MongoError(
398
+ 'Decompressing a compressed message from the server failed. The message is corrupt.'
399
+ )
400
+ );
401
+
402
+ return;
403
+ }
404
+
405
+ const ResponseConstructor = msgHeader.opCode === OP_MSG ? BinMsg : Response;
406
+ conn.emit(
407
+ 'message',
408
+ new ResponseConstructor(
409
+ conn.bson,
410
+ message,
411
+ msgHeader,
412
+ decompressedMsgBody,
413
+ conn.responseOptions
414
+ ),
415
+ conn
416
+ );
417
+ });
418
+ }
419
+
420
+ function dataHandler(conn) {
421
+ return function(data) {
422
+ // Parse until we are done with the data
423
+ while (data.length > 0) {
424
+ // If we still have bytes to read on the current message
425
+ if (conn.bytesRead > 0 && conn.sizeOfMessage > 0) {
426
+ // Calculate the amount of remaining bytes
427
+ const remainingBytesToRead = conn.sizeOfMessage - conn.bytesRead;
428
+ // Check if the current chunk contains the rest of the message
429
+ if (remainingBytesToRead > data.length) {
430
+ // Copy the new data into the exiting buffer (should have been allocated when we know the message size)
431
+ data.copy(conn.buffer, conn.bytesRead);
432
+ // Adjust the number of bytes read so it point to the correct index in the buffer
433
+ conn.bytesRead = conn.bytesRead + data.length;
434
+
435
+ // Reset state of buffer
436
+ data = Buffer.alloc(0);
437
+ } else {
438
+ // Copy the missing part of the data into our current buffer
439
+ data.copy(conn.buffer, conn.bytesRead, 0, remainingBytesToRead);
440
+ // Slice the overflow into a new buffer that we will then re-parse
441
+ data = data.slice(remainingBytesToRead);
442
+
443
+ // Emit current complete message
444
+ const emitBuffer = conn.buffer;
445
+ // Reset state of buffer
446
+ conn.buffer = null;
447
+ conn.sizeOfMessage = 0;
448
+ conn.bytesRead = 0;
449
+ conn.stubBuffer = null;
450
+
451
+ processMessage(conn, emitBuffer);
452
+ }
453
+ } else {
454
+ // Stub buffer is kept in case we don't get enough bytes to determine the
455
+ // size of the message (< 4 bytes)
456
+ if (conn.stubBuffer != null && conn.stubBuffer.length > 0) {
457
+ // If we have enough bytes to determine the message size let's do it
458
+ if (conn.stubBuffer.length + data.length > 4) {
459
+ // Prepad the data
460
+ const newData = Buffer.alloc(conn.stubBuffer.length + data.length);
461
+ conn.stubBuffer.copy(newData, 0);
462
+ data.copy(newData, conn.stubBuffer.length);
463
+ // Reassign for parsing
464
+ data = newData;
465
+
466
+ // Reset state of buffer
467
+ conn.buffer = null;
468
+ conn.sizeOfMessage = 0;
469
+ conn.bytesRead = 0;
470
+ conn.stubBuffer = null;
471
+ } else {
472
+ // Add the the bytes to the stub buffer
473
+ const newStubBuffer = Buffer.alloc(conn.stubBuffer.length + data.length);
474
+ // Copy existing stub buffer
475
+ conn.stubBuffer.copy(newStubBuffer, 0);
476
+ // Copy missing part of the data
477
+ data.copy(newStubBuffer, conn.stubBuffer.length);
478
+ // Exit parsing loop
479
+ data = Buffer.alloc(0);
480
+ }
481
+ } else {
482
+ if (data.length > 4) {
483
+ // Retrieve the message size
484
+ const sizeOfMessage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
485
+ // If we have a negative sizeOfMessage emit error and return
486
+ if (sizeOfMessage < 0 || sizeOfMessage > conn.maxBsonMessageSize) {
487
+ const errorObject = {
488
+ err: 'socketHandler',
489
+ trace: '',
490
+ bin: conn.buffer,
491
+ parseState: {
492
+ sizeOfMessage: sizeOfMessage,
493
+ bytesRead: conn.bytesRead,
494
+ stubBuffer: conn.stubBuffer
495
+ }
496
+ };
497
+ // We got a parse Error fire it off then keep going
498
+ conn.emit('parseError', errorObject, conn);
499
+ return;
500
+ }
501
+
502
+ // Ensure that the size of message is larger than 0 and less than the max allowed
503
+ if (
504
+ sizeOfMessage > 4 &&
505
+ sizeOfMessage < conn.maxBsonMessageSize &&
506
+ sizeOfMessage > data.length
507
+ ) {
508
+ conn.buffer = Buffer.alloc(sizeOfMessage);
509
+ // Copy all the data into the buffer
510
+ data.copy(conn.buffer, 0);
511
+ // Update bytes read
512
+ conn.bytesRead = data.length;
513
+ // Update sizeOfMessage
514
+ conn.sizeOfMessage = sizeOfMessage;
515
+ // Ensure stub buffer is null
516
+ conn.stubBuffer = null;
517
+ // Exit parsing loop
518
+ data = Buffer.alloc(0);
519
+ } else if (
520
+ sizeOfMessage > 4 &&
521
+ sizeOfMessage < conn.maxBsonMessageSize &&
522
+ sizeOfMessage === data.length
523
+ ) {
524
+ const emitBuffer = data;
525
+ // Reset state of buffer
526
+ conn.buffer = null;
527
+ conn.sizeOfMessage = 0;
528
+ conn.bytesRead = 0;
529
+ conn.stubBuffer = null;
530
+ // Exit parsing loop
531
+ data = Buffer.alloc(0);
532
+ // Emit the message
533
+ processMessage(conn, emitBuffer);
534
+ } else if (sizeOfMessage <= 4 || sizeOfMessage > conn.maxBsonMessageSize) {
535
+ const errorObject = {
536
+ err: 'socketHandler',
537
+ trace: null,
538
+ bin: data,
539
+ parseState: {
540
+ sizeOfMessage: sizeOfMessage,
541
+ bytesRead: 0,
542
+ buffer: null,
543
+ stubBuffer: null
544
+ }
545
+ };
546
+ // We got a parse Error fire it off then keep going
547
+ conn.emit('parseError', errorObject, conn);
548
+
549
+ // Clear out the state of the parser
550
+ conn.buffer = null;
551
+ conn.sizeOfMessage = 0;
552
+ conn.bytesRead = 0;
553
+ conn.stubBuffer = null;
554
+ // Exit parsing loop
555
+ data = Buffer.alloc(0);
556
+ } else {
557
+ const emitBuffer = data.slice(0, sizeOfMessage);
558
+ // Reset state of buffer
559
+ conn.buffer = null;
560
+ conn.sizeOfMessage = 0;
561
+ conn.bytesRead = 0;
562
+ conn.stubBuffer = null;
563
+ // Copy rest of message
564
+ data = data.slice(sizeOfMessage);
565
+ // Emit the message
566
+ processMessage(conn, emitBuffer);
567
+ }
568
+ } else {
569
+ // Create a buffer that contains the space for the non-complete message
570
+ conn.stubBuffer = Buffer.alloc(data.length);
571
+ // Copy the data to the stub buffer
572
+ data.copy(conn.stubBuffer, 0);
573
+ // Exit parsing loop
574
+ data = Buffer.alloc(0);
575
+ }
576
+ }
577
+ }
578
+ }
579
+ };
580
+ }
581
+
582
+ /**
583
+ * A server connect event, used to verify that the connection is up and running
584
+ *
585
+ * @event Connection#connect
586
+ * @type {Connection}
587
+ */
588
+
589
+ /**
590
+ * The server connection closed, all pool connections closed
591
+ *
592
+ * @event Connection#close
593
+ * @type {Connection}
594
+ */
595
+
596
+ /**
597
+ * The server connection caused an error, all pool connections closed
598
+ *
599
+ * @event Connection#error
600
+ * @type {Connection}
601
+ */
602
+
603
+ /**
604
+ * The server connection timed out, all pool connections closed
605
+ *
606
+ * @event Connection#timeout
607
+ * @type {Connection}
608
+ */
609
+
610
+ /**
611
+ * The driver experienced an invalid message, all pool connections closed
612
+ *
613
+ * @event Connection#parseError
614
+ * @type {Connection}
615
+ */
616
+
617
+ /**
618
+ * An event emitted each time the connection receives a parsed message from the wire
619
+ *
620
+ * @event Connection#message
621
+ * @type {Connection}
622
+ */
623
+
624
+ module.exports = Connection;