UNPKG

mysql2

Version:

fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS

195 lines (178 loc) 5.96 kB
var Buffer = require('safe-buffer').Buffer; var Packet = require('./packets/packet.js'); function PacketParser(onPacket, packetHeaderLength) { // 4 for normal packets, 7 for comprssed protocol packets if (typeof packetHeaderLength == 'undefined') { packetHeaderLength = 4; } // array of last payload chunks // only used when current payload is not complete this.buffer = []; // total length of chunks on buffer this.bufferLength = 0; this.packetHeaderLength = packetHeaderLength; // incomplete header state: number of header bytes received this.headerLen = 0; // expected payload length this.length = 0; this.largePacketParts = []; this.firstPacketSequenceId = 0; this.onPacket = onPacket; this.execute = PacketParser.prototype.executeStart; this._flushLargePacket = packetHeaderLength == 7 ? this._flushLargePacket7 : this._flushLargePacket4; } function readPacketLength(b, off) { var b0 = b[off]; var b1 = b[off + 1]; var b2 = b[off + 2]; if (b1 + b2 === 0) { return b0; } return b0 + (b1 << 8) + (b2 << 16); } var MAX_PACKET_LENGTH = 16777215; PacketParser.prototype._flushLargePacket4 = function _flushLargePacket() { var numPackets = this.largePacketParts.length; this.largePacketParts.unshift(Buffer.from([0, 0, 0, 0])); // insert header var body = Buffer.concat(this.largePacketParts); var packet = new Packet(this.firstPacketSequenceId, body, 0, body.length); this.largePacketParts.length = 0; packet.numPackets = numPackets; this.onPacket(packet); }; PacketParser.prototype._flushLargePacket7 = function _flushLargePacket() { var numPackets = this.largePacketParts.length; this.largePacketParts.unshift(Buffer.from([0, 0, 0, 0, 0, 0, 0])); // insert header var body = Buffer.concat(this.largePacketParts); this.largePacketParts.length = 0; var packet = new Packet(this.firstPacketSequenceId, body, 0, body.length); packet.numPackets = numPackets; this.onPacket(packet); }; PacketParser.prototype.executeStart = function executeStart(chunk) { var start = 0; var end = chunk.length; while (end - start >= 3) { this.length = readPacketLength(chunk, start); if (end - start >= this.length + this.packetHeaderLength) { // at least one full packet var sequenceId = chunk[start + 3]; if ( this.length < MAX_PACKET_LENGTH && this.largePacketParts.length === 0 ) { this.onPacket( new Packet( sequenceId, chunk, start, start + this.packetHeaderLength + this.length ) ); } else { // first large packet - remember it's id if (this.largePacketParts.length === 0) { this.firstPacketSequenceId = sequenceId; } this.largePacketParts.push( chunk.slice( start + this.packetHeaderLength, start + this.packetHeaderLength + this.length ) ); if (this.length < MAX_PACKET_LENGTH) { this._flushLargePacket(); } } start += this.packetHeaderLength + this.length; } else { // payload is incomplete this.buffer = [chunk.slice(start + 3, end)]; this.bufferLength = end - start - 3; this.execute = PacketParser.prototype.executePayload; return; } } if (end - start > 0) { // there is start of length header, but it's not full 3 bytes this.headerLen = end - start; // 1 or 2 bytes this.length = chunk[start]; if (this.headerLen == 2) { this.length = chunk[start] + (chunk[start + 1] << 8); this.execute = PacketParser.prototype.executeHeader3; } else { this.execute = PacketParser.prototype.executeHeader2; } } }; PacketParser.prototype.executePayload = function executePayload(chunk) { var start = 0; var end = chunk.length; var remainingPayload = this.length - this.bufferLength + this.packetHeaderLength - 3; if (end - start >= remainingPayload) { // last chunk for payload var payload = Buffer.allocUnsafe(this.length + this.packetHeaderLength); var offset = 3; for (var i = 0; i < this.buffer.length; ++i) { this.buffer[i].copy(payload, offset); offset += this.buffer[i].length; } chunk.copy(payload, offset, start, start + remainingPayload); var sequenceId = payload[3]; if (this.length < MAX_PACKET_LENGTH && this.largePacketParts.length === 0) { this.onPacket( new Packet( sequenceId, payload, 0, this.length + this.packetHeaderLength ) ); } else { // first large packet - remember it's id if (this.largePacketParts.length === 0) { this.firstPacketSequenceId = sequenceId; } this.largePacketParts.push( payload.slice( this.packetHeaderLength, this.packetHeaderLength + this.length ) ); if (this.length < MAX_PACKET_LENGTH) { this._flushLargePacket(); } } this.buffer = []; this.bufferLength = 0; this.execute = PacketParser.prototype.executeStart; start += remainingPayload; if (end - start > 0) { return this.execute(chunk.slice(start, end)); } } else { this.buffer.push(chunk); this.bufferLength += chunk.length; } return null; }; PacketParser.prototype.executeHeader2 = function executeHeader2(chunk) { this.length += chunk[0] << 8; if (chunk.length > 1) { this.length += chunk[1] << 16; this.execute = PacketParser.prototype.executePayload; return this.executePayload(chunk.slice(2)); } else { this.execute = PacketParser.prototype.executeHeader3; } return null; }; PacketParser.prototype.executeHeader3 = function executeHeader3(chunk) { this.length += chunk[0] << 16; this.execute = PacketParser.prototype.executePayload; return this.executePayload(chunk.slice(1)); }; module.exports = PacketParser;