mirror of
https://github.com/sstent/node.git
synced 2026-01-27 15:41:43 +00:00
651 lines
19 KiB
JavaScript
651 lines
19 KiB
JavaScript
var util = require('util');
|
|
var Buffer = require('buffer').Buffer;
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var POWS = [1, 256, 65536, 16777216];
|
|
|
|
function Parser() {
|
|
EventEmitter.call(this);
|
|
|
|
this.state = Parser.PACKET_LENGTH;
|
|
this.packet = null;
|
|
this.greeted = false;
|
|
this.authenticated = false;
|
|
this.receivingFieldPackets = false;
|
|
this.receivingRowPackets = false;
|
|
|
|
this._lengthCodedLength = null;
|
|
this._lengthCodedStringLength = null;
|
|
};
|
|
util.inherits(Parser, EventEmitter);
|
|
module.exports = Parser;
|
|
|
|
Parser.prototype.write = function(buffer) {
|
|
var i = 0,
|
|
c = null,
|
|
self = this,
|
|
state = this.state,
|
|
length = buffer.length,
|
|
packet = this.packet,
|
|
advance = function(newState) {
|
|
self.state = state = (newState === undefined)
|
|
? self.state + 1
|
|
: newState;
|
|
|
|
packet.index = -1;
|
|
},
|
|
lengthCoded = function(val, nextState) {
|
|
if (self._lengthCodedLength === null) {
|
|
if (c === Parser.LENGTH_CODED_16BIT_WORD) {
|
|
self._lengthCodedLength = 2;
|
|
} else if (c === Parser.LENGTH_CODED_24BIT_WORD) {
|
|
self._lengthCodedLength = 3;
|
|
} else if (c === Parser.LENGTH_CODED_64BIT_WORD) {
|
|
self._lengthCodedLength = 8;
|
|
} else if (c === Parser.LENGTH_CODED_NULL) {
|
|
advance(nextState);
|
|
return null;
|
|
} else if (c < Parser.LENGTH_CODED_NULL) {
|
|
advance(nextState);
|
|
return c;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (c) {
|
|
val += POWS[packet.index - 1] * c;
|
|
}
|
|
|
|
if (packet.index === self._lengthCodedLength) {
|
|
self._lengthCodedLength = null;
|
|
advance(nextState);
|
|
}
|
|
|
|
return val;
|
|
},
|
|
emitPacket = function() {
|
|
self.packet = null;
|
|
self.state = state = Parser.PACKET_LENGTH;
|
|
self.greeted = true;
|
|
delete packet.index;
|
|
self.emit('packet', packet);
|
|
packet = null;
|
|
};
|
|
|
|
for (; i < length; i++) {
|
|
c = buffer[i];
|
|
|
|
if (state > Parser.PACKET_NUMBER) {
|
|
packet.received++;
|
|
}
|
|
|
|
switch (state) {
|
|
// PACKET HEADER
|
|
case 0: // PACKET_LENGTH:
|
|
if (!packet) {
|
|
packet = this.packet = new EventEmitter();
|
|
packet.index = 0;
|
|
packet.length = 0;
|
|
packet.received = 0;
|
|
packet.number = 0;
|
|
}
|
|
|
|
// 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(Parser.GREETING_PROTOCOL_VERSION);
|
|
break;
|
|
}
|
|
|
|
if (this.receivingFieldPackets) {
|
|
advance(Parser.FIELD_CATALOG_LENGTH);
|
|
} else if (this.receivingRowPackets) {
|
|
advance(Parser.COLUMN_VALUE_LENGTH);
|
|
} else {
|
|
advance(Parser.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 = Parser.ERROR_PACKET;
|
|
advance(Parser.ERROR_NUMBER);
|
|
break;
|
|
}
|
|
|
|
// 1 byte
|
|
packet.type = Parser.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 = new Buffer(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 = Parser.ERROR_PACKET;
|
|
advance(Parser.ERROR_NUMBER);
|
|
break;
|
|
}
|
|
|
|
if (c == 0xfe && !this.authenticated) {
|
|
packet.type = Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET;
|
|
break;
|
|
}
|
|
|
|
if (c === 0x00) {
|
|
// after the first OK PACKET, we are authenticated
|
|
this.authenticated = true;
|
|
packet.type = Parser.OK_PACKET;
|
|
advance(Parser.AFFECTED_ROWS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.receivingFieldPackets = true;
|
|
packet.type = Parser.RESULT_SET_HEADER_PACKET;
|
|
packet.fieldCount = lengthCoded(packet.fieldCount, Parser.EXTRA_LENGTH);
|
|
|
|
break;
|
|
|
|
// ERROR_PACKET
|
|
case 13: // ERROR_NUMBER:
|
|
if (packet.index == 0) {
|
|
packet.errorNumber = 0;
|
|
}
|
|
|
|
// 2 bytes = Little endian
|
|
packet.errorNumber += 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(Parser.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(Parser.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 = Parser.EOF_PACKET;
|
|
advance(Parser.EOF_WARNING_COUNT);
|
|
break;
|
|
}
|
|
packet.type = Parser.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 = Parser.ROW_DATA_PACKET;
|
|
}
|
|
|
|
if (packet.received == 1) {
|
|
if (c === 0xfe) {
|
|
packet.type = Parser.EOF_PACKET;
|
|
this.receivingRowPackets = false;
|
|
advance(Parser.EOF_WARNING_COUNT);
|
|
break;
|
|
}
|
|
this.emit('packet', packet);
|
|
}
|
|
|
|
packet.columnLength = lengthCoded(packet.columnLength);
|
|
|
|
if (!packet.columnLength && !this._lengthCodedLength) {
|
|
packet.emit('data', (packet.columnLength === null ? null : new Buffer(0)), 0);
|
|
if (packet.received < packet.length) {
|
|
advance(Parser.COLUMN_VALUE_LENGTH);
|
|
} else {
|
|
self.packet = packet = null;
|
|
self.state = state = Parser.PACKET_LENGTH;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
case 47: // COLUMN_VALUE_STRING:
|
|
var remaining = packet.columnLength - packet.index, read;
|
|
if (i + remaining > buffer.length) {
|
|
read = buffer.length - i;
|
|
packet.index += read;
|
|
packet.emit('data', buffer.slice(i, buffer.length), 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.emit('data', buffer.slice(i, i + remaining), 0);
|
|
i += remaining - 1;
|
|
packet.received += remaining - 1;
|
|
advance(Parser.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 = packet = null;
|
|
self.state = state = Parser.PACKET_LENGTH;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
packet.index++;
|
|
|
|
if (state > Parser.PACKET_NUMBER && packet.received === packet.length) {
|
|
emitPacket();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
Parser.LENGTH_CODED_NULL = 251;
|
|
Parser.LENGTH_CODED_16BIT_WORD= 252;
|
|
Parser.LENGTH_CODED_24BIT_WORD= 253;
|
|
Parser.LENGTH_CODED_64BIT_WORD= 254;
|
|
|
|
// Parser states
|
|
var s = 0;
|
|
Parser.PACKET_LENGTH = s++;
|
|
Parser.PACKET_NUMBER = s++;
|
|
Parser.GREETING_PROTOCOL_VERSION = s++;
|
|
Parser.GREETING_SERVER_VERSION = s++;
|
|
Parser.GREETING_THREAD_ID = s++;
|
|
Parser.GREETING_SCRAMBLE_BUFF_1 = s++;
|
|
Parser.GREETING_FILLER_1 = s++;
|
|
Parser.GREETING_SERVER_CAPABILITIES = s++;
|
|
Parser.GREETING_SERVER_LANGUAGE = s++;
|
|
Parser.GREETING_SERVER_STATUS = s++;
|
|
Parser.GREETING_FILLER_2 = s++;
|
|
Parser.GREETING_SCRAMBLE_BUFF_2 = s++;
|
|
Parser.FIELD_COUNT = s++;
|
|
Parser.ERROR_NUMBER = s++;
|
|
Parser.ERROR_SQL_STATE_MARKER = s++;
|
|
Parser.ERROR_SQL_STATE = s++;
|
|
Parser.ERROR_MESSAGE = s++;
|
|
Parser.AFFECTED_ROWS = s++;
|
|
Parser.INSERT_ID = s++;
|
|
Parser.SERVER_STATUS = s++;
|
|
Parser.WARNING_COUNT = s++;
|
|
Parser.MESSAGE = s++;
|
|
Parser.EXTRA_LENGTH = s++;
|
|
Parser.EXTRA_STRING = s++;
|
|
Parser.FIELD_CATALOG_LENGTH = s++;
|
|
Parser.FIELD_CATALOG_STRING = s++;
|
|
Parser.FIELD_DB_LENGTH = s++;
|
|
Parser.FIELD_DB_STRING = s++;
|
|
Parser.FIELD_TABLE_LENGTH = s++;
|
|
Parser.FIELD_TABLE_STRING = s++;
|
|
Parser.FIELD_ORIGINAL_TABLE_LENGTH = s++;
|
|
Parser.FIELD_ORIGINAL_TABLE_STRING = s++;
|
|
Parser.FIELD_NAME_LENGTH = s++;
|
|
Parser.FIELD_NAME_STRING = s++;
|
|
Parser.FIELD_ORIGINAL_NAME_LENGTH = s++;
|
|
Parser.FIELD_ORIGINAL_NAME_STRING = s++;
|
|
Parser.FIELD_FILLER_1 = s++;
|
|
Parser.FIELD_CHARSET_NR = s++;
|
|
Parser.FIELD_LENGTH = s++;
|
|
Parser.FIELD_TYPE = s++;
|
|
Parser.FIELD_FLAGS = s++;
|
|
Parser.FIELD_DECIMALS = s++;
|
|
Parser.FIELD_FILLER_2 = s++;
|
|
Parser.FIELD_DEFAULT = s++;
|
|
Parser.EOF_WARNING_COUNT = s++;
|
|
Parser.EOF_SERVER_STATUS = s++;
|
|
Parser.COLUMN_VALUE_LENGTH = s++;
|
|
Parser.COLUMN_VALUE_STRING = s++;
|
|
|
|
// Packet types
|
|
var p = 0;
|
|
Parser.GREETING_PACKET = p++;
|
|
Parser.OK_PACKET = p++;
|
|
Parser.ERROR_PACKET = p++;
|
|
Parser.RESULT_SET_HEADER_PACKET = p++;
|
|
Parser.FIELD_PACKET = p++;
|
|
Parser.EOF_PACKET = p++;
|
|
Parser.ROW_DATA_PACKET = p++;
|
|
Parser.ROW_DATA_BINARY_PACKET = p++;
|
|
Parser.OK_FOR_PREPARED_STATEMENT_PACKET = p++;
|
|
Parser.PARAMETER_PACKET = p++;
|
|
Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET = p++;
|