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++;