|
"use strict"; |
|
|
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.decodePayloadAsBinary = exports.encodePayloadAsBinary = exports.decodePayload = exports.encodePayload = exports.decodeBase64Packet = exports.decodePacket = exports.encodeBase64Packet = exports.encodePacket = exports.packets = exports.protocol = void 0; |
|
|
|
|
|
|
|
var utf8 = require('./utf8'); |
|
|
|
|
|
|
|
exports.protocol = 3; |
|
const hasBinary = (packets) => { |
|
for (const packet of packets) { |
|
if (packet.data instanceof ArrayBuffer || ArrayBuffer.isView(packet.data)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}; |
|
|
|
|
|
|
|
exports.packets = { |
|
open: 0 |
|
, |
|
close: 1 |
|
, |
|
ping: 2, |
|
pong: 3, |
|
message: 4, |
|
upgrade: 5, |
|
noop: 6 |
|
}; |
|
var packetslist = Object.keys(exports.packets); |
|
|
|
|
|
|
|
var err = { type: 'error', data: 'parser error' }; |
|
const EMPTY_BUFFER = Buffer.concat([]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function encodePacket(packet, supportsBinary, utf8encode, callback) { |
|
if (typeof supportsBinary === 'function') { |
|
callback = supportsBinary; |
|
supportsBinary = null; |
|
} |
|
if (typeof utf8encode === 'function') { |
|
callback = utf8encode; |
|
utf8encode = null; |
|
} |
|
if (Buffer.isBuffer(packet.data)) { |
|
return encodeBuffer(packet, supportsBinary, callback); |
|
} |
|
else if (packet.data && (packet.data.buffer || packet.data) instanceof ArrayBuffer) { |
|
return encodeBuffer({ type: packet.type, data: arrayBufferToBuffer(packet.data) }, supportsBinary, callback); |
|
} |
|
|
|
var encoded = exports.packets[packet.type]; |
|
|
|
if (undefined !== packet.data) { |
|
encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data); |
|
} |
|
return callback('' + encoded); |
|
} |
|
exports.encodePacket = encodePacket; |
|
; |
|
|
|
|
|
|
|
function encodeBuffer(packet, supportsBinary, callback) { |
|
if (!supportsBinary) { |
|
return encodeBase64Packet(packet, callback); |
|
} |
|
var data = packet.data; |
|
var typeBuffer = Buffer.allocUnsafe(1); |
|
typeBuffer[0] = exports.packets[packet.type]; |
|
return callback(Buffer.concat([typeBuffer, data])); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function encodeBase64Packet(packet, callback) { |
|
var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data); |
|
var message = 'b' + exports.packets[packet.type]; |
|
message += data.toString('base64'); |
|
return callback(message); |
|
} |
|
exports.encodeBase64Packet = encodeBase64Packet; |
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function decodePacket(data, binaryType, utf8decode) { |
|
if (data === undefined) { |
|
return err; |
|
} |
|
var type; |
|
|
|
if (typeof data === 'string') { |
|
type = data.charAt(0); |
|
if (type === 'b') { |
|
return decodeBase64Packet(data.slice(1), binaryType); |
|
} |
|
if (utf8decode) { |
|
data = tryDecode(data); |
|
if (data === false) { |
|
return err; |
|
} |
|
} |
|
if (Number(type) != type || !packetslist[type]) { |
|
return err; |
|
} |
|
if (data.length > 1) { |
|
return { type: packetslist[type], data: data.slice(1) }; |
|
} |
|
else { |
|
return { type: packetslist[type] }; |
|
} |
|
} |
|
|
|
if (binaryType === 'arraybuffer') { |
|
|
|
var intArray = new Uint8Array(data); |
|
type = intArray[0]; |
|
return { type: packetslist[type], data: intArray.buffer.slice(1) }; |
|
} |
|
if (data instanceof ArrayBuffer) { |
|
data = arrayBufferToBuffer(data); |
|
} |
|
type = data[0]; |
|
return { type: packetslist[type], data: data.slice(1) }; |
|
} |
|
exports.decodePacket = decodePacket; |
|
; |
|
function tryDecode(data) { |
|
try { |
|
data = utf8.decode(data, { strict: false }); |
|
} |
|
catch (e) { |
|
return false; |
|
} |
|
return data; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function decodeBase64Packet(msg, binaryType) { |
|
var type = packetslist[msg.charAt(0)]; |
|
var data = Buffer.from(msg.slice(1), 'base64'); |
|
if (binaryType === 'arraybuffer') { |
|
var abv = new Uint8Array(data.length); |
|
for (var i = 0; i < abv.length; i++) { |
|
abv[i] = data[i]; |
|
} |
|
|
|
data = abv.buffer; |
|
} |
|
return { type: type, data: data }; |
|
} |
|
exports.decodeBase64Packet = decodeBase64Packet; |
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function encodePayload(packets, supportsBinary, callback) { |
|
if (typeof supportsBinary === 'function') { |
|
callback = supportsBinary; |
|
supportsBinary = null; |
|
} |
|
if (supportsBinary && hasBinary(packets)) { |
|
return encodePayloadAsBinary(packets, callback); |
|
} |
|
if (!packets.length) { |
|
return callback('0:'); |
|
} |
|
function encodeOne(packet, doneCallback) { |
|
encodePacket(packet, supportsBinary, false, function (message) { |
|
doneCallback(null, setLengthHeader(message)); |
|
}); |
|
} |
|
map(packets, encodeOne, function (err, results) { |
|
return callback(results.join('')); |
|
}); |
|
} |
|
exports.encodePayload = encodePayload; |
|
; |
|
function setLengthHeader(message) { |
|
return message.length + ':' + message; |
|
} |
|
|
|
|
|
|
|
function map(ary, each, done) { |
|
const results = new Array(ary.length); |
|
let count = 0; |
|
for (let i = 0; i < ary.length; i++) { |
|
each(ary[i], (error, msg) => { |
|
results[i] = msg; |
|
if (++count === ary.length) { |
|
done(null, results); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function decodePayload(data, binaryType, callback) { |
|
if (typeof data !== 'string') { |
|
return decodePayloadAsBinary(data, binaryType, callback); |
|
} |
|
if (typeof binaryType === 'function') { |
|
callback = binaryType; |
|
binaryType = null; |
|
} |
|
if (data === '') { |
|
|
|
return callback(err, 0, 1); |
|
} |
|
var length = '', n, msg, packet; |
|
for (var i = 0, l = data.length; i < l; i++) { |
|
var chr = data.charAt(i); |
|
if (chr !== ':') { |
|
length += chr; |
|
continue; |
|
} |
|
|
|
if (length === '' || (length != (n = Number(length)))) { |
|
|
|
return callback(err, 0, 1); |
|
} |
|
msg = data.slice(i + 1, i + 1 + n); |
|
if (length != msg.length) { |
|
|
|
return callback(err, 0, 1); |
|
} |
|
if (msg.length) { |
|
packet = decodePacket(msg, binaryType, false); |
|
if (err.type === packet.type && err.data === packet.data) { |
|
|
|
return callback(err, 0, 1); |
|
} |
|
var more = callback(packet, i + n, l); |
|
if (false === more) |
|
return; |
|
} |
|
|
|
i += n; |
|
length = ''; |
|
} |
|
if (length !== '') { |
|
|
|
return callback(err, 0, 1); |
|
} |
|
} |
|
exports.decodePayload = decodePayload; |
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function bufferToString(buffer) { |
|
var str = ''; |
|
for (var i = 0, l = buffer.length; i < l; i++) { |
|
str += String.fromCharCode(buffer[i]); |
|
} |
|
return str; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function stringToBuffer(string) { |
|
var buf = Buffer.allocUnsafe(string.length); |
|
for (var i = 0, l = string.length; i < l; i++) { |
|
buf.writeUInt8(string.charCodeAt(i), i); |
|
} |
|
return buf; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function arrayBufferToBuffer(data) { |
|
|
|
var length = data.byteLength || data.length; |
|
var offset = data.byteOffset || 0; |
|
return Buffer.from(data.buffer || data, offset, length); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function encodePayloadAsBinary(packets, callback) { |
|
if (!packets.length) { |
|
return callback(EMPTY_BUFFER); |
|
} |
|
map(packets, encodeOneBinaryPacket, function (err, results) { |
|
return callback(Buffer.concat(results)); |
|
}); |
|
} |
|
exports.encodePayloadAsBinary = encodePayloadAsBinary; |
|
; |
|
function encodeOneBinaryPacket(p, doneCallback) { |
|
function onBinaryPacketEncode(packet) { |
|
var encodingLength = '' + packet.length; |
|
var sizeBuffer; |
|
if (typeof packet === 'string') { |
|
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2); |
|
sizeBuffer[0] = 0; |
|
for (var i = 0; i < encodingLength.length; i++) { |
|
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10); |
|
} |
|
sizeBuffer[sizeBuffer.length - 1] = 255; |
|
return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)])); |
|
} |
|
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2); |
|
sizeBuffer[0] = 1; |
|
for (var i = 0; i < encodingLength.length; i++) { |
|
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10); |
|
} |
|
sizeBuffer[sizeBuffer.length - 1] = 255; |
|
doneCallback(null, Buffer.concat([sizeBuffer, packet])); |
|
} |
|
encodePacket(p, true, true, onBinaryPacketEncode); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function decodePayloadAsBinary(data, binaryType, callback) { |
|
if (typeof binaryType === 'function') { |
|
callback = binaryType; |
|
binaryType = null; |
|
} |
|
var bufferTail = data; |
|
var buffers = []; |
|
var i; |
|
while (bufferTail.length > 0) { |
|
var strLen = ''; |
|
var isString = bufferTail[0] === 0; |
|
for (i = 1;; i++) { |
|
if (bufferTail[i] === 255) |
|
break; |
|
|
|
if (strLen.length > 310) { |
|
return callback(err, 0, 1); |
|
} |
|
strLen += '' + bufferTail[i]; |
|
} |
|
bufferTail = bufferTail.slice(strLen.length + 1); |
|
var msgLength = parseInt(strLen, 10); |
|
var msg = bufferTail.slice(1, msgLength + 1); |
|
if (isString) |
|
msg = bufferToString(msg); |
|
buffers.push(msg); |
|
bufferTail = bufferTail.slice(msgLength + 1); |
|
} |
|
var total = buffers.length; |
|
for (i = 0; i < total; i++) { |
|
var buffer = buffers[i]; |
|
callback(decodePacket(buffer, binaryType, true), i, total); |
|
} |
|
} |
|
exports.decodePayloadAsBinary = decodePayloadAsBinary; |
|
; |
|
|