commands.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.BinMsg = exports.Msg = exports.Response = exports.KillCursor = exports.GetMore = exports.Query = void 0;
  4. const BSON = require("../bson");
  5. const error_1 = require("../error");
  6. const read_preference_1 = require("../read_preference");
  7. const utils_1 = require("../utils");
  8. const constants_1 = require("./wire_protocol/constants");
  9. // Incrementing request id
  10. let _requestId = 0;
  11. // Query flags
  12. const OPTS_TAILABLE_CURSOR = 2;
  13. const OPTS_SECONDARY = 4;
  14. const OPTS_OPLOG_REPLAY = 8;
  15. const OPTS_NO_CURSOR_TIMEOUT = 16;
  16. const OPTS_AWAIT_DATA = 32;
  17. const OPTS_EXHAUST = 64;
  18. const OPTS_PARTIAL = 128;
  19. // Response flags
  20. const CURSOR_NOT_FOUND = 1;
  21. const QUERY_FAILURE = 2;
  22. const SHARD_CONFIG_STALE = 4;
  23. const AWAIT_CAPABLE = 8;
  24. /**************************************************************
  25. * QUERY
  26. **************************************************************/
  27. /** @internal */
  28. class Query {
  29. constructor(ns, query, options) {
  30. // Basic options needed to be passed in
  31. // TODO(NODE-3483): Replace with MongoCommandError
  32. if (ns == null)
  33. throw new error_1.MongoRuntimeError('Namespace must be specified for query');
  34. // TODO(NODE-3483): Replace with MongoCommandError
  35. if (query == null)
  36. throw new error_1.MongoRuntimeError('A query document must be specified for query');
  37. // Validate that we are not passing 0x00 in the collection name
  38. if (ns.indexOf('\x00') !== -1) {
  39. // TODO(NODE-3483): Use MongoNamespace static method
  40. throw new error_1.MongoRuntimeError('Namespace cannot contain a null character');
  41. }
  42. // Basic options
  43. this.ns = ns;
  44. this.query = query;
  45. // Additional options
  46. this.numberToSkip = options.numberToSkip || 0;
  47. this.numberToReturn = options.numberToReturn || 0;
  48. this.returnFieldSelector = options.returnFieldSelector || undefined;
  49. this.requestId = Query.getRequestId();
  50. // special case for pre-3.2 find commands, delete ASAP
  51. this.pre32Limit = options.pre32Limit;
  52. // Serialization option
  53. this.serializeFunctions =
  54. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  55. this.ignoreUndefined =
  56. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  57. this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
  58. this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  59. this.batchSize = this.numberToReturn;
  60. // Flags
  61. this.tailable = false;
  62. this.secondaryOk = typeof options.secondaryOk === 'boolean' ? options.secondaryOk : false;
  63. this.oplogReplay = false;
  64. this.noCursorTimeout = false;
  65. this.awaitData = false;
  66. this.exhaust = false;
  67. this.partial = false;
  68. }
  69. /** Assign next request Id. */
  70. incRequestId() {
  71. this.requestId = _requestId++;
  72. }
  73. /** Peek next request Id. */
  74. nextRequestId() {
  75. return _requestId + 1;
  76. }
  77. /** Increment then return next request Id. */
  78. static getRequestId() {
  79. return ++_requestId;
  80. }
  81. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  82. toBin() {
  83. const buffers = [];
  84. let projection = null;
  85. // Set up the flags
  86. let flags = 0;
  87. if (this.tailable) {
  88. flags |= OPTS_TAILABLE_CURSOR;
  89. }
  90. if (this.secondaryOk) {
  91. flags |= OPTS_SECONDARY;
  92. }
  93. if (this.oplogReplay) {
  94. flags |= OPTS_OPLOG_REPLAY;
  95. }
  96. if (this.noCursorTimeout) {
  97. flags |= OPTS_NO_CURSOR_TIMEOUT;
  98. }
  99. if (this.awaitData) {
  100. flags |= OPTS_AWAIT_DATA;
  101. }
  102. if (this.exhaust) {
  103. flags |= OPTS_EXHAUST;
  104. }
  105. if (this.partial) {
  106. flags |= OPTS_PARTIAL;
  107. }
  108. // If batchSize is different to this.numberToReturn
  109. if (this.batchSize !== this.numberToReturn)
  110. this.numberToReturn = this.batchSize;
  111. // Allocate write protocol header buffer
  112. const header = Buffer.alloc(4 * 4 + // Header
  113. 4 + // Flags
  114. Buffer.byteLength(this.ns) +
  115. 1 + // namespace
  116. 4 + // numberToSkip
  117. 4 // numberToReturn
  118. );
  119. // Add header to buffers
  120. buffers.push(header);
  121. // Serialize the query
  122. const query = BSON.serialize(this.query, {
  123. checkKeys: this.checkKeys,
  124. serializeFunctions: this.serializeFunctions,
  125. ignoreUndefined: this.ignoreUndefined
  126. });
  127. // Add query document
  128. buffers.push(query);
  129. if (this.returnFieldSelector && Object.keys(this.returnFieldSelector).length > 0) {
  130. // Serialize the projection document
  131. projection = BSON.serialize(this.returnFieldSelector, {
  132. checkKeys: this.checkKeys,
  133. serializeFunctions: this.serializeFunctions,
  134. ignoreUndefined: this.ignoreUndefined
  135. });
  136. // Add projection document
  137. buffers.push(projection);
  138. }
  139. // Total message size
  140. const totalLength = header.length + query.length + (projection ? projection.length : 0);
  141. // Set up the index
  142. let index = 4;
  143. // Write total document length
  144. header[3] = (totalLength >> 24) & 0xff;
  145. header[2] = (totalLength >> 16) & 0xff;
  146. header[1] = (totalLength >> 8) & 0xff;
  147. header[0] = totalLength & 0xff;
  148. // Write header information requestId
  149. header[index + 3] = (this.requestId >> 24) & 0xff;
  150. header[index + 2] = (this.requestId >> 16) & 0xff;
  151. header[index + 1] = (this.requestId >> 8) & 0xff;
  152. header[index] = this.requestId & 0xff;
  153. index = index + 4;
  154. // Write header information responseTo
  155. header[index + 3] = (0 >> 24) & 0xff;
  156. header[index + 2] = (0 >> 16) & 0xff;
  157. header[index + 1] = (0 >> 8) & 0xff;
  158. header[index] = 0 & 0xff;
  159. index = index + 4;
  160. // Write header information OP_QUERY
  161. header[index + 3] = (constants_1.OP_QUERY >> 24) & 0xff;
  162. header[index + 2] = (constants_1.OP_QUERY >> 16) & 0xff;
  163. header[index + 1] = (constants_1.OP_QUERY >> 8) & 0xff;
  164. header[index] = constants_1.OP_QUERY & 0xff;
  165. index = index + 4;
  166. // Write header information flags
  167. header[index + 3] = (flags >> 24) & 0xff;
  168. header[index + 2] = (flags >> 16) & 0xff;
  169. header[index + 1] = (flags >> 8) & 0xff;
  170. header[index] = flags & 0xff;
  171. index = index + 4;
  172. // Write collection name
  173. index = index + header.write(this.ns, index, 'utf8') + 1;
  174. header[index - 1] = 0;
  175. // Write header information flags numberToSkip
  176. header[index + 3] = (this.numberToSkip >> 24) & 0xff;
  177. header[index + 2] = (this.numberToSkip >> 16) & 0xff;
  178. header[index + 1] = (this.numberToSkip >> 8) & 0xff;
  179. header[index] = this.numberToSkip & 0xff;
  180. index = index + 4;
  181. // Write header information flags numberToReturn
  182. header[index + 3] = (this.numberToReturn >> 24) & 0xff;
  183. header[index + 2] = (this.numberToReturn >> 16) & 0xff;
  184. header[index + 1] = (this.numberToReturn >> 8) & 0xff;
  185. header[index] = this.numberToReturn & 0xff;
  186. index = index + 4;
  187. // Return the buffers
  188. return buffers;
  189. }
  190. }
  191. exports.Query = Query;
  192. /**************************************************************
  193. * GETMORE
  194. **************************************************************/
  195. /** @internal */
  196. class GetMore {
  197. constructor(ns, cursorId, opts = {}) {
  198. this.numberToReturn = opts.numberToReturn || 0;
  199. this.requestId = _requestId++;
  200. this.ns = ns;
  201. this.cursorId = cursorId;
  202. }
  203. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  204. toBin() {
  205. const length = 4 + Buffer.byteLength(this.ns) + 1 + 4 + 8 + 4 * 4;
  206. // Create command buffer
  207. let index = 0;
  208. // Allocate buffer
  209. const _buffer = Buffer.alloc(length);
  210. // Write header information
  211. // index = write32bit(index, _buffer, length);
  212. _buffer[index + 3] = (length >> 24) & 0xff;
  213. _buffer[index + 2] = (length >> 16) & 0xff;
  214. _buffer[index + 1] = (length >> 8) & 0xff;
  215. _buffer[index] = length & 0xff;
  216. index = index + 4;
  217. // index = write32bit(index, _buffer, requestId);
  218. _buffer[index + 3] = (this.requestId >> 24) & 0xff;
  219. _buffer[index + 2] = (this.requestId >> 16) & 0xff;
  220. _buffer[index + 1] = (this.requestId >> 8) & 0xff;
  221. _buffer[index] = this.requestId & 0xff;
  222. index = index + 4;
  223. // index = write32bit(index, _buffer, 0);
  224. _buffer[index + 3] = (0 >> 24) & 0xff;
  225. _buffer[index + 2] = (0 >> 16) & 0xff;
  226. _buffer[index + 1] = (0 >> 8) & 0xff;
  227. _buffer[index] = 0 & 0xff;
  228. index = index + 4;
  229. // index = write32bit(index, _buffer, OP_GETMORE);
  230. _buffer[index + 3] = (constants_1.OP_GETMORE >> 24) & 0xff;
  231. _buffer[index + 2] = (constants_1.OP_GETMORE >> 16) & 0xff;
  232. _buffer[index + 1] = (constants_1.OP_GETMORE >> 8) & 0xff;
  233. _buffer[index] = constants_1.OP_GETMORE & 0xff;
  234. index = index + 4;
  235. // index = write32bit(index, _buffer, 0);
  236. _buffer[index + 3] = (0 >> 24) & 0xff;
  237. _buffer[index + 2] = (0 >> 16) & 0xff;
  238. _buffer[index + 1] = (0 >> 8) & 0xff;
  239. _buffer[index] = 0 & 0xff;
  240. index = index + 4;
  241. // Write collection name
  242. index = index + _buffer.write(this.ns, index, 'utf8') + 1;
  243. _buffer[index - 1] = 0;
  244. // Write batch size
  245. // index = write32bit(index, _buffer, numberToReturn);
  246. _buffer[index + 3] = (this.numberToReturn >> 24) & 0xff;
  247. _buffer[index + 2] = (this.numberToReturn >> 16) & 0xff;
  248. _buffer[index + 1] = (this.numberToReturn >> 8) & 0xff;
  249. _buffer[index] = this.numberToReturn & 0xff;
  250. index = index + 4;
  251. // Write cursor id
  252. // index = write32bit(index, _buffer, cursorId.getLowBits());
  253. _buffer[index + 3] = (this.cursorId.getLowBits() >> 24) & 0xff;
  254. _buffer[index + 2] = (this.cursorId.getLowBits() >> 16) & 0xff;
  255. _buffer[index + 1] = (this.cursorId.getLowBits() >> 8) & 0xff;
  256. _buffer[index] = this.cursorId.getLowBits() & 0xff;
  257. index = index + 4;
  258. // index = write32bit(index, _buffer, cursorId.getHighBits());
  259. _buffer[index + 3] = (this.cursorId.getHighBits() >> 24) & 0xff;
  260. _buffer[index + 2] = (this.cursorId.getHighBits() >> 16) & 0xff;
  261. _buffer[index + 1] = (this.cursorId.getHighBits() >> 8) & 0xff;
  262. _buffer[index] = this.cursorId.getHighBits() & 0xff;
  263. index = index + 4;
  264. // Return buffer
  265. return [_buffer];
  266. }
  267. }
  268. exports.GetMore = GetMore;
  269. /**************************************************************
  270. * KILLCURSOR
  271. **************************************************************/
  272. /** @internal */
  273. class KillCursor {
  274. constructor(ns, cursorIds) {
  275. this.ns = ns;
  276. this.requestId = _requestId++;
  277. this.cursorIds = cursorIds;
  278. }
  279. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  280. toBin() {
  281. const length = 4 + 4 + 4 * 4 + this.cursorIds.length * 8;
  282. // Create command buffer
  283. let index = 0;
  284. const _buffer = Buffer.alloc(length);
  285. // Write header information
  286. // index = write32bit(index, _buffer, length);
  287. _buffer[index + 3] = (length >> 24) & 0xff;
  288. _buffer[index + 2] = (length >> 16) & 0xff;
  289. _buffer[index + 1] = (length >> 8) & 0xff;
  290. _buffer[index] = length & 0xff;
  291. index = index + 4;
  292. // index = write32bit(index, _buffer, requestId);
  293. _buffer[index + 3] = (this.requestId >> 24) & 0xff;
  294. _buffer[index + 2] = (this.requestId >> 16) & 0xff;
  295. _buffer[index + 1] = (this.requestId >> 8) & 0xff;
  296. _buffer[index] = this.requestId & 0xff;
  297. index = index + 4;
  298. // index = write32bit(index, _buffer, 0);
  299. _buffer[index + 3] = (0 >> 24) & 0xff;
  300. _buffer[index + 2] = (0 >> 16) & 0xff;
  301. _buffer[index + 1] = (0 >> 8) & 0xff;
  302. _buffer[index] = 0 & 0xff;
  303. index = index + 4;
  304. // index = write32bit(index, _buffer, OP_KILL_CURSORS);
  305. _buffer[index + 3] = (constants_1.OP_KILL_CURSORS >> 24) & 0xff;
  306. _buffer[index + 2] = (constants_1.OP_KILL_CURSORS >> 16) & 0xff;
  307. _buffer[index + 1] = (constants_1.OP_KILL_CURSORS >> 8) & 0xff;
  308. _buffer[index] = constants_1.OP_KILL_CURSORS & 0xff;
  309. index = index + 4;
  310. // index = write32bit(index, _buffer, 0);
  311. _buffer[index + 3] = (0 >> 24) & 0xff;
  312. _buffer[index + 2] = (0 >> 16) & 0xff;
  313. _buffer[index + 1] = (0 >> 8) & 0xff;
  314. _buffer[index] = 0 & 0xff;
  315. index = index + 4;
  316. // Write batch size
  317. // index = write32bit(index, _buffer, this.cursorIds.length);
  318. _buffer[index + 3] = (this.cursorIds.length >> 24) & 0xff;
  319. _buffer[index + 2] = (this.cursorIds.length >> 16) & 0xff;
  320. _buffer[index + 1] = (this.cursorIds.length >> 8) & 0xff;
  321. _buffer[index] = this.cursorIds.length & 0xff;
  322. index = index + 4;
  323. // Write all the cursor ids into the array
  324. for (let i = 0; i < this.cursorIds.length; i++) {
  325. // Write cursor id
  326. // index = write32bit(index, _buffer, cursorIds[i].getLowBits());
  327. _buffer[index + 3] = (this.cursorIds[i].getLowBits() >> 24) & 0xff;
  328. _buffer[index + 2] = (this.cursorIds[i].getLowBits() >> 16) & 0xff;
  329. _buffer[index + 1] = (this.cursorIds[i].getLowBits() >> 8) & 0xff;
  330. _buffer[index] = this.cursorIds[i].getLowBits() & 0xff;
  331. index = index + 4;
  332. // index = write32bit(index, _buffer, cursorIds[i].getHighBits());
  333. _buffer[index + 3] = (this.cursorIds[i].getHighBits() >> 24) & 0xff;
  334. _buffer[index + 2] = (this.cursorIds[i].getHighBits() >> 16) & 0xff;
  335. _buffer[index + 1] = (this.cursorIds[i].getHighBits() >> 8) & 0xff;
  336. _buffer[index] = this.cursorIds[i].getHighBits() & 0xff;
  337. index = index + 4;
  338. }
  339. // Return buffer
  340. return [_buffer];
  341. }
  342. }
  343. exports.KillCursor = KillCursor;
  344. /** @internal */
  345. class Response {
  346. constructor(message, msgHeader, msgBody, opts) {
  347. this.documents = new Array(0);
  348. this.parsed = false;
  349. this.raw = message;
  350. this.data = msgBody;
  351. this.opts = opts !== null && opts !== void 0 ? opts : {
  352. promoteLongs: true,
  353. promoteValues: true,
  354. promoteBuffers: false,
  355. bsonRegExp: false
  356. };
  357. // Read the message header
  358. this.length = msgHeader.length;
  359. this.requestId = msgHeader.requestId;
  360. this.responseTo = msgHeader.responseTo;
  361. this.opCode = msgHeader.opCode;
  362. this.fromCompressed = msgHeader.fromCompressed;
  363. // Flag values
  364. this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
  365. this.promoteValues =
  366. typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
  367. this.promoteBuffers =
  368. typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
  369. this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
  370. }
  371. isParsed() {
  372. return this.parsed;
  373. }
  374. parse(options) {
  375. var _a, _b, _c, _d;
  376. // Don't parse again if not needed
  377. if (this.parsed)
  378. return;
  379. options = options !== null && options !== void 0 ? options : {};
  380. // Allow the return of raw documents instead of parsing
  381. const raw = options.raw || false;
  382. const documentsReturnedIn = options.documentsReturnedIn || null;
  383. const promoteLongs = (_a = options.promoteLongs) !== null && _a !== void 0 ? _a : this.opts.promoteLongs;
  384. const promoteValues = (_b = options.promoteValues) !== null && _b !== void 0 ? _b : this.opts.promoteValues;
  385. const promoteBuffers = (_c = options.promoteBuffers) !== null && _c !== void 0 ? _c : this.opts.promoteBuffers;
  386. const bsonRegExp = (_d = options.bsonRegExp) !== null && _d !== void 0 ? _d : this.opts.bsonRegExp;
  387. let bsonSize;
  388. // Set up the options
  389. const _options = {
  390. promoteLongs,
  391. promoteValues,
  392. promoteBuffers,
  393. bsonRegExp
  394. };
  395. // Position within OP_REPLY at which documents start
  396. // (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
  397. this.index = 20;
  398. // Read the message body
  399. this.responseFlags = this.data.readInt32LE(0);
  400. this.cursorId = new BSON.Long(this.data.readInt32LE(4), this.data.readInt32LE(8));
  401. this.startingFrom = this.data.readInt32LE(12);
  402. this.numberReturned = this.data.readInt32LE(16);
  403. // Preallocate document array
  404. this.documents = new Array(this.numberReturned);
  405. this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
  406. this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
  407. this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
  408. this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
  409. // Parse Body
  410. for (let i = 0; i < this.numberReturned; i++) {
  411. bsonSize =
  412. this.data[this.index] |
  413. (this.data[this.index + 1] << 8) |
  414. (this.data[this.index + 2] << 16) |
  415. (this.data[this.index + 3] << 24);
  416. // If we have raw results specified slice the return document
  417. if (raw) {
  418. this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
  419. }
  420. else {
  421. this.documents[i] = BSON.deserialize(this.data.slice(this.index, this.index + bsonSize), _options);
  422. }
  423. // Adjust the index
  424. this.index = this.index + bsonSize;
  425. }
  426. if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
  427. const fieldsAsRaw = {};
  428. fieldsAsRaw[documentsReturnedIn] = true;
  429. _options.fieldsAsRaw = fieldsAsRaw;
  430. const doc = BSON.deserialize(this.documents[0], _options);
  431. this.documents = [doc];
  432. }
  433. // Set parsed
  434. this.parsed = true;
  435. }
  436. }
  437. exports.Response = Response;
  438. // Implementation of OP_MSG spec:
  439. // https://github.com/mongodb/specifications/blob/master/source/message/OP_MSG.rst
  440. //
  441. // struct Section {
  442. // uint8 payloadType;
  443. // union payload {
  444. // document document; // payloadType == 0
  445. // struct sequence { // payloadType == 1
  446. // int32 size;
  447. // cstring identifier;
  448. // document* documents;
  449. // };
  450. // };
  451. // };
  452. // struct OP_MSG {
  453. // struct MsgHeader {
  454. // int32 messageLength;
  455. // int32 requestID;
  456. // int32 responseTo;
  457. // int32 opCode = 2013;
  458. // };
  459. // uint32 flagBits;
  460. // Section+ sections;
  461. // [uint32 checksum;]
  462. // };
  463. // Msg Flags
  464. const OPTS_CHECKSUM_PRESENT = 1;
  465. const OPTS_MORE_TO_COME = 2;
  466. const OPTS_EXHAUST_ALLOWED = 1 << 16;
  467. /** @internal */
  468. class Msg {
  469. constructor(ns, command, options) {
  470. // Basic options needed to be passed in
  471. if (command == null)
  472. throw new error_1.MongoInvalidArgumentError('Query document must be specified for query');
  473. // Basic options
  474. this.ns = ns;
  475. this.command = command;
  476. this.command.$db = (0, utils_1.databaseNamespace)(ns);
  477. if (options.readPreference && options.readPreference.mode !== read_preference_1.ReadPreference.PRIMARY) {
  478. this.command.$readPreference = options.readPreference.toJSON();
  479. }
  480. // Ensure empty options
  481. this.options = options !== null && options !== void 0 ? options : {};
  482. // Additional options
  483. this.requestId = options.requestId ? options.requestId : Msg.getRequestId();
  484. // Serialization option
  485. this.serializeFunctions =
  486. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  487. this.ignoreUndefined =
  488. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  489. this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  490. this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
  491. // flags
  492. this.checksumPresent = false;
  493. this.moreToCome = options.moreToCome || false;
  494. this.exhaustAllowed =
  495. typeof options.exhaustAllowed === 'boolean' ? options.exhaustAllowed : false;
  496. }
  497. toBin() {
  498. const buffers = [];
  499. let flags = 0;
  500. if (this.checksumPresent) {
  501. flags |= OPTS_CHECKSUM_PRESENT;
  502. }
  503. if (this.moreToCome) {
  504. flags |= OPTS_MORE_TO_COME;
  505. }
  506. if (this.exhaustAllowed) {
  507. flags |= OPTS_EXHAUST_ALLOWED;
  508. }
  509. const header = Buffer.alloc(4 * 4 + // Header
  510. 4 // Flags
  511. );
  512. buffers.push(header);
  513. let totalLength = header.length;
  514. const command = this.command;
  515. totalLength += this.makeDocumentSegment(buffers, command);
  516. header.writeInt32LE(totalLength, 0); // messageLength
  517. header.writeInt32LE(this.requestId, 4); // requestID
  518. header.writeInt32LE(0, 8); // responseTo
  519. header.writeInt32LE(constants_1.OP_MSG, 12); // opCode
  520. header.writeUInt32LE(flags, 16); // flags
  521. return buffers;
  522. }
  523. makeDocumentSegment(buffers, document) {
  524. const payloadTypeBuffer = Buffer.alloc(1);
  525. payloadTypeBuffer[0] = 0;
  526. const documentBuffer = this.serializeBson(document);
  527. buffers.push(payloadTypeBuffer);
  528. buffers.push(documentBuffer);
  529. return payloadTypeBuffer.length + documentBuffer.length;
  530. }
  531. serializeBson(document) {
  532. return BSON.serialize(document, {
  533. checkKeys: this.checkKeys,
  534. serializeFunctions: this.serializeFunctions,
  535. ignoreUndefined: this.ignoreUndefined
  536. });
  537. }
  538. static getRequestId() {
  539. _requestId = (_requestId + 1) & 0x7fffffff;
  540. return _requestId;
  541. }
  542. }
  543. exports.Msg = Msg;
  544. /** @internal */
  545. class BinMsg {
  546. constructor(message, msgHeader, msgBody, opts) {
  547. this.parsed = false;
  548. this.raw = message;
  549. this.data = msgBody;
  550. this.opts = opts !== null && opts !== void 0 ? opts : {
  551. promoteLongs: true,
  552. promoteValues: true,
  553. promoteBuffers: false,
  554. bsonRegExp: false
  555. };
  556. // Read the message header
  557. this.length = msgHeader.length;
  558. this.requestId = msgHeader.requestId;
  559. this.responseTo = msgHeader.responseTo;
  560. this.opCode = msgHeader.opCode;
  561. this.fromCompressed = msgHeader.fromCompressed;
  562. // Read response flags
  563. this.responseFlags = msgBody.readInt32LE(0);
  564. this.checksumPresent = (this.responseFlags & OPTS_CHECKSUM_PRESENT) !== 0;
  565. this.moreToCome = (this.responseFlags & OPTS_MORE_TO_COME) !== 0;
  566. this.exhaustAllowed = (this.responseFlags & OPTS_EXHAUST_ALLOWED) !== 0;
  567. this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
  568. this.promoteValues =
  569. typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
  570. this.promoteBuffers =
  571. typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
  572. this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
  573. this.documents = [];
  574. }
  575. isParsed() {
  576. return this.parsed;
  577. }
  578. parse(options) {
  579. var _a, _b, _c, _d;
  580. // Don't parse again if not needed
  581. if (this.parsed)
  582. return;
  583. options = options !== null && options !== void 0 ? options : {};
  584. this.index = 4;
  585. // Allow the return of raw documents instead of parsing
  586. const raw = options.raw || false;
  587. const documentsReturnedIn = options.documentsReturnedIn || null;
  588. const promoteLongs = (_a = options.promoteLongs) !== null && _a !== void 0 ? _a : this.opts.promoteLongs;
  589. const promoteValues = (_b = options.promoteValues) !== null && _b !== void 0 ? _b : this.opts.promoteValues;
  590. const promoteBuffers = (_c = options.promoteBuffers) !== null && _c !== void 0 ? _c : this.opts.promoteBuffers;
  591. const bsonRegExp = (_d = options.bsonRegExp) !== null && _d !== void 0 ? _d : this.opts.bsonRegExp;
  592. const validation = this.parseBsonSerializationOptions(options);
  593. // Set up the options
  594. const bsonOptions = {
  595. promoteLongs,
  596. promoteValues,
  597. promoteBuffers,
  598. bsonRegExp,
  599. validation
  600. // Due to the strictness of the BSON libraries validation option we need this cast
  601. };
  602. while (this.index < this.data.length) {
  603. const payloadType = this.data.readUInt8(this.index++);
  604. if (payloadType === 0) {
  605. const bsonSize = this.data.readUInt32LE(this.index);
  606. const bin = this.data.slice(this.index, this.index + bsonSize);
  607. this.documents.push(raw ? bin : BSON.deserialize(bin, bsonOptions));
  608. this.index += bsonSize;
  609. }
  610. else if (payloadType === 1) {
  611. // It was decided that no driver makes use of payload type 1
  612. // TODO(NODE-3483): Replace with MongoDeprecationError
  613. throw new error_1.MongoRuntimeError('OP_MSG Payload Type 1 detected unsupported protocol');
  614. }
  615. }
  616. if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
  617. const fieldsAsRaw = {};
  618. fieldsAsRaw[documentsReturnedIn] = true;
  619. bsonOptions.fieldsAsRaw = fieldsAsRaw;
  620. const doc = BSON.deserialize(this.documents[0], bsonOptions);
  621. this.documents = [doc];
  622. }
  623. this.parsed = true;
  624. }
  625. parseBsonSerializationOptions({ enableUtf8Validation }) {
  626. if (enableUtf8Validation === false) {
  627. return { utf8: false };
  628. }
  629. return { utf8: { writeErrors: false } };
  630. }
  631. }
  632. exports.BinMsg = BinMsg;
  633. //# sourceMappingURL=commands.js.map