diff options
Diffstat (limited to 'devtools/client/netmonitor/src/components/messages/parsers/socket-io')
5 files changed, 474 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/messages/parsers/socket-io/binary.js b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/binary.js new file mode 100644 index 0000000000..340791199f --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/binary.js @@ -0,0 +1,48 @@ +/* + * 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 + */ + +"use strict"; + +/** + * Reconstructs a binary packet from its placeholder packet and buffers + * + * @param {Object} packet - event packet with placeholders + * @param {Array} buffers - binary buffers to put in placeholder positions + * @return {Object} reconstructed packet + * @api public + */ + +exports.reconstructPacket = function (packet, buffers) { + packet.data = _reconstructPacket(packet.data, buffers); + packet.attachments = undefined; // no longer useful + return packet; +}; + +function _reconstructPacket(data, buffers) { + if (!data) { + return data; + } + + if (data && data._placeholder) { + return buffers[data.num]; // appropriate buffer (should be natural order anyway) + } else if (Array.isArray(data)) { + for (let i = 0; i < data.length; i++) { + data[i] = _reconstructPacket(data[i], buffers); + } + } else if (typeof data === "object") { + for (const key in data) { + data[key] = _reconstructPacket(data[key], buffers); + } + } + + return data; +} diff --git a/devtools/client/netmonitor/src/components/messages/parsers/socket-io/component-emitter.js b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/component-emitter.js new file mode 100644 index 0000000000..5874778f75 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/component-emitter.js @@ -0,0 +1,84 @@ +/* + * Event emitter component. + * + * Copyright (c) 2014 Component contributors <dev@component.io> + * + * 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/component/emitter + */ + +"use strict"; + +/** + * Initialize a new `Emitter`. + * + * @api public + */ + +function Emitter(obj) { + if (obj) { + return mixin(obj); + } +} + +/** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (const key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +/** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.on = function (event, fn) { + this._callbacks = this._callbacks || {}; + (this._callbacks["$" + event] = this._callbacks["$" + event] || []).push(fn); + return this; +}; + +/** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + +Emitter.prototype.emit = function (event) { + this._callbacks = this._callbacks || {}; + + const args = new Array(arguments.length - 1); + let callbacks = this._callbacks["$" + event]; + + for (let i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + + if (callbacks) { + callbacks = callbacks.slice(0); + for (let i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +module.exports = Emitter; 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; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/socket-io/is-buffer.js b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/is-buffer.js new file mode 100644 index 0000000000..cdbad14abc --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/is-buffer.js @@ -0,0 +1,40 @@ +/* + * 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-undef */ + +"use strict"; + +var withNativeBuffer = + typeof Buffer === "function" && typeof Buffer.isBuffer === "function"; +var withNativeArrayBuffer = typeof ArrayBuffer === "function"; + +var isView = function (obj) { + return typeof ArrayBuffer.isView === "function" + ? ArrayBuffer.isView(obj) + : obj.buffer instanceof ArrayBuffer; +}; + +/** + * Returns true if obj is a buffer or an arraybuffer. + * + * @api private + */ + +function isBuf(obj) { + return ( + (withNativeBuffer && Buffer.isBuffer(obj)) || + (withNativeArrayBuffer && (obj instanceof ArrayBuffer || isView(obj))) + ); +} + +module.exports = isBuf; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/socket-io/moz.build b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/moz.build new file mode 100644 index 0000000000..d38ca19dd6 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/socket-io/moz.build @@ -0,0 +1,10 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DevToolsModules( + "binary.js", + "component-emitter.js", + "index.js", + "is-buffer.js", +) |