inbound-parser.test.js 18 KB

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