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.
- package/HISTORY.md +0 -10
- package/index.js +4 -4
- package/lib/admin.js +56 -56
- package/lib/aggregation_cursor.js +7 -3
- package/lib/bulk/common.js +18 -13
- package/lib/change_stream.js +196 -89
- package/lib/collection.js +217 -169
- package/lib/command_cursor.js +17 -7
- package/lib/core/auth/auth_provider.js +158 -0
- package/lib/core/auth/defaultAuthProviders.js +29 -0
- package/lib/core/auth/gssapi.js +241 -0
- package/lib/core/auth/mongo_credentials.js +81 -0
- package/lib/core/auth/mongocr.js +51 -0
- package/lib/core/auth/plain.js +35 -0
- package/lib/core/auth/scram.js +293 -0
- package/lib/core/auth/sspi.js +131 -0
- package/lib/core/auth/x509.js +26 -0
- package/lib/core/connection/apm.js +236 -0
- package/lib/core/connection/command_result.js +36 -0
- package/lib/core/connection/commands.js +507 -0
- package/lib/core/connection/connect.js +370 -0
- package/lib/core/connection/connection.js +624 -0
- package/lib/core/connection/logger.js +246 -0
- package/lib/core/connection/msg.js +219 -0
- package/lib/core/connection/pool.js +1285 -0
- package/lib/core/connection/utils.js +57 -0
- package/lib/core/cursor.js +752 -0
- package/lib/core/error.js +186 -0
- package/lib/core/index.js +50 -0
- package/lib/core/sdam/monitoring.js +228 -0
- package/lib/core/sdam/server.js +467 -0
- package/lib/core/sdam/server_description.js +163 -0
- package/lib/core/sdam/server_selectors.js +244 -0
- package/lib/core/sdam/srv_polling.js +135 -0
- package/lib/core/sdam/topology.js +1151 -0
- package/lib/core/sdam/topology_description.js +408 -0
- package/lib/core/sessions.js +711 -0
- package/lib/core/tools/smoke_plugin.js +61 -0
- package/lib/core/topologies/mongos.js +1337 -0
- package/lib/core/topologies/read_preference.js +202 -0
- package/lib/core/topologies/replset.js +1507 -0
- package/lib/core/topologies/replset_state.js +1121 -0
- package/lib/core/topologies/server.js +984 -0
- package/lib/core/topologies/shared.js +453 -0
- package/lib/core/transactions.js +167 -0
- package/lib/core/uri_parser.js +631 -0
- package/lib/core/utils.js +165 -0
- package/lib/core/wireprotocol/command.js +170 -0
- package/lib/core/wireprotocol/compression.js +73 -0
- package/lib/core/wireprotocol/constants.js +13 -0
- package/lib/core/wireprotocol/get_more.js +86 -0
- package/lib/core/wireprotocol/index.js +18 -0
- package/lib/core/wireprotocol/kill_cursors.js +70 -0
- package/lib/core/wireprotocol/query.js +224 -0
- package/lib/core/wireprotocol/shared.js +115 -0
- package/lib/core/wireprotocol/write_command.js +50 -0
- package/lib/cursor.js +40 -46
- package/lib/db.js +141 -95
- package/lib/dynamic_loaders.js +32 -0
- package/lib/error.js +12 -10
- package/lib/gridfs/chunk.js +2 -2
- package/lib/gridfs/grid_store.js +31 -25
- package/lib/gridfs-stream/index.js +4 -4
- package/lib/gridfs-stream/upload.js +1 -1
- package/lib/mongo_client.js +37 -15
- package/lib/operations/add_user.js +96 -0
- package/lib/operations/aggregate.js +24 -13
- package/lib/operations/aggregate_operation.js +127 -0
- package/lib/operations/bulk_write.js +104 -0
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +28 -287
- package/lib/operations/collections.js +55 -0
- package/lib/operations/command.js +120 -0
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/common_functions.js +372 -0
- package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
- package/lib/operations/count.js +72 -0
- package/lib/operations/count_documents.js +46 -0
- package/lib/operations/create_collection.js +118 -0
- package/lib/operations/create_index.js +92 -0
- package/lib/operations/create_indexes.js +61 -0
- package/lib/operations/cursor_ops.js +3 -4
- package/lib/operations/db_ops.js +15 -12
- package/lib/operations/delete_many.js +25 -0
- package/lib/operations/delete_one.js +25 -0
- package/lib/operations/distinct.js +85 -0
- package/lib/operations/drop.js +53 -0
- package/lib/operations/drop_index.js +42 -0
- package/lib/operations/drop_indexes.js +23 -0
- package/lib/operations/estimated_document_count.js +33 -0
- package/lib/operations/execute_db_admin_command.js +34 -0
- package/lib/operations/execute_operation.js +165 -0
- package/lib/operations/explain.js +23 -0
- package/lib/operations/find_and_modify.js +98 -0
- package/lib/operations/find_one.js +33 -0
- package/lib/operations/find_one_and_delete.js +16 -0
- package/lib/operations/find_one_and_replace.js +18 -0
- package/lib/operations/find_one_and_update.js +19 -0
- package/lib/operations/geo_haystack_search.js +79 -0
- package/lib/operations/has_next.js +40 -0
- package/lib/operations/index_exists.js +39 -0
- package/lib/operations/index_information.js +23 -0
- package/lib/operations/indexes.js +22 -0
- package/lib/operations/insert_many.js +63 -0
- package/lib/operations/insert_one.js +75 -0
- package/lib/operations/is_capped.js +19 -0
- package/lib/operations/list_indexes.js +66 -0
- package/lib/operations/map_reduce.js +189 -0
- package/lib/operations/next.js +32 -0
- package/lib/operations/operation.js +63 -0
- package/lib/operations/options_operation.js +32 -0
- package/lib/operations/profiling_level.js +31 -0
- package/lib/operations/re_index.js +28 -0
- package/lib/operations/remove_user.js +52 -0
- package/lib/operations/rename.js +61 -0
- package/lib/operations/replace_one.js +47 -0
- package/lib/operations/set_profiling_level.js +48 -0
- package/lib/operations/stats.js +45 -0
- package/lib/operations/to_array.js +68 -0
- package/lib/operations/update_many.js +29 -0
- package/lib/operations/update_one.js +44 -0
- package/lib/operations/validate_collection.js +40 -0
- package/lib/read_concern.js +55 -0
- package/lib/topologies/mongos.js +3 -3
- package/lib/topologies/native_topology.js +22 -2
- package/lib/topologies/replset.js +3 -3
- package/lib/topologies/server.js +4 -4
- package/lib/topologies/topology_base.js +6 -6
- package/lib/url_parser.js +4 -3
- package/lib/utils.js +46 -59
- package/lib/write_concern.js +66 -0
- package/package.json +15 -6
- 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;
|