nxkit
Version:
This is a collection of tools, independent of any other libraries
654 lines (653 loc) • 29.9 kB
JavaScript
"use strict";
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2015, xuewen.chu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of xuewen.chu nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL xuewen.chu BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
Object.defineProperty(exports, "__esModule", { value: true });
const event_1 = require("../event");
const POWS = [1, 256, 65536, 16777216];
var Constants;
(function (Constants) {
Constants[Constants["LENGTH_CODED_NULL"] = 251] = "LENGTH_CODED_NULL";
Constants[Constants["LENGTH_CODED_16BIT_WORD"] = 252] = "LENGTH_CODED_16BIT_WORD";
Constants[Constants["LENGTH_CODED_24BIT_WORD"] = 253] = "LENGTH_CODED_24BIT_WORD";
Constants[Constants["LENGTH_CODED_64BIT_WORD"] = 254] = "LENGTH_CODED_64BIT_WORD";
// Parser states
Constants[Constants["PACKET_LENGTH"] = 0] = "PACKET_LENGTH";
Constants[Constants["PACKET_NUMBER"] = 1] = "PACKET_NUMBER";
Constants[Constants["GREETING_PROTOCOL_VERSION"] = 2] = "GREETING_PROTOCOL_VERSION";
Constants[Constants["GREETING_SERVER_VERSION"] = 3] = "GREETING_SERVER_VERSION";
Constants[Constants["GREETING_THREAD_ID"] = 4] = "GREETING_THREAD_ID";
Constants[Constants["GREETING_SCRAMBLE_BUFF_1"] = 5] = "GREETING_SCRAMBLE_BUFF_1";
Constants[Constants["GREETING_FILLER_1"] = 6] = "GREETING_FILLER_1";
Constants[Constants["GREETING_SERVER_CAPABILITIES"] = 7] = "GREETING_SERVER_CAPABILITIES";
Constants[Constants["GREETING_SERVER_LANGUAGE"] = 8] = "GREETING_SERVER_LANGUAGE";
Constants[Constants["GREETING_SERVER_STATUS"] = 9] = "GREETING_SERVER_STATUS";
Constants[Constants["GREETING_FILLER_2"] = 10] = "GREETING_FILLER_2";
Constants[Constants["GREETING_SCRAMBLE_BUFF_2"] = 11] = "GREETING_SCRAMBLE_BUFF_2";
Constants[Constants["FIELD_COUNT"] = 12] = "FIELD_COUNT";
Constants[Constants["ERROR_NUMBER"] = 13] = "ERROR_NUMBER";
Constants[Constants["ERROR_SQL_STATE_MARKER"] = 14] = "ERROR_SQL_STATE_MARKER";
Constants[Constants["ERROR_SQL_STATE"] = 15] = "ERROR_SQL_STATE";
Constants[Constants["ERROR_MESSAGE"] = 16] = "ERROR_MESSAGE";
Constants[Constants["AFFECTED_ROWS"] = 17] = "AFFECTED_ROWS";
Constants[Constants["INSERT_ID"] = 18] = "INSERT_ID";
Constants[Constants["SERVER_STATUS"] = 19] = "SERVER_STATUS";
Constants[Constants["WARNING_COUNT"] = 20] = "WARNING_COUNT";
Constants[Constants["MESSAGE"] = 21] = "MESSAGE";
Constants[Constants["EXTRA_LENGTH"] = 22] = "EXTRA_LENGTH";
Constants[Constants["EXTRA_STRING"] = 23] = "EXTRA_STRING";
Constants[Constants["FIELD_CATALOG_LENGTH"] = 24] = "FIELD_CATALOG_LENGTH";
Constants[Constants["FIELD_CATALOG_STRING"] = 25] = "FIELD_CATALOG_STRING";
Constants[Constants["FIELD_DB_LENGTH"] = 26] = "FIELD_DB_LENGTH";
Constants[Constants["FIELD_DB_STRING"] = 27] = "FIELD_DB_STRING";
Constants[Constants["FIELD_TABLE_LENGTH"] = 28] = "FIELD_TABLE_LENGTH";
Constants[Constants["FIELD_TABLE_STRING"] = 29] = "FIELD_TABLE_STRING";
Constants[Constants["FIELD_ORIGINAL_TABLE_LENGTH"] = 30] = "FIELD_ORIGINAL_TABLE_LENGTH";
Constants[Constants["FIELD_ORIGINAL_TABLE_STRING"] = 31] = "FIELD_ORIGINAL_TABLE_STRING";
Constants[Constants["FIELD_NAME_LENGTH"] = 32] = "FIELD_NAME_LENGTH";
Constants[Constants["FIELD_NAME_STRING"] = 33] = "FIELD_NAME_STRING";
Constants[Constants["FIELD_ORIGINAL_NAME_LENGTH"] = 34] = "FIELD_ORIGINAL_NAME_LENGTH";
Constants[Constants["FIELD_ORIGINAL_NAME_STRING"] = 35] = "FIELD_ORIGINAL_NAME_STRING";
Constants[Constants["FIELD_FILLER_1"] = 36] = "FIELD_FILLER_1";
Constants[Constants["FIELD_CHARSET_NR"] = 37] = "FIELD_CHARSET_NR";
Constants[Constants["FIELD_LENGTH"] = 38] = "FIELD_LENGTH";
Constants[Constants["FIELD_TYPE"] = 39] = "FIELD_TYPE";
Constants[Constants["FIELD_FLAGS"] = 40] = "FIELD_FLAGS";
Constants[Constants["FIELD_DECIMALS"] = 41] = "FIELD_DECIMALS";
Constants[Constants["FIELD_FILLER_2"] = 42] = "FIELD_FILLER_2";
Constants[Constants["FIELD_DEFAULT"] = 43] = "FIELD_DEFAULT";
Constants[Constants["EOF_WARNING_COUNT"] = 44] = "EOF_WARNING_COUNT";
Constants[Constants["EOF_SERVER_STATUS"] = 45] = "EOF_SERVER_STATUS";
Constants[Constants["COLUMN_VALUE_LENGTH"] = 46] = "COLUMN_VALUE_LENGTH";
Constants[Constants["COLUMN_VALUE_STRING"] = 47] = "COLUMN_VALUE_STRING";
// Packet types
Constants[Constants["GREETING_PACKET"] = 0] = "GREETING_PACKET";
Constants[Constants["OK_PACKET"] = 1] = "OK_PACKET";
Constants[Constants["ERROR_PACKET"] = 2] = "ERROR_PACKET";
Constants[Constants["RESULT_SET_HEADER_PACKET"] = 3] = "RESULT_SET_HEADER_PACKET";
Constants[Constants["FIELD_PACKET"] = 4] = "FIELD_PACKET";
Constants[Constants["EOF_PACKET"] = 5] = "EOF_PACKET";
Constants[Constants["ROW_DATA_PACKET"] = 6] = "ROW_DATA_PACKET";
Constants[Constants["ROW_DATA_BINARY_PACKET"] = 7] = "ROW_DATA_BINARY_PACKET";
Constants[Constants["OK_FOR_PREPARED_STATEMENT_PACKET"] = 8] = "OK_FOR_PREPARED_STATEMENT_PACKET";
Constants[Constants["PARAMETER_PACKET"] = 9] = "PARAMETER_PACKET";
Constants[Constants["USE_OLD_PASSWORD_PROTOCOL_PACKET"] = 10] = "USE_OLD_PASSWORD_PROTOCOL_PACKET";
})(Constants = exports.Constants || (exports.Constants = {}));
;
class Packet {
constructor() {
this.onData = new event_1.EventNoticer('Data', this);
this.data = {};
this.type = Constants.LENGTH_CODED_NULL;
this.number = 0;
this.index = 0;
this.length = 0;
this.received = 0;
}
toJSON() {
var data = this.data;
if (this.type == Constants.ERROR_PACKET) {
var err = Error.new([data.errno, data.errorMessage]);
for (var [key, val] of Object.entries(data))
err[key] = val;
return err;
}
return data;
}
}
exports.Packet = Packet;
class Parser {
constructor() {
this.packet = null;
this.greeted = false;
this.authenticated = false;
this.receivingFieldPackets = false;
this.receivingRowPackets = false;
this.state = Constants.PACKET_LENGTH;
/**
* @event onpacket
*/
this.onPacket = new event_1.EventNoticer('Packet', this);
}
/**
* write buffer and parser
* @param {node.Buffer}
*/
write(buffer) {
var c = 0;
var self = this;
var state = this.state;
var length = buffer.length;
var packet = this.packet;
var packet_ = {};
function advance(newState) {
self.state = state = (newState === undefined)
? self.state + 1
: newState;
packet.index = -1;
}
function lengthCoded(val, nextState) {
if (self._lengthCodedLength === undefined) {
if (c === Constants.LENGTH_CODED_16BIT_WORD) {
self._lengthCodedLength = 2;
}
else if (c === Constants.LENGTH_CODED_24BIT_WORD) {
self._lengthCodedLength = 3;
}
else if (c === Constants.LENGTH_CODED_64BIT_WORD) {
self._lengthCodedLength = 8;
}
else if (c === Constants.LENGTH_CODED_NULL) {
advance(nextState);
return; // null;
}
else if (c < Constants.LENGTH_CODED_NULL) {
advance(nextState);
return c;
}
return 0;
}
if (c) {
if (val === undefined)
throw new Error('Type error');
val += POWS[packet.index - 1] * c;
}
if (packet.index === self._lengthCodedLength) {
self._lengthCodedLength = undefined;
advance(nextState);
}
return val;
}
function emitPacket() {
if (packet) {
self.packet = null;
self.state = state = Constants.PACKET_LENGTH;
self.greeted = true;
delete packet.index;
self.onPacket.trigger(packet);
packet = null;
}
}
for (var i = 0; i < length; i++) {
c = buffer[i];
if (state > Constants.PACKET_NUMBER) {
packet.received++;
}
switch (state) {
// PACKET HEADER
case 0: // PACKET_LENGTH:
if (!packet) {
packet = this.packet = new Packet();
packet_ = packet.data;
}
// 3 bytes - Little endian
packet.length += POWS[packet.index] * c;
if (packet.index == 2) {
advance();
}
break;
case 1: // PACKET_NUMBER:
// 1 byte
packet.number = c;
if (!this.greeted) {
advance(Constants.GREETING_PROTOCOL_VERSION);
break;
}
if (this.receivingFieldPackets) {
advance(Constants.FIELD_CATALOG_LENGTH);
}
else if (this.receivingRowPackets) {
advance(Constants.COLUMN_VALUE_LENGTH);
}
else {
advance(Constants.FIELD_COUNT);
}
break;
// GREETING_PACKET
case 2: // GREETING_PROTOCOL_VERSION:
// Nice undocumented MySql gem, the initial greeting can be an error
// packet. Happens for too many connections errors.
if (c === 0xff) {
packet.type = Constants.ERROR_PACKET;
advance(Constants.ERROR_NUMBER);
break;
}
// 1 byte
packet.type = Constants.GREETING_PACKET;
packet_.protocolVersion = c;
advance();
break;
case 3: // GREETING_SERVER_VERSION:
if (packet.index == 0) {
packet_.serverVersion = '';
}
// Null-Terminated String
if (c != 0) {
packet_.serverVersion += String.fromCharCode(c);
}
else {
advance();
}
break;
case 4: // GREETING_THREAD_ID:
if (packet.index == 0) {
packet_.threadId = 0;
}
// 4 bytes = probably Little endian, protocol docs are not clear
packet_.threadId += POWS[packet.index] * c;
if (packet.index == 3) {
advance();
}
break;
case 5: // GREETING_SCRAMBLE_BUFF_1:
if (packet.index == 0) {
packet_.scrambleBuffer = Buffer.alloc(8 + 12);
}
// 8 bytes
packet_.scrambleBuffer[packet.index] = c;
if (packet.index == 7) {
advance();
}
break;
case 6: // GREETING_FILLER_1:
// 1 byte - 0x00
advance();
break;
case 7: // GREETING_SERVER_CAPABILITIES:
if (packet.index == 0) {
packet_.serverCapabilities = 0;
}
// 2 bytes = probably Little endian, protocol docs are not clear
packet_.serverCapabilities += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 8: // GREETING_SERVER_LANGUAGE:
packet_.serverLanguage = c;
advance();
break;
case 9: // GREETING_SERVER_STATUS:
if (packet.index == 0) {
packet_.serverStatus = 0;
}
// 2 bytes = probably Little endian, protocol docs are not clear
packet_.serverStatus += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 10: // GREETING_FILLER_2:
// 13 bytes - 0x00
if (packet.index == 12) {
advance();
}
break;
case 11: // GREETING_SCRAMBLE_BUFF_2:
// 12 bytes - not 13 bytes like the protocol spec says ...
if (packet.index < 12) {
packet_.scrambleBuffer[packet.index + 8] = c;
}
break;
// OK_PACKET, ERROR_PACKET, or RESULT_SET_HEADER_PACKET
case 12: // FIELD_COUNT:
if (packet.index == 0) {
if (c === 0xff) {
packet.type = Constants.ERROR_PACKET;
advance(Constants.ERROR_NUMBER);
break;
}
if (c == 0xfe && !this.authenticated) {
packet.type = Constants.USE_OLD_PASSWORD_PROTOCOL_PACKET;
break;
}
if (c === 0x00) {
// after the first OK PACKET, we are authenticated
this.authenticated = true;
packet.type = Constants.OK_PACKET;
advance(Constants.AFFECTED_ROWS);
break;
}
}
this.receivingFieldPackets = true;
packet.type = Constants.RESULT_SET_HEADER_PACKET;
packet_.fieldCount = lengthCoded(packet_.fieldCount, Constants.EXTRA_LENGTH);
break;
// ERROR_PACKET
case 13: // ERROR_NUMBER:
if (packet.index == 0) {
packet_.errno = 0;
}
// 2 bytes = Little endian
packet_.errno += POWS[packet.index] * c;
if (packet.index == 1) {
if (!this.greeted) {
// Turns out error packets are confirming to the 4.0 protocol when
// not greeted yet. Oh MySql, you are such a thing of beauty ...
advance(Constants.ERROR_MESSAGE);
break;
}
advance();
}
break;
case 14: // ERROR_SQL_STATE_MARKER:
// 1 character - always #
packet_.sqlStateMarker = String.fromCharCode(c);
packet_.sqlState = '';
advance();
break;
case 15: // ERROR_SQL_STATE:
// 5 characters
if (packet.index < 5) {
packet_.sqlState += String.fromCharCode(c);
}
if (packet.index == 4) {
advance(Constants.ERROR_MESSAGE);
}
break;
case 16: // ERROR_MESSAGE:
if (packet.received <= packet.length) {
packet_.errorMessage = (packet_.errorMessage || '') + String.fromCharCode(c);
}
break;
// OK_PACKET
case 17: // AFFECTED_ROWS:
packet_.affectedRows = lengthCoded(packet_.affectedRows);
break;
case 18: // INSERT_ID:
packet_.insertId = lengthCoded(packet_.insertId);
break;
case 19: // SERVER_STATUS:
if (packet.index == 0) {
packet_.serverStatus = 0;
}
// 2 bytes - Little endian
packet_.serverStatus += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 20: // WARNING_COUNT:
if (packet.index == 0) {
packet_.warningCount = 0;
}
// 2 bytes - Little endian
packet_.warningCount += POWS[packet.index] * c;
if (packet.index == 1) {
packet_.message = '';
advance();
}
break;
case 21: // MESSAGE:
if (packet.received <= packet.length) {
packet_.message += String.fromCharCode(c);
}
break;
// RESULT_SET_HEADER_PACKET
case 22: // EXTRA_LENGTH:
packet_.extra = '';
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 23: // EXTRA_STRING:
packet_.extra += String.fromCharCode(c);
break;
// FIELD_PACKET or EOF_PACKET
case 24: // FIELD_CATALOG_LENGTH:
if (packet.index == 0) {
if (c === 0xfe) {
packet.type = Constants.EOF_PACKET;
advance(Constants.EOF_WARNING_COUNT);
break;
}
packet.type = Constants.FIELD_PACKET;
}
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 25: // FIELD_CATALOG_STRING:
if (packet.index == 0) {
packet_.catalog = '';
}
packet_.catalog += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 26: // FIELD_DB_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 27: // FIELD_DB_STRING:
if (packet.index == 0) {
packet_.db = '';
}
packet_.db += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 28: // FIELD_TABLE_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 29: // FIELD_TABLE_STRING:
if (packet.index == 0) {
packet_.table = '';
}
packet_.table += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 30: // FIELD_ORIGINAL_TABLE_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 31: // FIELD_ORIGINAL_TABLE_STRING:
if (packet.index == 0) {
packet_.originalTable = '';
}
packet_.originalTable += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 32: // FIELD_NAME_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 33: // FIELD_NAME_STRING:
if (packet.index == 0) {
packet_.name = '';
}
packet_.name += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 34: // FIELD_ORIGINAL_NAME_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 35: // FIELD_ORIGINAL_NAME_STRING:
if (packet.index == 0) {
packet_.originalName = '';
}
packet_.originalName += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 36: // FIELD_FILLER_1:
// 1 bytes - 0x00
advance();
break;
case 37: // FIELD_CHARSET_NR:
if (packet.index == 0) {
packet_.charsetNumber = 0;
}
// 2 bytes - Little endian
packet_.charsetNumber += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 38: // FIELD_LENGTH:
if (packet.index == 0) {
packet_.fieldLength = 0;
}
// 4 bytes - Little endian
packet_.fieldLength += Math.pow(256, packet.index) * c;
if (packet.index == 3) {
advance();
}
break;
case 39: // FIELD_TYPE:
// 1 byte
packet_.fieldType = c;
advance();
case 40: // FIELD_FLAGS:
if (packet.index == 0) {
packet_.flags = 0;
}
// 2 bytes - Little endian
packet_.flags += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 41: // FIELD_DECIMALS:
// 1 byte
packet_.decimals = c;
advance();
break;
case 42: // FIELD_FILLER_2:
// 2 bytes - 0x00
if (packet.index == 1) {
advance();
}
break;
case 43: // FIELD_DEFAULT:
// TODO: Only occurs for mysql_list_fields()
break;
// EOF_PACKET
case 44: // EOF_WARNING_COUNT:
if (packet.index == 0) {
packet_.warningCount = 0;
}
// 2 bytes - Little endian
packet_.warningCount += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 45: // EOF_SERVER_STATUS:
if (packet.index == 0) {
packet_.serverStatus = 0;
}
// 2 bytes - Little endian
packet_.serverStatus += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
if (this.receivingFieldPackets) {
this.receivingFieldPackets = false;
this.receivingRowPackets = true;
}
else {
}
}
break;
case 46: // COLUMN_VALUE_LENGTH:
if (packet.index == 0) {
packet_.columnLength = 0;
packet.type = Constants.ROW_DATA_PACKET;
}
if (packet.received == 1) {
if (c === 0xfe) {
packet.type = Constants.EOF_PACKET;
this.receivingRowPackets = false;
advance(Constants.EOF_WARNING_COUNT);
break;
}
this.onPacket.trigger(packet);
}
packet_.columnLength = lengthCoded(packet_.columnLength);
if (!packet_.columnLength && !this._lengthCodedLength) {
packet.onData.trigger({ buffer: packet_.columnLength === undefined ? null : Buffer.alloc(0), remaining: 0 });
if (packet.received < packet.length) {
advance(Constants.COLUMN_VALUE_LENGTH);
}
else {
self.packet = null;
packet = null;
self.state = state = Constants.PACKET_LENGTH;
continue;
}
}
break;
case 47: // COLUMN_VALUE_STRING:
if (packet_.columnLength === undefined)
throw new Error('Type error');
var remaining = packet_.columnLength - packet.index, read;
if (i + remaining > buffer.length) {
read = buffer.length - i;
packet.index += read;
packet.onData.trigger({ buffer: buffer.slice(i, buffer.length), remaining: remaining - read });
// the -1 offsets are because these values are also manipulated by the loop itself
packet.received += read - 1;
i = buffer.length;
}
else {
packet.onData.trigger({ buffer: buffer.slice(i, i + remaining), remaining: 0 });
i += remaining - 1;
packet.received += remaining - 1;
advance(Constants.COLUMN_VALUE_LENGTH);
// advance() sets this to -1, but packet.index++ is skipped, so we need to manually fix
packet.index = 0;
}
if (packet.received == packet.length) {
self.packet = null;
packet = null;
self.state = state = Constants.PACKET_LENGTH;
}
continue;
}
packet.index++;
if (state > Constants.PACKET_NUMBER && packet.received === packet.length) {
emitPacket();
}
}
}
}
exports.Parser = Parser;