engine.io-parser
Advanced tools
Comparing version 4.0.0-alpha.0 to 4.0.0-alpha.1
492
lib/index.js
@@ -1,478 +0,40 @@ | ||
const utf8 = require("./utf8"); | ||
const hasBinary = require("has-binary2"); | ||
const after = require("after"); | ||
const encodePacket = require("./encodePacket"); | ||
const decodePacket = require("./decodePacket"); | ||
/** | ||
* Current protocol version. | ||
*/ | ||
exports.protocol = 4; | ||
const SEPARATOR = "\x1e"; // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text | ||
/** | ||
* Packet types. | ||
*/ | ||
const packets = (exports.packets = { | ||
open: 0, // non-ws | ||
close: 1, // non-ws | ||
ping: 2, | ||
pong: 3, | ||
message: 4, | ||
upgrade: 5, | ||
noop: 6 | ||
}); | ||
const encodePayload = (packets, callback) => { | ||
const encodedPackets = new Array(packets.length); | ||
let count = 0; | ||
const packetslist = Object.keys(packets); | ||
/** | ||
* Premade error packet. | ||
*/ | ||
const err = { type: "error", data: "parser error" }; | ||
const EMPTY_BUFFER = Buffer.concat([]); | ||
/** | ||
* Encodes a packet. | ||
* | ||
* <packet type id> [ <data> ] | ||
* | ||
* Example: | ||
* | ||
* 5hello world | ||
* 3 | ||
* 4 | ||
* | ||
* Binary is encoded in an identical principle | ||
* | ||
* @api private | ||
*/ | ||
exports.encodePacket = function(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 | ||
); | ||
} | ||
// Sending data as a utf-8 string | ||
let encoded = packets[packet.type]; | ||
// data fragment is optional | ||
if (undefined !== packet.data) { | ||
encoded += utf8encode | ||
? utf8.encode(String(packet.data), { strict: false }) | ||
: String(packet.data); | ||
} | ||
return callback("" + encoded); | ||
}; | ||
/** | ||
* Encode Buffer data | ||
*/ | ||
function encodeBuffer(packet, supportsBinary, callback) { | ||
if (!supportsBinary) { | ||
return exports.encodeBase64Packet(packet, callback); | ||
} | ||
return callback(packet.data); | ||
} | ||
/** | ||
* Encodes a packet with binary data in a base64 string | ||
* | ||
* @param {Object} packet, has `type` and `data` | ||
* @return {String} base64 encoded message | ||
*/ | ||
exports.encodeBase64Packet = function(packet, callback) { | ||
const data = Buffer.isBuffer(packet.data) | ||
? packet.data | ||
: arrayBufferToBuffer(packet.data); | ||
let message = "b" + packets[packet.type]; | ||
message += data.toString("base64"); | ||
return callback(message); | ||
}; | ||
/** | ||
* Decodes a packet. Data also available as an ArrayBuffer if requested. | ||
* | ||
* @return {Object} with `type` and `data` (if any) | ||
* @api private | ||
*/ | ||
exports.decodePacket = function(data, binaryType, utf8decode) { | ||
if (data === undefined) { | ||
return err; | ||
} | ||
let type; | ||
// String data | ||
if (typeof data === "string") { | ||
type = data.charAt(0); | ||
if (type === "b") { | ||
return exports.decodeBase64Packet(data.substr(1), binaryType); | ||
} | ||
if (utf8decode) { | ||
data = tryDecode(data); | ||
if (data === false) { | ||
return err; | ||
packets.forEach((packet, i) => { | ||
// force base64 encoding for binary packets | ||
encodePacket(packet, false, encodedPacket => { | ||
encodedPackets[i] = encodedPacket; | ||
if (++count === packets.length) { | ||
callback(encodedPackets.join(SEPARATOR)); | ||
} | ||
} | ||
if (Number(type) != type || !packetslist[type]) { | ||
return err; | ||
} | ||
if (data.length > 1) { | ||
return { type: packetslist[type], data: data.substring(1) }; | ||
} else { | ||
return { type: packetslist[type] }; | ||
} | ||
} | ||
// Binary data | ||
if (binaryType === "arraybuffer") { | ||
// wrap Buffer/ArrayBuffer data into an Uint8Array | ||
const intArray = new Uint8Array(data); | ||
return { type: "message", data: intArray.buffer }; | ||
} | ||
if (data instanceof ArrayBuffer) { | ||
data = arrayBufferToBuffer(data); | ||
} | ||
// only 'message' packets can contain binary data | ||
return { type: "message", data }; | ||
}; | ||
function tryDecode(data) { | ||
try { | ||
data = utf8.decode(data, { strict: false }); | ||
} catch (e) { | ||
return false; | ||
} | ||
return data; | ||
} | ||
/** | ||
* Decodes a packet encoded in a base64 string. | ||
* | ||
* @param {String} base64 encoded message | ||
* @return {Object} with `type` and `data` (if any) | ||
*/ | ||
exports.decodeBase64Packet = function(msg, binaryType) { | ||
const type = packetslist[msg.charAt(0)]; | ||
let data = Buffer.from(msg.substr(1), "base64"); | ||
if (binaryType === "arraybuffer") { | ||
const abv = new Uint8Array(data.length); | ||
for (let i = 0; i < abv.length; i++) { | ||
abv[i] = data[i]; | ||
} | ||
data = abv.buffer; | ||
} | ||
return { type: type, data: data }; | ||
}; | ||
/** | ||
* Encodes multiple messages (payload). | ||
* | ||
* <length>:data | ||
* | ||
* Example: | ||
* | ||
* 11:hello world2:hi | ||
* | ||
* If any contents are binary, they will be encoded as base64 strings. Base64 | ||
* encoded strings are marked with a b before the length specifier | ||
* | ||
* @param {Array} packets | ||
* @api private | ||
*/ | ||
exports.encodePayload = function(packets, supportsBinary, callback) { | ||
if (typeof supportsBinary === "function") { | ||
callback = supportsBinary; | ||
supportsBinary = null; | ||
} | ||
if (supportsBinary && hasBinary(packets)) { | ||
return exports.encodePayloadAsBinary(packets, callback); | ||
} | ||
if (!packets.length) { | ||
return callback("0:"); | ||
} | ||
function encodeOne(packet, doneCallback) { | ||
exports.encodePacket(packet, supportsBinary, false, function(message) { | ||
doneCallback(null, setLengthHeader(message)); | ||
}); | ||
} | ||
map(packets, encodeOne, function(err, results) { | ||
return callback(results.join("")); | ||
}); | ||
}; | ||
function setLengthHeader(message) { | ||
return message.length + ":" + message; | ||
} | ||
/** | ||
* Async array map using after | ||
*/ | ||
function map(ary, each, done) { | ||
const result = new Array(ary.length); | ||
const next = after(ary.length, done); | ||
for (let i = 0; i < ary.length; i++) { | ||
each(ary[i], function(error, msg) { | ||
result[i] = msg; | ||
next(error, result); | ||
}); | ||
} | ||
} | ||
/* | ||
* Decodes data when a payload is maybe expected. Possible binary contents are | ||
* decoded from their base64 representation | ||
* | ||
* @param {String} data, callback method | ||
* @api public | ||
*/ | ||
exports.decodePayload = function(data, binaryType, callback) { | ||
if (typeof data !== "string") { | ||
return exports.decodePayloadAsBinary(data, binaryType, callback); | ||
} | ||
if (typeof binaryType === "function") { | ||
callback = binaryType; | ||
binaryType = null; | ||
} | ||
if (data === "") { | ||
// parser error - ignoring payload | ||
return callback(err, 0, 1); | ||
} | ||
let length = "", | ||
n, | ||
msg, | ||
packet; | ||
let i = 0; | ||
const l = data.length; | ||
for (; i < l; i++) { | ||
const chr = data.charAt(i); | ||
if (chr !== ":") { | ||
length += chr; | ||
continue; | ||
const decodePayload = (encodedPayload, binaryType) => { | ||
const encodedPackets = encodedPayload.split(SEPARATOR); | ||
const packets = []; | ||
for (let i = 0; i < encodedPackets.length; i++) { | ||
const decodedPacket = decodePacket(encodedPackets[i], binaryType); | ||
packets.push(decodedPacket); | ||
if (decodedPacket.type === "error") { | ||
break; | ||
} | ||
if (length === "" || length != (n = Number(length))) { | ||
// parser error - ignoring payload | ||
return callback(err, 0, 1); | ||
} | ||
msg = data.substr(i + 1, n); | ||
if (length != msg.length) { | ||
// parser error - ignoring payload | ||
return callback(err, 0, 1); | ||
} | ||
if (msg.length) { | ||
packet = exports.decodePacket(msg, binaryType, false); | ||
if (err.type === packet.type && err.data === packet.data) { | ||
// parser error in individual packet - ignoring payload | ||
return callback(err, 0, 1); | ||
} | ||
const more = callback(packet, i + n, l); | ||
if (false === more) return; | ||
} | ||
// advance cursor | ||
i += n; | ||
length = ""; | ||
} | ||
if (length !== "") { | ||
// parser error - ignoring payload | ||
return callback(err, 0, 1); | ||
} | ||
return packets; | ||
}; | ||
/** | ||
* | ||
* Converts a buffer to a utf8.js encoded string | ||
* | ||
* @api private | ||
*/ | ||
function bufferToString(buffer) { | ||
let str = ""; | ||
let i = 0; | ||
const l = buffer.length; | ||
for (; i < l; i++) { | ||
str += String.fromCharCode(buffer[i]); | ||
} | ||
return str; | ||
} | ||
/** | ||
* | ||
* Converts a utf8.js encoded string to a buffer | ||
* | ||
* @api private | ||
*/ | ||
function stringToBuffer(string) { | ||
const buf = Buffer.allocUnsafe(string.length); | ||
let i = 0; | ||
const l = string.length; | ||
for (; i < l; i++) { | ||
buf.writeUInt8(string.charCodeAt(i), i); | ||
} | ||
return buf; | ||
} | ||
/** | ||
* | ||
* Converts an ArrayBuffer to a Buffer | ||
* | ||
* @api private | ||
*/ | ||
function arrayBufferToBuffer(data) { | ||
// data is either an ArrayBuffer or ArrayBufferView. | ||
const length = data.byteLength || data.length; | ||
const offset = data.byteOffset || 0; | ||
return Buffer.from(data.buffer || data, offset, length); | ||
} | ||
/** | ||
* Encodes multiple messages (payload) as binary. | ||
* | ||
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number | ||
* 255><data> | ||
* | ||
* Example: | ||
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers | ||
* | ||
* @param {Array} packets | ||
* @return {Buffer} encoded payload | ||
* @api private | ||
*/ | ||
exports.encodePayloadAsBinary = function(packets, callback) { | ||
if (!packets.length) { | ||
return callback(EMPTY_BUFFER); | ||
} | ||
map(packets, encodeOneBinaryPacket, function(err, results) { | ||
return callback(Buffer.concat(results)); | ||
}); | ||
module.exports = { | ||
protocol: 4, | ||
encodePacket, | ||
encodePayload, | ||
decodePacket, | ||
decodePayload | ||
}; | ||
function encodeOneBinaryPacket(p, doneCallback) { | ||
function onBinaryPacketEncode(packet) { | ||
const encodingLength = "" + packet.length; | ||
let sizeBuffer; | ||
if (typeof packet === "string") { | ||
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2); | ||
sizeBuffer[0] = 0; // is a string (not true binary = 0) | ||
for (let 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; // is binary (true binary = 1) | ||
for (let i = 0; i < encodingLength.length; i++) { | ||
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10); | ||
} | ||
sizeBuffer[sizeBuffer.length - 1] = 255; | ||
doneCallback(null, Buffer.concat([sizeBuffer, packet])); | ||
} | ||
exports.encodePacket(p, true, true, onBinaryPacketEncode); | ||
} | ||
/* | ||
* Decodes data when a payload is maybe expected. Strings are decoded by | ||
* interpreting each byte as a key code for entries marked to start with 0. See | ||
* description of encodePayloadAsBinary | ||
* @param {Buffer} data, callback method | ||
* @api public | ||
*/ | ||
exports.decodePayloadAsBinary = function(data, binaryType, callback) { | ||
if (typeof binaryType === "function") { | ||
callback = binaryType; | ||
binaryType = null; | ||
} | ||
let bufferTail = data; | ||
const buffers = []; | ||
let i; | ||
while (bufferTail.length > 0) { | ||
let strLen = ""; | ||
const isString = bufferTail[0] === 0; | ||
for (i = 1; ; i++) { | ||
if (bufferTail[i] === 255) break; | ||
// 310 = char length of Number.MAX_VALUE | ||
if (strLen.length > 310) { | ||
return callback(err, 0, 1); | ||
} | ||
strLen += "" + bufferTail[i]; | ||
} | ||
bufferTail = bufferTail.slice(strLen.length + 1); | ||
const msgLength = parseInt(strLen, 10); | ||
let msg = bufferTail.slice(1, msgLength + 1); | ||
if (isString) msg = bufferToString(msg); | ||
buffers.push(msg); | ||
bufferTail = bufferTail.slice(msgLength + 1); | ||
} | ||
const total = buffers.length; | ||
for (i = 0; i < total; i++) { | ||
const buffer = buffers[i]; | ||
callback(exports.decodePacket(buffer, binaryType, true), i, total); | ||
} | ||
}; |
@@ -5,10 +5,11 @@ { | ||
"license": "MIT", | ||
"version": "4.0.0-alpha.0", | ||
"version": "4.0.0-alpha.1", | ||
"main": "lib/index.js", | ||
"homepage": "https://github.com/socketio/engine.io-parser", | ||
"devDependencies": { | ||
"@babel/core": "^7.8.3", | ||
"@babel/preset-env": "^7.8.3", | ||
"@babel/core": "~7.9.6", | ||
"@babel/preset-env": "~7.9.6", | ||
"babel-eslint": "^10.0.3", | ||
"babelify": "^10.0.0", | ||
"base64-arraybuffer": "0.1.5", | ||
"benchmark": "^2.1.4", | ||
@@ -19,2 +20,3 @@ "eslint": "^6.8.0", | ||
"mocha": "^5.2.0", | ||
"nyc": "~15.0.1", | ||
"prettier": "^1.19.1", | ||
@@ -25,11 +27,7 @@ "socket.io-browsers": "^1.0.4", | ||
}, | ||
"dependencies": { | ||
"after": "0.8.2", | ||
"arraybuffer.slice": "~0.0.7", | ||
"base64-arraybuffer": "0.1.5", | ||
"blob": "0.0.5", | ||
"has-binary2": "~1.0.2" | ||
}, | ||
"dependencies": {}, | ||
"scripts": { | ||
"test": "npm run lint && npm run format:check && make test", | ||
"test": "npm run lint && npm run format:check && if test \"$BROWSERS\" = \"1\" ; then npm run test:browser; else npm run test:node; fi", | ||
"test:node": "nyc mocha test/index.js", | ||
"test:browser": "zuul test/index.js --no-coverage", | ||
"format:check": "prettier --check 'lib/**/*.js' 'test/**/*.js'", | ||
@@ -46,3 +44,6 @@ "format:fix": "prettier --write 'lib/**/*.js' 'test/**/*.js'", | ||
], | ||
"browser": "./lib/browser.js", | ||
"browser": { | ||
"./lib/encodePacket.js": "./lib/encodePacket.browser.js", | ||
"./lib/decodePacket.js": "./lib/decodePacket.browser.js" | ||
}, | ||
"engines": { | ||
@@ -49,0 +50,0 @@ "node": ">=8.0.0" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
0
10
16924
15
213
1
- Removedafter@0.8.2
- Removedarraybuffer.slice@~0.0.7
- Removedbase64-arraybuffer@0.1.5
- Removedblob@0.0.5
- Removedhas-binary2@~1.0.2
- Removedafter@0.8.2(transitive)
- Removedarraybuffer.slice@0.0.7(transitive)
- Removedbase64-arraybuffer@0.1.5(transitive)
- Removedblob@0.0.5(transitive)
- Removedhas-binary2@1.0.3(transitive)
- Removedisarray@2.0.1(transitive)