UNPKG

js2ray

Version:

The v2ray vmess protocol, based on nodejs javascript which you can use on hosts and servers

133 lines (112 loc) 3.02 kB
"use strict"; const EventEmitter = require('events'); /** * Provide a mechanism for dealing with packet sticking and incomplete packet * when receiving data from a socket in a long connection over TCP. * * @glossary * * [0xff, 0x00, 0x04, 0xff, ...] = packet * | | * +--------chunk---------+ * * @options * getPacketLength (Function): how to interpret the bytes to a number * * @methods * .on('data', callback) * .put(chunk); * * @examples * const buffer = new AdvancedBuffer({ * getPacketLength: (bytes) => 0 // default * }); * * buffer.on('data', (all) => { * // all = [0, 2] * }); * * buffer.put(Buffer.from([0, 2])); * buffer.put(Buffer.from([0])) * buffer.put... */ class AdvancedBuffer extends EventEmitter { // native Buffer instance to store our data _buffer = Buffer.alloc(0); _getPacketLength = null; _nextLength = 0; constructor(options = {}) { super(); if (typeof options.getPacketLength !== 'function') { throw Error('options.getPacketLength should be a function'); } this._getPacketLength = options.getPacketLength; } /** * put incoming chunk to the buffer, then digest them * @param chunk{Buffer} * @param args */ put(chunk, ...args) { if (!(chunk instanceof Buffer)) { throw Error('chunk must be a Buffer'); } this._buffer = this._digest(Buffer.concat([this._buffer, chunk]), ...args); } /** * get the rest of data in the buffer * @returns {Buffer} */ final() { return this._buffer; } /** * clear staged buffer */ clear() { this._buffer = Buffer.alloc(0); } /** * digest a buffer, emit an event if a complete packet was resolved * @param buffer{Buffer}: a buffer to be digested * @param args * @returns {Buffer} */ _digest(buffer, ...args) { const retVal = this._nextLength || this._getPacketLength(buffer, ...args); // start from the new point if (retVal instanceof Buffer) { return this._digest(retVal, ...args); } // continue to put else if (retVal === 0 || retVal === undefined) { return buffer; } // drop this one else if (retVal < 0) { return Buffer.alloc(0); } // luckily: <- [chunk] if (buffer.length === retVal) { this.emit('data', buffer, ...args); this._nextLength = 0; return Buffer.alloc(0); } // incomplete packet: <- [chu] if (buffer.length < retVal) { // prevent redundant calling to getPacketLength() this._nextLength = retVal; // continue to put return buffer; } // packet sticking: <- [chunk][chunk][chu... if (buffer.length > retVal) { this.emit('data', buffer.subarray(0, retVal), ...args); // note that each chunk has probably different length this._nextLength = 0; // digest buffer recursively return this._digest(buffer.subarray(retVal), ...args); } } } module.exports = AdvancedBuffer