commands.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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.parsed = false;
  348. this.raw = message;
  349. this.data = msgBody;
  350. this.opts = opts !== null && opts !== void 0 ? opts : {
  351. promoteLongs: true,
  352. promoteValues: true,
  353. promoteBuffers: false,
  354. bsonRegExp: false
  355. };
  356. // Read the message header
  357. this.length = msgHeader.length;
  358. this.requestId = msgHeader.requestId;
  359. this.responseTo = msgHeader.responseTo;
  360. this.opCode = msgHeader.opCode;
  361. this.fromCompressed = msgHeader.fromCompressed;
  362. // Read the message body
  363. this.responseFlags = msgBody.readInt32LE(0);
  364. this.cursorId = new BSON.Long(msgBody.readInt32LE(4), msgBody.readInt32LE(8));
  365. this.startingFrom = msgBody.readInt32LE(12);
  366. this.numberReturned = msgBody.readInt32LE(16);
  367. // Preallocate document array
  368. this.documents = new Array(this.numberReturned);
  369. // Flag values
  370. this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
  371. this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
  372. this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
  373. this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
  374. this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
  375. this.promoteValues =
  376. typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
  377. this.promoteBuffers =
  378. typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
  379. this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
  380. }
  381. isParsed() {
  382. return this.parsed;
  383. }
  384. parse(options) {
  385. var _a, _b, _c, _d;
  386. // Don't parse again if not needed
  387. if (this.parsed)
  388. return;
  389. options = options !== null && options !== void 0 ? options : {};
  390. // Allow the return of raw documents instead of parsing
  391. const raw = options.raw || false;
  392. const documentsReturnedIn = options.documentsReturnedIn || null;
  393. const promoteLongs = (_a = options.promoteLongs) !== null && _a !== void 0 ? _a : this.opts.promoteLongs;
  394. const promoteValues = (_b = options.promoteValues) !== null && _b !== void 0 ? _b : this.opts.promoteValues;
  395. const promoteBuffers = (_c = options.promoteBuffers) !== null && _c !== void 0 ? _c : this.opts.promoteBuffers;
  396. const bsonRegExp = (_d = options.bsonRegExp) !== null && _d !== void 0 ? _d : this.opts.bsonRegExp;
  397. let bsonSize;
  398. // Set up the options
  399. const _options = {
  400. promoteLongs,
  401. promoteValues,
  402. promoteBuffers,
  403. bsonRegExp
  404. };
  405. // Position within OP_REPLY at which documents start
  406. // (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
  407. this.index = 20;
  408. // Parse Body
  409. for (let i = 0; i < this.numberReturned; i++) {
  410. bsonSize =
  411. this.data[this.index] |
  412. (this.data[this.index + 1] << 8) |
  413. (this.data[this.index + 2] << 16) |
  414. (this.data[this.index + 3] << 24);
  415. // If we have raw results specified slice the return document
  416. if (raw) {
  417. this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
  418. }
  419. else {
  420. this.documents[i] = BSON.deserialize(this.data.slice(this.index, this.index + bsonSize), _options);
  421. }
  422. // Adjust the index
  423. this.index = this.index + bsonSize;
  424. }
  425. if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
  426. const fieldsAsRaw = {};
  427. fieldsAsRaw[documentsReturnedIn] = true;
  428. _options.fieldsAsRaw = fieldsAsRaw;
  429. const doc = BSON.deserialize(this.documents[0], _options);
  430. this.documents = [doc];
  431. }
  432. // Set parsed
  433. this.parsed = true;
  434. }
  435. }
  436. exports.Response = Response;
  437. // Implementation of OP_MSG spec:
  438. // https://github.com/mongodb/specifications/blob/master/source/message/OP_MSG.rst
  439. //
  440. // struct Section {
  441. // uint8 payloadType;
  442. // union payload {
  443. // document document; // payloadType == 0
  444. // struct sequence { // payloadType == 1
  445. // int32 size;
  446. // cstring identifier;
  447. // document* documents;
  448. // };
  449. // };
  450. // };
  451. // struct OP_MSG {
  452. // struct MsgHeader {
  453. // int32 messageLength;
  454. // int32 requestID;
  455. // int32 responseTo;
  456. // int32 opCode = 2013;
  457. // };
  458. // uint32 flagBits;
  459. // Section+ sections;
  460. // [uint32 checksum;]
  461. // };
  462. // Msg Flags
  463. const OPTS_CHECKSUM_PRESENT = 1;
  464. const OPTS_MORE_TO_COME = 2;
  465. const OPTS_EXHAUST_ALLOWED = 1 << 16;
  466. /** @internal */
  467. class Msg {
  468. constructor(ns, command, options) {
  469. // Basic options needed to be passed in
  470. if (command == null)
  471. throw new error_1.MongoInvalidArgumentError('Query document must be specified for query');
  472. // Basic options
  473. this.ns = ns;
  474. this.command = command;
  475. this.command.$db = (0, utils_1.databaseNamespace)(ns);
  476. if (options.readPreference && options.readPreference.mode !== read_preference_1.ReadPreference.PRIMARY) {
  477. this.command.$readPreference = options.readPreference.toJSON();
  478. }
  479. // Ensure empty options
  480. this.options = options !== null && options !== void 0 ? options : {};
  481. // Additional options
  482. this.requestId = options.requestId ? options.requestId : Msg.getRequestId();
  483. // Serialization option
  484. this.serializeFunctions =
  485. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  486. this.ignoreUndefined =
  487. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  488. this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  489. this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
  490. // flags
  491. this.checksumPresent = false;
  492. this.moreToCome = options.moreToCome || false;
  493. this.exhaustAllowed =
  494. typeof options.exhaustAllowed === 'boolean' ? options.exhaustAllowed : false;
  495. }
  496. toBin() {
  497. const buffers = [];
  498. let flags = 0;
  499. if (this.checksumPresent) {
  500. flags |= OPTS_CHECKSUM_PRESENT;
  501. }
  502. if (this.moreToCome) {
  503. flags |= OPTS_MORE_TO_COME;
  504. }
  505. if (this.exhaustAllowed) {
  506. flags |= OPTS_EXHAUST_ALLOWED;
  507. }
  508. const header = Buffer.alloc(4 * 4 + // Header
  509. 4 // Flags
  510. );
  511. buffers.push(header);
  512. let totalLength = header.length;
  513. const command = this.command;
  514. totalLength += this.makeDocumentSegment(buffers, command);
  515. header.writeInt32LE(totalLength, 0); // messageLength
  516. header.writeInt32LE(this.requestId, 4); // requestID
  517. header.writeInt32LE(0, 8); // responseTo
  518. header.writeInt32LE(constants_1.OP_MSG, 12); // opCode
  519. header.writeUInt32LE(flags, 16); // flags
  520. return buffers;
  521. }
  522. makeDocumentSegment(buffers, document) {
  523. const payloadTypeBuffer = Buffer.alloc(1);
  524. payloadTypeBuffer[0] = 0;
  525. const documentBuffer = this.serializeBson(document);
  526. buffers.push(payloadTypeBuffer);
  527. buffers.push(documentBuffer);
  528. return payloadTypeBuffer.length + documentBuffer.length;
  529. }
  530. serializeBson(document) {
  531. return BSON.serialize(document, {
  532. checkKeys: this.checkKeys,
  533. serializeFunctions: this.serializeFunctions,
  534. ignoreUndefined: this.ignoreUndefined
  535. });
  536. }
  537. static getRequestId() {
  538. _requestId = (_requestId + 1) & 0x7fffffff;
  539. return _requestId;
  540. }
  541. }
  542. exports.Msg = Msg;
  543. /** @internal */
  544. class BinMsg {
  545. constructor(message, msgHeader, msgBody, opts) {
  546. this.parsed = false;
  547. this.raw = message;
  548. this.data = msgBody;
  549. this.opts = opts !== null && opts !== void 0 ? opts : {
  550. promoteLongs: true,
  551. promoteValues: true,
  552. promoteBuffers: false,
  553. bsonRegExp: false
  554. };
  555. // Read the message header
  556. this.length = msgHeader.length;
  557. this.requestId = msgHeader.requestId;
  558. this.responseTo = msgHeader.responseTo;
  559. this.opCode = msgHeader.opCode;
  560. this.fromCompressed = msgHeader.fromCompressed;
  561. // Read response flags
  562. this.responseFlags = msgBody.readInt32LE(0);
  563. this.checksumPresent = (this.responseFlags & OPTS_CHECKSUM_PRESENT) !== 0;
  564. this.moreToCome = (this.responseFlags & OPTS_MORE_TO_COME) !== 0;
  565. this.exhaustAllowed = (this.responseFlags & OPTS_EXHAUST_ALLOWED) !== 0;
  566. this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
  567. this.promoteValues =
  568. typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
  569. this.promoteBuffers =
  570. typeof this.opts.promoteBuffers === 'boolean' ? this.opts.promoteBuffers : false;
  571. this.bsonRegExp = typeof this.opts.bsonRegExp === 'boolean' ? this.opts.bsonRegExp : false;
  572. this.documents = [];
  573. }
  574. isParsed() {
  575. return this.parsed;
  576. }
  577. parse(options) {
  578. var _a, _b, _c, _d;
  579. // Don't parse again if not needed
  580. if (this.parsed)
  581. return;
  582. options = options !== null && options !== void 0 ? options : {};
  583. this.index = 4;
  584. // Allow the return of raw documents instead of parsing
  585. const raw = options.raw || false;
  586. const documentsReturnedIn = options.documentsReturnedIn || null;
  587. const promoteLongs = (_a = options.promoteLongs) !== null && _a !== void 0 ? _a : this.opts.promoteLongs;
  588. const promoteValues = (_b = options.promoteValues) !== null && _b !== void 0 ? _b : this.opts.promoteValues;
  589. const promoteBuffers = (_c = options.promoteBuffers) !== null && _c !== void 0 ? _c : this.opts.promoteBuffers;
  590. const bsonRegExp = (_d = options.bsonRegExp) !== null && _d !== void 0 ? _d : this.opts.bsonRegExp;
  591. const validation = this.parseBsonSerializationOptions(options);
  592. // Set up the options
  593. const bsonOptions = {
  594. promoteLongs,
  595. promoteValues,
  596. promoteBuffers,
  597. bsonRegExp,
  598. validation
  599. // Due to the strictness of the BSON libraries validation option we need this cast
  600. };
  601. while (this.index < this.data.length) {
  602. const payloadType = this.data.readUInt8(this.index++);
  603. if (payloadType === 0) {
  604. const bsonSize = this.data.readUInt32LE(this.index);
  605. const bin = this.data.slice(this.index, this.index + bsonSize);
  606. this.documents.push(raw ? bin : BSON.deserialize(bin, bsonOptions));
  607. this.index += bsonSize;
  608. }
  609. else if (payloadType === 1) {
  610. // It was decided that no driver makes use of payload type 1
  611. // TODO(NODE-3483): Replace with MongoDeprecationError
  612. throw new error_1.MongoRuntimeError('OP_MSG Payload Type 1 detected unsupported protocol');
  613. }
  614. }
  615. if (this.documents.length === 1 && documentsReturnedIn != null && raw) {
  616. const fieldsAsRaw = {};
  617. fieldsAsRaw[documentsReturnedIn] = true;
  618. bsonOptions.fieldsAsRaw = fieldsAsRaw;
  619. const doc = BSON.deserialize(this.documents[0], bsonOptions);
  620. this.documents = [doc];
  621. }
  622. this.parsed = true;
  623. }
  624. parseBsonSerializationOptions({ enableUtf8Validation }) {
  625. if (enableUtf8Validation === false) {
  626. return { utf8: false };
  627. }
  628. return { utf8: { writeErrors: false } };
  629. }
  630. }
  631. exports.BinMsg = BinMsg;
  632. //# sourceMappingURL=commands.js.map