inbound-parser.test.js 19 KB

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