inbound-parser.test.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. var __importDefault = (this && this.__importDefault) || function (mod) {
  12. return (mod && mod.__esModule) ? mod : { "default": mod };
  13. };
  14. Object.defineProperty(exports, "__esModule", { value: true });
  15. const test_buffers_1 = __importDefault(require("./testing/test-buffers"));
  16. const buffer_list_1 = __importDefault(require("./testing/buffer-list"));
  17. const _1 = require(".");
  18. const assert_1 = __importDefault(require("assert"));
  19. const stream_1 = require("stream");
  20. const parser_1 = require("./parser");
  21. const authOkBuffer = test_buffers_1.default.authenticationOk();
  22. const paramStatusBuffer = test_buffers_1.default.parameterStatus('client_encoding', 'UTF8');
  23. const readyForQueryBuffer = test_buffers_1.default.readyForQuery();
  24. const backendKeyDataBuffer = test_buffers_1.default.backendKeyData(1, 2);
  25. const commandCompleteBuffer = test_buffers_1.default.commandComplete('SELECT 3');
  26. const parseCompleteBuffer = test_buffers_1.default.parseComplete();
  27. const bindCompleteBuffer = test_buffers_1.default.bindComplete();
  28. const portalSuspendedBuffer = test_buffers_1.default.portalSuspended();
  29. const row1 = {
  30. name: 'id',
  31. tableID: 1,
  32. attributeNumber: 2,
  33. dataTypeID: 3,
  34. dataTypeSize: 4,
  35. typeModifier: 5,
  36. formatCode: 0,
  37. };
  38. const oneRowDescBuff = test_buffers_1.default.rowDescription([row1]);
  39. row1.name = 'bang';
  40. const twoRowBuf = test_buffers_1.default.rowDescription([
  41. row1,
  42. {
  43. name: 'whoah',
  44. tableID: 10,
  45. attributeNumber: 11,
  46. dataTypeID: 12,
  47. dataTypeSize: 13,
  48. typeModifier: 14,
  49. formatCode: 0,
  50. },
  51. ]);
  52. const rowWithBigOids = {
  53. name: 'bigoid',
  54. tableID: 3000000001,
  55. attributeNumber: 2,
  56. dataTypeID: 3000000003,
  57. dataTypeSize: 4,
  58. typeModifier: 5,
  59. formatCode: 0,
  60. };
  61. const bigOidDescBuff = test_buffers_1.default.rowDescription([rowWithBigOids]);
  62. const emptyRowFieldBuf = test_buffers_1.default.dataRow([]);
  63. const oneFieldBuf = test_buffers_1.default.dataRow(['test']);
  64. const expectedAuthenticationOkayMessage = {
  65. name: 'authenticationOk',
  66. length: 8,
  67. };
  68. const expectedParameterStatusMessage = {
  69. name: 'parameterStatus',
  70. parameterName: 'client_encoding',
  71. parameterValue: 'UTF8',
  72. length: 25,
  73. };
  74. const expectedBackendKeyDataMessage = {
  75. name: 'backendKeyData',
  76. processID: 1,
  77. secretKey: 2,
  78. };
  79. const expectedReadyForQueryMessage = {
  80. name: 'readyForQuery',
  81. length: 5,
  82. status: 'I',
  83. };
  84. const expectedCommandCompleteMessage = {
  85. name: 'commandComplete',
  86. length: 13,
  87. text: 'SELECT 3',
  88. };
  89. const emptyRowDescriptionBuffer = new buffer_list_1.default()
  90. .addInt16(0) // number of fields
  91. .join(true, 'T');
  92. const expectedEmptyRowDescriptionMessage = {
  93. name: 'rowDescription',
  94. length: 6,
  95. fieldCount: 0,
  96. fields: [],
  97. };
  98. const expectedOneRowMessage = {
  99. name: 'rowDescription',
  100. length: 27,
  101. fieldCount: 1,
  102. fields: [
  103. {
  104. name: 'id',
  105. tableID: 1,
  106. columnID: 2,
  107. dataTypeID: 3,
  108. dataTypeSize: 4,
  109. dataTypeModifier: 5,
  110. format: 'text',
  111. },
  112. ],
  113. };
  114. const expectedTwoRowMessage = {
  115. name: 'rowDescription',
  116. length: 53,
  117. fieldCount: 2,
  118. fields: [
  119. {
  120. name: 'bang',
  121. tableID: 1,
  122. columnID: 2,
  123. dataTypeID: 3,
  124. dataTypeSize: 4,
  125. dataTypeModifier: 5,
  126. format: 'text',
  127. },
  128. {
  129. name: 'whoah',
  130. tableID: 10,
  131. columnID: 11,
  132. dataTypeID: 12,
  133. dataTypeSize: 13,
  134. dataTypeModifier: 14,
  135. format: 'text',
  136. },
  137. ],
  138. };
  139. const expectedBigOidMessage = {
  140. name: 'rowDescription',
  141. length: 31,
  142. fieldCount: 1,
  143. fields: [
  144. {
  145. name: 'bigoid',
  146. tableID: 3000000001,
  147. columnID: 2,
  148. dataTypeID: 3000000003,
  149. dataTypeSize: 4,
  150. dataTypeModifier: 5,
  151. format: 'text',
  152. },
  153. ],
  154. };
  155. const emptyParameterDescriptionBuffer = new buffer_list_1.default()
  156. .addInt16(0) // number of parameters
  157. .join(true, 't');
  158. const oneParameterDescBuf = test_buffers_1.default.parameterDescription([1111]);
  159. const twoParameterDescBuf = test_buffers_1.default.parameterDescription([2222, 3333]);
  160. const expectedEmptyParameterDescriptionMessage = {
  161. name: 'parameterDescription',
  162. length: 6,
  163. parameterCount: 0,
  164. dataTypeIDs: [],
  165. };
  166. const expectedOneParameterMessage = {
  167. name: 'parameterDescription',
  168. length: 10,
  169. parameterCount: 1,
  170. dataTypeIDs: [1111],
  171. };
  172. const expectedTwoParameterMessage = {
  173. name: 'parameterDescription',
  174. length: 14,
  175. parameterCount: 2,
  176. dataTypeIDs: [2222, 3333],
  177. };
  178. const testForMessage = function (buffer, expectedMessage) {
  179. it('receives and parses ' + expectedMessage.name, () => __awaiter(this, void 0, void 0, function* () {
  180. const messages = yield parseBuffers([buffer]);
  181. const [lastMessage] = messages;
  182. for (const key in expectedMessage) {
  183. assert_1.default.deepEqual(lastMessage[key], expectedMessage[key]);
  184. }
  185. }));
  186. };
  187. const plainPasswordBuffer = test_buffers_1.default.authenticationCleartextPassword();
  188. const md5PasswordBuffer = test_buffers_1.default.authenticationMD5Password();
  189. const SASLBuffer = test_buffers_1.default.authenticationSASL();
  190. const SASLContinueBuffer = test_buffers_1.default.authenticationSASLContinue();
  191. const SASLFinalBuffer = test_buffers_1.default.authenticationSASLFinal();
  192. const expectedPlainPasswordMessage = {
  193. name: 'authenticationCleartextPassword',
  194. };
  195. const expectedMD5PasswordMessage = {
  196. name: 'authenticationMD5Password',
  197. salt: Buffer.from([1, 2, 3, 4]),
  198. };
  199. const expectedSASLMessage = {
  200. name: 'authenticationSASL',
  201. mechanisms: ['SCRAM-SHA-256'],
  202. };
  203. const expectedSASLContinueMessage = {
  204. name: 'authenticationSASLContinue',
  205. data: 'data',
  206. };
  207. const expectedSASLFinalMessage = {
  208. name: 'authenticationSASLFinal',
  209. data: 'data',
  210. };
  211. const notificationResponseBuffer = test_buffers_1.default.notification(4, 'hi', 'boom');
  212. const expectedNotificationResponseMessage = {
  213. name: 'notification',
  214. processId: 4,
  215. channel: 'hi',
  216. payload: 'boom',
  217. };
  218. const parseBuffers = (buffers) => __awaiter(void 0, void 0, void 0, function* () {
  219. const stream = new stream_1.PassThrough();
  220. for (const buffer of buffers) {
  221. stream.write(buffer);
  222. }
  223. stream.end();
  224. const msgs = [];
  225. yield (0, _1.parse)(stream, (msg) => msgs.push(msg));
  226. return msgs;
  227. });
  228. describe('PgPacketStream', function () {
  229. testForMessage(authOkBuffer, expectedAuthenticationOkayMessage);
  230. testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage);
  231. testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage);
  232. testForMessage(SASLBuffer, expectedSASLMessage);
  233. testForMessage(SASLContinueBuffer, expectedSASLContinueMessage);
  234. // this exercises a found bug in the parser:
  235. // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
  236. // and adds a test which is deterministic, rather than relying on network packet chunking
  237. const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])]);
  238. testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage);
  239. testForMessage(SASLFinalBuffer, expectedSASLFinalMessage);
  240. // this exercises a found bug in the parser:
  241. // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084
  242. // and adds a test which is deterministic, rather than relying on network packet chunking
  243. const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])]);
  244. testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage);
  245. testForMessage(paramStatusBuffer, expectedParameterStatusMessage);
  246. testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage);
  247. testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage);
  248. testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage);
  249. testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage);
  250. testForMessage(test_buffers_1.default.emptyQuery(), {
  251. name: 'emptyQuery',
  252. length: 4,
  253. });
  254. testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), {
  255. name: 'noData',
  256. });
  257. describe('rowDescription messages', function () {
  258. testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage);
  259. testForMessage(oneRowDescBuff, expectedOneRowMessage);
  260. testForMessage(twoRowBuf, expectedTwoRowMessage);
  261. testForMessage(bigOidDescBuff, expectedBigOidMessage);
  262. });
  263. describe('parameterDescription messages', function () {
  264. testForMessage(emptyParameterDescriptionBuffer, expectedEmptyParameterDescriptionMessage);
  265. testForMessage(oneParameterDescBuf, expectedOneParameterMessage);
  266. testForMessage(twoParameterDescBuf, expectedTwoParameterMessage);
  267. });
  268. describe('parsing rows', function () {
  269. describe('parsing empty row', function () {
  270. testForMessage(emptyRowFieldBuf, {
  271. name: 'dataRow',
  272. fieldCount: 0,
  273. });
  274. });
  275. describe('parsing data row with fields', function () {
  276. testForMessage(oneFieldBuf, {
  277. name: 'dataRow',
  278. fieldCount: 1,
  279. fields: ['test'],
  280. });
  281. });
  282. });
  283. describe('notice message', function () {
  284. // this uses the same logic as error message
  285. const buff = test_buffers_1.default.notice([{ type: 'C', value: 'code' }]);
  286. testForMessage(buff, {
  287. name: 'notice',
  288. code: 'code',
  289. });
  290. });
  291. testForMessage(test_buffers_1.default.error([]), {
  292. name: 'error',
  293. });
  294. describe('with all the fields', function () {
  295. const buffer = test_buffers_1.default.error([
  296. {
  297. type: 'S',
  298. value: 'ERROR',
  299. },
  300. {
  301. type: 'C',
  302. value: 'code',
  303. },
  304. {
  305. type: 'M',
  306. value: 'message',
  307. },
  308. {
  309. type: 'D',
  310. value: 'details',
  311. },
  312. {
  313. type: 'H',
  314. value: 'hint',
  315. },
  316. {
  317. type: 'P',
  318. value: '100',
  319. },
  320. {
  321. type: 'p',
  322. value: '101',
  323. },
  324. {
  325. type: 'q',
  326. value: 'query',
  327. },
  328. {
  329. type: 'W',
  330. value: 'where',
  331. },
  332. {
  333. type: 'F',
  334. value: 'file',
  335. },
  336. {
  337. type: 'L',
  338. value: 'line',
  339. },
  340. {
  341. type: 'R',
  342. value: 'routine',
  343. },
  344. {
  345. type: 'Z',
  346. value: 'alsdkf',
  347. },
  348. ]);
  349. testForMessage(buffer, {
  350. name: 'error',
  351. severity: 'ERROR',
  352. code: 'code',
  353. message: 'message',
  354. detail: 'details',
  355. hint: 'hint',
  356. position: '100',
  357. internalPosition: '101',
  358. internalQuery: 'query',
  359. where: 'where',
  360. file: 'file',
  361. line: 'line',
  362. routine: 'routine',
  363. });
  364. });
  365. testForMessage(parseCompleteBuffer, {
  366. name: 'parseComplete',
  367. });
  368. testForMessage(bindCompleteBuffer, {
  369. name: 'bindComplete',
  370. });
  371. testForMessage(bindCompleteBuffer, {
  372. name: 'bindComplete',
  373. });
  374. testForMessage(test_buffers_1.default.closeComplete(), {
  375. name: 'closeComplete',
  376. });
  377. describe('parses portal suspended message', function () {
  378. testForMessage(portalSuspendedBuffer, {
  379. name: 'portalSuspended',
  380. });
  381. });
  382. describe('parses replication start message', function () {
  383. testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), {
  384. name: 'replicationStart',
  385. length: 4,
  386. });
  387. });
  388. describe('copy', () => {
  389. testForMessage(test_buffers_1.default.copyIn(0), {
  390. name: 'copyInResponse',
  391. length: 7,
  392. binary: false,
  393. columnTypes: [],
  394. });
  395. testForMessage(test_buffers_1.default.copyIn(2), {
  396. name: 'copyInResponse',
  397. length: 11,
  398. binary: false,
  399. columnTypes: [0, 1],
  400. });
  401. testForMessage(test_buffers_1.default.copyOut(0), {
  402. name: 'copyOutResponse',
  403. length: 7,
  404. binary: false,
  405. columnTypes: [],
  406. });
  407. testForMessage(test_buffers_1.default.copyOut(3), {
  408. name: 'copyOutResponse',
  409. length: 13,
  410. binary: false,
  411. columnTypes: [0, 1, 2],
  412. });
  413. testForMessage(test_buffers_1.default.copyDone(), {
  414. name: 'copyDone',
  415. length: 4,
  416. });
  417. testForMessage(test_buffers_1.default.copyData(Buffer.from([5, 6, 7])), {
  418. name: 'copyData',
  419. length: 7,
  420. chunk: Buffer.from([5, 6, 7]),
  421. });
  422. });
  423. // since the data message on a stream can randomly divide the incomming
  424. // tcp packets anywhere, we need to make sure we can parse every single
  425. // split on a tcp message
  426. describe('split buffer, single message parsing', function () {
  427. const fullBuffer = test_buffers_1.default.dataRow([null, 'bang', 'zug zug', null, '!']);
  428. it('parses when full buffer comes in', function () {
  429. return __awaiter(this, void 0, void 0, function* () {
  430. const messages = yield parseBuffers([fullBuffer]);
  431. const message = messages[0];
  432. assert_1.default.equal(message.fields.length, 5);
  433. assert_1.default.equal(message.fields[0], null);
  434. assert_1.default.equal(message.fields[1], 'bang');
  435. assert_1.default.equal(message.fields[2], 'zug zug');
  436. assert_1.default.equal(message.fields[3], null);
  437. assert_1.default.equal(message.fields[4], '!');
  438. });
  439. });
  440. const testMessageReceivedAfterSplitAt = function (split) {
  441. return __awaiter(this, void 0, void 0, function* () {
  442. const firstBuffer = Buffer.alloc(fullBuffer.length - split);
  443. const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
  444. fullBuffer.copy(firstBuffer, 0, 0);
  445. fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
  446. const messages = yield parseBuffers([firstBuffer, secondBuffer]);
  447. const message = messages[0];
  448. assert_1.default.equal(message.fields.length, 5);
  449. assert_1.default.equal(message.fields[0], null);
  450. assert_1.default.equal(message.fields[1], 'bang');
  451. assert_1.default.equal(message.fields[2], 'zug zug');
  452. assert_1.default.equal(message.fields[3], null);
  453. assert_1.default.equal(message.fields[4], '!');
  454. });
  455. };
  456. it('parses when split in the middle', function () {
  457. return testMessageReceivedAfterSplitAt(6);
  458. });
  459. it('parses when split at end', function () {
  460. return testMessageReceivedAfterSplitAt(2);
  461. });
  462. it('parses when split at beginning', function () {
  463. return Promise.all([
  464. testMessageReceivedAfterSplitAt(fullBuffer.length - 2),
  465. testMessageReceivedAfterSplitAt(fullBuffer.length - 1),
  466. testMessageReceivedAfterSplitAt(fullBuffer.length - 5),
  467. ]);
  468. });
  469. });
  470. describe('split buffer, multiple message parsing', function () {
  471. const dataRowBuffer = test_buffers_1.default.dataRow(['!']);
  472. const readyForQueryBuffer = test_buffers_1.default.readyForQuery();
  473. const fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length);
  474. dataRowBuffer.copy(fullBuffer, 0, 0);
  475. readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0);
  476. const verifyMessages = function (messages) {
  477. assert_1.default.strictEqual(messages.length, 2);
  478. assert_1.default.deepEqual(messages[0], {
  479. name: 'dataRow',
  480. fieldCount: 1,
  481. length: 11,
  482. fields: ['!'],
  483. });
  484. assert_1.default.equal(messages[0].fields[0], '!');
  485. assert_1.default.deepEqual(messages[1], {
  486. name: 'readyForQuery',
  487. length: 5,
  488. status: 'I',
  489. });
  490. };
  491. // sanity check
  492. it('receives both messages when packet is not split', function () {
  493. return __awaiter(this, void 0, void 0, function* () {
  494. const messages = yield parseBuffers([fullBuffer]);
  495. verifyMessages(messages);
  496. });
  497. });
  498. const splitAndVerifyTwoMessages = function (split) {
  499. return __awaiter(this, void 0, void 0, function* () {
  500. const firstBuffer = Buffer.alloc(fullBuffer.length - split);
  501. const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length);
  502. fullBuffer.copy(firstBuffer, 0, 0);
  503. fullBuffer.copy(secondBuffer, 0, firstBuffer.length);
  504. const messages = yield parseBuffers([firstBuffer, secondBuffer]);
  505. verifyMessages(messages);
  506. });
  507. };
  508. describe('receives both messages when packet is split', function () {
  509. it('in the middle', function () {
  510. return splitAndVerifyTwoMessages(11);
  511. });
  512. it('at the front', function () {
  513. return Promise.all([
  514. splitAndVerifyTwoMessages(fullBuffer.length - 1),
  515. splitAndVerifyTwoMessages(fullBuffer.length - 4),
  516. splitAndVerifyTwoMessages(fullBuffer.length - 6),
  517. ]);
  518. });
  519. it('at the end', function () {
  520. return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]);
  521. });
  522. });
  523. });
  524. it('cleans up the reader after handling a packet', function () {
  525. const parser = new parser_1.Parser();
  526. parser.parse(oneFieldBuf, () => { });
  527. assert_1.default.strictEqual(parser.reader.buffer.byteLength, 0);
  528. });
  529. });
  530. //# sourceMappingURL=inbound-parser.test.js.map