Parser.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. var PacketHeader = require('./PacketHeader');
  2. var BigNumber = require('bignumber.js');
  3. var Buffer = require('safe-buffer').Buffer;
  4. var BufferList = require('./BufferList');
  5. var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1;
  6. var MUL_32BIT = Math.pow(2, 32);
  7. var PACKET_HEADER_LENGTH = 4;
  8. module.exports = Parser;
  9. function Parser(options) {
  10. options = options || {};
  11. this._supportBigNumbers = options.config && options.config.supportBigNumbers;
  12. this._buffer = Buffer.alloc(0);
  13. this._nextBuffers = new BufferList();
  14. this._longPacketBuffers = new BufferList();
  15. this._offset = 0;
  16. this._packetEnd = null;
  17. this._packetHeader = null;
  18. this._packetOffset = null;
  19. this._onError = options.onError || function(err) { throw err; };
  20. this._onPacket = options.onPacket || function() {};
  21. this._nextPacketNumber = 0;
  22. this._encoding = 'utf-8';
  23. this._paused = false;
  24. }
  25. Parser.prototype.write = function write(chunk) {
  26. this._nextBuffers.push(chunk);
  27. while (!this._paused) {
  28. var packetHeader = this._tryReadPacketHeader();
  29. if (!packetHeader) {
  30. break;
  31. }
  32. if (!this._combineNextBuffers(packetHeader.length)) {
  33. break;
  34. }
  35. this._parsePacket(packetHeader);
  36. }
  37. };
  38. Parser.prototype.append = function append(chunk) {
  39. if (!chunk || chunk.length === 0) {
  40. return;
  41. }
  42. // Calculate slice ranges
  43. var sliceEnd = this._buffer.length;
  44. var sliceStart = this._packetOffset === null
  45. ? this._offset
  46. : this._packetOffset;
  47. var sliceLength = sliceEnd - sliceStart;
  48. // Get chunk data
  49. var buffer = null;
  50. var chunks = !(chunk instanceof Array || Array.isArray(chunk)) ? [chunk] : chunk;
  51. var length = 0;
  52. var offset = 0;
  53. for (var i = 0; i < chunks.length; i++) {
  54. length += chunks[i].length;
  55. }
  56. if (sliceLength !== 0) {
  57. // Create a new Buffer
  58. buffer = Buffer.allocUnsafe(sliceLength + length);
  59. offset = 0;
  60. // Copy data slice
  61. offset += this._buffer.copy(buffer, 0, sliceStart, sliceEnd);
  62. // Copy chunks
  63. for (var i = 0; i < chunks.length; i++) {
  64. offset += chunks[i].copy(buffer, offset);
  65. }
  66. } else if (chunks.length > 1) {
  67. // Create a new Buffer
  68. buffer = Buffer.allocUnsafe(length);
  69. offset = 0;
  70. // Copy chunks
  71. for (var i = 0; i < chunks.length; i++) {
  72. offset += chunks[i].copy(buffer, offset);
  73. }
  74. } else {
  75. // Buffer is the only chunk
  76. buffer = chunks[0];
  77. }
  78. // Adjust data-tracking pointers
  79. this._buffer = buffer;
  80. this._offset = this._offset - sliceStart;
  81. this._packetEnd = this._packetEnd !== null
  82. ? this._packetEnd - sliceStart
  83. : null;
  84. this._packetOffset = this._packetOffset !== null
  85. ? this._packetOffset - sliceStart
  86. : null;
  87. };
  88. Parser.prototype.pause = function() {
  89. this._paused = true;
  90. };
  91. Parser.prototype.resume = function() {
  92. this._paused = false;
  93. // nextTick() to avoid entering write() multiple times within the same stack
  94. // which would cause problems as write manipulates the state of the object.
  95. process.nextTick(this.write.bind(this));
  96. };
  97. Parser.prototype.peak = function peak(offset) {
  98. return this._buffer[this._offset + (offset >>> 0)];
  99. };
  100. Parser.prototype.parseUnsignedNumber = function parseUnsignedNumber(bytes) {
  101. if (bytes === 1) {
  102. return this._buffer[this._offset++];
  103. }
  104. var buffer = this._buffer;
  105. var offset = this._offset + bytes - 1;
  106. var value = 0;
  107. if (bytes > 4) {
  108. var err = new Error('parseUnsignedNumber: Supports only up to 4 bytes');
  109. err.offset = (this._offset - this._packetOffset - 1);
  110. err.code = 'PARSER_UNSIGNED_TOO_LONG';
  111. throw err;
  112. }
  113. while (offset >= this._offset) {
  114. value = ((value << 8) | buffer[offset]) >>> 0;
  115. offset--;
  116. }
  117. this._offset += bytes;
  118. return value;
  119. };
  120. Parser.prototype.parseLengthCodedString = function() {
  121. var length = this.parseLengthCodedNumber();
  122. if (length === null) {
  123. return null;
  124. }
  125. return this.parseString(length);
  126. };
  127. Parser.prototype.parseLengthCodedBuffer = function() {
  128. var length = this.parseLengthCodedNumber();
  129. if (length === null) {
  130. return null;
  131. }
  132. return this.parseBuffer(length);
  133. };
  134. Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {
  135. if (this._offset >= this._buffer.length) {
  136. var err = new Error('Parser: read past end');
  137. err.offset = (this._offset - this._packetOffset);
  138. err.code = 'PARSER_READ_PAST_END';
  139. throw err;
  140. }
  141. var bits = this._buffer[this._offset++];
  142. if (bits <= 250) {
  143. return bits;
  144. }
  145. switch (bits) {
  146. case 251:
  147. return null;
  148. case 252:
  149. return this.parseUnsignedNumber(2);
  150. case 253:
  151. return this.parseUnsignedNumber(3);
  152. case 254:
  153. break;
  154. default:
  155. var err = new Error('Unexpected first byte' + (bits ? ': 0x' + bits.toString(16) : ''));
  156. err.offset = (this._offset - this._packetOffset - 1);
  157. err.code = 'PARSER_BAD_LENGTH_BYTE';
  158. throw err;
  159. }
  160. var low = this.parseUnsignedNumber(4);
  161. var high = this.parseUnsignedNumber(4);
  162. var value;
  163. if (high >>> 21) {
  164. value = BigNumber(MUL_32BIT).times(high).plus(low).toString();
  165. if (this._supportBigNumbers) {
  166. return value;
  167. }
  168. var err = new Error(
  169. 'parseLengthCodedNumber: JS precision range exceeded, ' +
  170. 'number is >= 53 bit: "' + value + '"'
  171. );
  172. err.offset = (this._offset - this._packetOffset - 8);
  173. err.code = 'PARSER_JS_PRECISION_RANGE_EXCEEDED';
  174. throw err;
  175. }
  176. value = low + (MUL_32BIT * high);
  177. return value;
  178. };
  179. Parser.prototype.parseFiller = function(length) {
  180. return this.parseBuffer(length);
  181. };
  182. Parser.prototype.parseNullTerminatedBuffer = function() {
  183. var end = this._nullByteOffset();
  184. var value = this._buffer.slice(this._offset, end);
  185. this._offset = end + 1;
  186. return value;
  187. };
  188. Parser.prototype.parseNullTerminatedString = function() {
  189. var end = this._nullByteOffset();
  190. var value = this._buffer.toString(this._encoding, this._offset, end);
  191. this._offset = end + 1;
  192. return value;
  193. };
  194. Parser.prototype._nullByteOffset = function() {
  195. var offset = this._offset;
  196. while (this._buffer[offset] !== 0x00) {
  197. offset++;
  198. if (offset >= this._buffer.length) {
  199. var err = new Error('Offset of null terminated string not found.');
  200. err.offset = (this._offset - this._packetOffset);
  201. err.code = 'PARSER_MISSING_NULL_BYTE';
  202. throw err;
  203. }
  204. }
  205. return offset;
  206. };
  207. Parser.prototype.parsePacketTerminatedBuffer = function parsePacketTerminatedBuffer() {
  208. var length = this._packetEnd - this._offset;
  209. return this.parseBuffer(length);
  210. };
  211. Parser.prototype.parsePacketTerminatedString = function() {
  212. var length = this._packetEnd - this._offset;
  213. return this.parseString(length);
  214. };
  215. Parser.prototype.parseBuffer = function(length) {
  216. var response = Buffer.alloc(length);
  217. this._buffer.copy(response, 0, this._offset, this._offset + length);
  218. this._offset += length;
  219. return response;
  220. };
  221. Parser.prototype.parseString = function(length) {
  222. var offset = this._offset;
  223. var end = offset + length;
  224. var value = this._buffer.toString(this._encoding, offset, end);
  225. this._offset = end;
  226. return value;
  227. };
  228. Parser.prototype.parseGeometryValue = function() {
  229. var buffer = this.parseLengthCodedBuffer();
  230. var offset = 4;
  231. if (buffer === null || !buffer.length) {
  232. return null;
  233. }
  234. function parseGeometry() {
  235. var result = null;
  236. var byteOrder = buffer.readUInt8(offset); offset += 1;
  237. var wkbType = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  238. switch (wkbType) {
  239. case 1: // WKBPoint
  240. var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  241. var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  242. result = {x: x, y: y};
  243. break;
  244. case 2: // WKBLineString
  245. var numPoints = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  246. result = [];
  247. for (var i = numPoints; i > 0; i--) {
  248. var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  249. var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  250. result.push({x: x, y: y});
  251. }
  252. break;
  253. case 3: // WKBPolygon
  254. var numRings = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  255. result = [];
  256. for (var i = numRings; i > 0; i--) {
  257. var numPoints = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  258. var line = [];
  259. for (var j = numPoints; j > 0; j--) {
  260. var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  261. var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  262. line.push({x: x, y: y});
  263. }
  264. result.push(line);
  265. }
  266. break;
  267. case 4: // WKBMultiPoint
  268. case 5: // WKBMultiLineString
  269. case 6: // WKBMultiPolygon
  270. case 7: // WKBGeometryCollection
  271. var num = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  272. var result = [];
  273. for (var i = num; i > 0; i--) {
  274. result.push(parseGeometry());
  275. }
  276. break;
  277. }
  278. return result;
  279. }
  280. return parseGeometry();
  281. };
  282. Parser.prototype.reachedPacketEnd = function() {
  283. return this._offset === this._packetEnd;
  284. };
  285. Parser.prototype.incrementPacketNumber = function() {
  286. var currentPacketNumber = this._nextPacketNumber;
  287. this._nextPacketNumber = (this._nextPacketNumber + 1) % 256;
  288. return currentPacketNumber;
  289. };
  290. Parser.prototype.resetPacketNumber = function() {
  291. this._nextPacketNumber = 0;
  292. };
  293. Parser.prototype.packetLength = function packetLength() {
  294. if (!this._packetHeader) {
  295. return null;
  296. }
  297. return this._packetHeader.length + this._longPacketBuffers.size;
  298. };
  299. Parser.prototype._combineNextBuffers = function _combineNextBuffers(bytes) {
  300. var length = this._buffer.length - this._offset;
  301. if (length >= bytes) {
  302. return true;
  303. }
  304. if ((length + this._nextBuffers.size) < bytes) {
  305. return false;
  306. }
  307. var buffers = [];
  308. var bytesNeeded = bytes - length;
  309. while (bytesNeeded > 0) {
  310. var buffer = this._nextBuffers.shift();
  311. buffers.push(buffer);
  312. bytesNeeded -= buffer.length;
  313. }
  314. this.append(buffers);
  315. return true;
  316. };
  317. Parser.prototype._combineLongPacketBuffers = function _combineLongPacketBuffers() {
  318. if (!this._longPacketBuffers.size) {
  319. return;
  320. }
  321. // Calculate bytes
  322. var remainingBytes = this._buffer.length - this._offset;
  323. var trailingPacketBytes = this._buffer.length - this._packetEnd;
  324. // Create buffer
  325. var buf = null;
  326. var buffer = Buffer.allocUnsafe(remainingBytes + this._longPacketBuffers.size);
  327. var offset = 0;
  328. // Copy long buffers
  329. while ((buf = this._longPacketBuffers.shift())) {
  330. offset += buf.copy(buffer, offset);
  331. }
  332. // Copy remaining bytes
  333. this._buffer.copy(buffer, offset, this._offset);
  334. this._buffer = buffer;
  335. this._offset = 0;
  336. this._packetEnd = this._buffer.length - trailingPacketBytes;
  337. this._packetOffset = 0;
  338. };
  339. Parser.prototype._parsePacket = function _parsePacket(packetHeader) {
  340. this._packetEnd = this._offset + packetHeader.length;
  341. this._packetOffset = this._offset;
  342. if (packetHeader.length === MAX_PACKET_LENGTH) {
  343. this._longPacketBuffers.push(this._buffer.slice(this._packetOffset, this._packetEnd));
  344. this._advanceToNextPacket();
  345. return;
  346. }
  347. this._combineLongPacketBuffers();
  348. var hadException = true;
  349. try {
  350. this._onPacket(packetHeader);
  351. hadException = false;
  352. } catch (err) {
  353. if (!err || typeof err.code !== 'string' || err.code.substr(0, 7) !== 'PARSER_') {
  354. throw err; // Rethrow non-MySQL errors
  355. }
  356. // Pass down parser errors
  357. this._onError(err);
  358. hadException = false;
  359. } finally {
  360. this._advanceToNextPacket();
  361. // If there was an exception, the parser while loop will be broken out
  362. // of after the finally block. So schedule a blank write to re-enter it
  363. // to continue parsing any bytes that may already have been received.
  364. if (hadException) {
  365. process.nextTick(this.write.bind(this));
  366. }
  367. }
  368. };
  369. Parser.prototype._tryReadPacketHeader = function _tryReadPacketHeader() {
  370. if (this._packetHeader) {
  371. return this._packetHeader;
  372. }
  373. if (!this._combineNextBuffers(PACKET_HEADER_LENGTH)) {
  374. return null;
  375. }
  376. this._packetHeader = new PacketHeader(
  377. this.parseUnsignedNumber(3),
  378. this.parseUnsignedNumber(1)
  379. );
  380. if (this._packetHeader.number !== this._nextPacketNumber) {
  381. var err = new Error(
  382. 'Packets out of order. Got: ' + this._packetHeader.number + ' ' +
  383. 'Expected: ' + this._nextPacketNumber
  384. );
  385. err.code = 'PROTOCOL_PACKETS_OUT_OF_ORDER';
  386. err.fatal = true;
  387. this._onError(err);
  388. }
  389. this.incrementPacketNumber();
  390. return this._packetHeader;
  391. };
  392. Parser.prototype._advanceToNextPacket = function() {
  393. this._offset = this._packetEnd;
  394. this._packetHeader = null;
  395. this._packetEnd = null;
  396. this._packetOffset = null;
  397. };