diff options
Diffstat (limited to 'devtools/client/netmonitor/src/components/messages/parsers/socket-io/index.js')
-rw-r--r-- | devtools/client/netmonitor/src/components/messages/parsers/socket-io/index.js | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/messages/parsers/socket-io/index.js b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/index.js new file mode 100644 index 0000000000..9d631d014b --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/index.js @@ -0,0 +1,292 @@ +/* + * A socket.io encoder and decoder written in JavaScript complying with version 4 + * of socket.io-protocol. Used by socket.io and socket.io-client. + * + * Copyright (c) 2014 Guillermo Rauch <guillermo@learnboost.com> + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of the library source tree. + * + * https://github.com/socketio/socket.io-parser + */ + +/* eslint-disable no-unused-vars */ + +"use strict"; + +const Emitter = require("resource://devtools/client/netmonitor/src/components/messages/parsers/socket-io/component-emitter.js"); +const binary = require("resource://devtools/client/netmonitor/src/components/messages/parsers/socket-io/binary.js"); +const isBuf = require("resource://devtools/client/netmonitor/src/components/messages/parsers/socket-io/is-buffer.js"); + +/** + * Packet types + */ + +const TYPES = [ + "CONNECT", + "DISCONNECT", + "EVENT", + "ACK", + "ERROR", + "BINARY_EVENT", + "BINARY_ACK", +]; + +/** + * Packet type `connect` + */ + +const CONNECT = 0; + +/** + * Packet type `disconnect` + */ + +const DISCONNECT = 1; + +/** + * Packet type `event` + */ + +const EVENT = 2; + +/** + * Packet type `ack` + */ + +const ACK = 3; + +/** + * Packet type `error` + */ + +const ERROR = 4; + +/** + * Packet type 'binary event' + */ +const BINARY_EVENT = 5; + +/** + * Packet type `binary ack`. For acks with binary arguments + */ + +const BINARY_ACK = 6; + +/** + * A socket.io Decoder instance + * + * @return {Object} decoder + * @api public + */ + +function Decoder() { + this.reconstructor = null; +} + +/** + * Mix in `Emitter` with Decoder. + */ + +Emitter(Decoder.prototype); + +/** + * A manager of a binary event's 'buffer sequence'. Should + * be constructed whenever a packet of type BINARY_EVENT is + * decoded. + * + * @param {Object} packet + * @return {BinaryReconstructor} initialized reconstructor + * @api private + */ + +function BinaryReconstructor(packet) { + this.reconPack = packet; + this.buffers = []; +} + +/** + * Method to be called when binary data received from connection + * after a BINARY_EVENT packet. + * + * @param {Buffer | ArrayBuffer} binData - the raw binary data received + * @return {null | Object} returns null if more binary data is expected or + * a reconstructed packet object if all buffers have been received. + * @api private + */ + +BinaryReconstructor.prototype.takeBinaryData = function (binData) { + this.buffers.push(binData); + if (this.buffers.length === this.reconPack.attachments) { + // done with buffer list + const packet = binary.reconstructPacket(this.reconPack, this.buffers); + this.finishedReconstruction(); + return packet; + } + return null; +}; + +/** + * Cleans up binary packet reconstruction variables. + * + * @api private + */ + +BinaryReconstructor.prototype.finishedReconstruction = function () { + this.reconPack = null; + this.buffers = []; +}; + +/** + * Decodes an encoded packet string into packet JSON. + * + * @param {String} obj - encoded packet + * @return {Object} packet + * @api public + */ + +Decoder.prototype.add = function (obj) { + let packet; + if (typeof obj === "string") { + packet = decodeString(obj); + if (BINARY_EVENT === packet.type || BINARY_ACK === packet.type) { + // binary packet's json + this.reconstructor = new BinaryReconstructor(packet); + + // no attachments, labeled binary but no binary data to follow + if (this.reconstructor.reconPack.attachments === 0) { + this.emit("decoded", packet); + } + } else { + // non-binary full packet + this.emit("decoded", packet); + } + } else if (isBuf(obj) || obj.base64) { + // raw binary data + if (!this.reconstructor) { + throw new Error("got binary data when not reconstructing a packet"); + } else { + packet = this.reconstructor.takeBinaryData(obj); + if (packet) { + // received final buffer + this.reconstructor = null; + this.emit("decoded", packet); + } + } + } else { + throw new Error("Unknown type: " + obj); + } +}; + +/** + * Decode a packet String (JSON data) + * + * @param {String} str + * @return {Object} packet + * @api private + */ +// eslint-disable-next-line complexity +function decodeString(str) { + let i = 0; + // look up type + const p = { + type: Number(str.charAt(0)), + }; + + if (TYPES[p.type] == null) { + return error("unknown packet type " + p.type); + } + + // look up attachments if type binary + if (BINARY_EVENT === p.type || BINARY_ACK === p.type) { + let buf = ""; + while (str.charAt(++i) !== "-") { + buf += str.charAt(i); + if (i === str.length) { + break; + } + } + if (buf != Number(buf) || str.charAt(i) !== "-") { + throw new Error("Illegal attachments"); + } + p.attachments = Number(buf); + } + + // look up namespace (if any) + if (str.charAt(i + 1) === "/") { + p.nsp = ""; + while (++i) { + const c = str.charAt(i); + if (c === ",") { + break; + } + p.nsp += c; + if (i === str.length) { + break; + } + } + } else { + p.nsp = "/"; + } + + // look up id + const next = str.charAt(i + 1); + if (next !== "" && Number(next) == next) { + p.id = ""; + while (++i) { + const c = str.charAt(i); + if (c == null || Number(c) != c) { + --i; + break; + } + p.id += str.charAt(i); + if (i === str.length) { + break; + } + } + p.id = Number(p.id); + } + + // look up json data + if (str.charAt(++i)) { + const payload = tryParse(str.substr(i)); + const isPayloadValid = + payload !== false && (p.type === ERROR || Array.isArray(payload)); + if (isPayloadValid) { + p.data = payload; + } else { + return error("invalid payload"); + } + } + + return p; +} + +function tryParse(str) { + try { + return JSON.parse(str); + } catch (e) { + return false; + } +} + +/** + * Deallocates a parser's resources + * + * @api public + */ + +Decoder.prototype.destroy = function () { + if (this.reconstructor) { + this.reconstructor.finishedReconstruction(); + } +}; + +function error(msg) { + return { + type: ERROR, + data: "parser error: " + msg, + }; +} + +module.exports = Decoder; |