Parser.js 13 KB

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