inbound-parser.test.js 19 KB

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