123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 'use strict';
- const Packet = require('./packets/packet.js');
- const MAX_PACKET_LENGTH = 16777215;
- function readPacketLength(b, off) {
- const b0 = b[off];
- const b1 = b[off + 1];
- const b2 = b[off + 2];
- if (b1 + b2 === 0) {
- return b0;
- }
- return b0 + (b1 << 8) + (b2 << 16);
- }
- class PacketParser {
- constructor(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;
- }
- _flushLargePacket4() {
- const numPackets = this.largePacketParts.length;
- this.largePacketParts.unshift(Buffer.from([0, 0, 0, 0])); // insert header
- const body = Buffer.concat(this.largePacketParts);
- const packet = new Packet(this.firstPacketSequenceId, body, 0, body.length);
- this.largePacketParts.length = 0;
- packet.numPackets = numPackets;
- this.onPacket(packet);
- }
- _flushLargePacket7() {
- const numPackets = this.largePacketParts.length;
- this.largePacketParts.unshift(Buffer.from([0, 0, 0, 0, 0, 0, 0])); // insert header
- const body = Buffer.concat(this.largePacketParts);
- this.largePacketParts.length = 0;
- const packet = new Packet(this.firstPacketSequenceId, body, 0, body.length);
- packet.numPackets = numPackets;
- this.onPacket(packet);
- }
- executeStart(chunk) {
- let start = 0;
- const end = chunk.length;
- while (end - start >= 3) {
- this.length = readPacketLength(chunk, start);
- if (end - start >= this.length + this.packetHeaderLength) {
- // at least one full packet
- const 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;
- }
- }
- }
- executePayload(chunk) {
- let start = 0;
- const end = chunk.length;
- const remainingPayload =
- this.length - this.bufferLength + this.packetHeaderLength - 3;
- if (end - start >= remainingPayload) {
- // last chunk for payload
- const payload = Buffer.allocUnsafe(this.length + this.packetHeaderLength);
- let offset = 3;
- for (let 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);
- const 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;
- }
- 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));
- }
- this.execute = PacketParser.prototype.executeHeader3;
-
- return null;
- }
- executeHeader3(chunk) {
- this.length += chunk[0] << 16;
- this.execute = PacketParser.prototype.executePayload;
- return this.executePayload(chunk.slice(1));
- }
- }
- module.exports = PacketParser;
|