[6a3a178] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.Decoder = exports.Encoder = exports.PacketType = exports.protocol = void 0;
|
---|
| 4 | const Emitter = require("component-emitter");
|
---|
| 5 | const binary_1 = require("./binary");
|
---|
| 6 | const is_binary_1 = require("./is-binary");
|
---|
| 7 | const debug = require("debug")("socket.io-parser");
|
---|
| 8 | /**
|
---|
| 9 | * Protocol version.
|
---|
| 10 | *
|
---|
| 11 | * @public
|
---|
| 12 | */
|
---|
| 13 | exports.protocol = 5;
|
---|
| 14 | var PacketType;
|
---|
| 15 | (function (PacketType) {
|
---|
| 16 | PacketType[PacketType["CONNECT"] = 0] = "CONNECT";
|
---|
| 17 | PacketType[PacketType["DISCONNECT"] = 1] = "DISCONNECT";
|
---|
| 18 | PacketType[PacketType["EVENT"] = 2] = "EVENT";
|
---|
| 19 | PacketType[PacketType["ACK"] = 3] = "ACK";
|
---|
| 20 | PacketType[PacketType["CONNECT_ERROR"] = 4] = "CONNECT_ERROR";
|
---|
| 21 | PacketType[PacketType["BINARY_EVENT"] = 5] = "BINARY_EVENT";
|
---|
| 22 | PacketType[PacketType["BINARY_ACK"] = 6] = "BINARY_ACK";
|
---|
| 23 | })(PacketType = exports.PacketType || (exports.PacketType = {}));
|
---|
| 24 | /**
|
---|
| 25 | * A socket.io Encoder instance
|
---|
| 26 | */
|
---|
| 27 | class Encoder {
|
---|
| 28 | /**
|
---|
| 29 | * Encode a packet as a single string if non-binary, or as a
|
---|
| 30 | * buffer sequence, depending on packet type.
|
---|
| 31 | *
|
---|
| 32 | * @param {Object} obj - packet object
|
---|
| 33 | */
|
---|
| 34 | encode(obj) {
|
---|
| 35 | debug("encoding packet %j", obj);
|
---|
| 36 | if (obj.type === PacketType.EVENT || obj.type === PacketType.ACK) {
|
---|
| 37 | if (is_binary_1.hasBinary(obj)) {
|
---|
| 38 | obj.type =
|
---|
| 39 | obj.type === PacketType.EVENT
|
---|
| 40 | ? PacketType.BINARY_EVENT
|
---|
| 41 | : PacketType.BINARY_ACK;
|
---|
| 42 | return this.encodeAsBinary(obj);
|
---|
| 43 | }
|
---|
| 44 | }
|
---|
| 45 | return [this.encodeAsString(obj)];
|
---|
| 46 | }
|
---|
| 47 | /**
|
---|
| 48 | * Encode packet as string.
|
---|
| 49 | */
|
---|
| 50 | encodeAsString(obj) {
|
---|
| 51 | // first is type
|
---|
| 52 | let str = "" + obj.type;
|
---|
| 53 | // attachments if we have them
|
---|
| 54 | if (obj.type === PacketType.BINARY_EVENT ||
|
---|
| 55 | obj.type === PacketType.BINARY_ACK) {
|
---|
| 56 | str += obj.attachments + "-";
|
---|
| 57 | }
|
---|
| 58 | // if we have a namespace other than `/`
|
---|
| 59 | // we append it followed by a comma `,`
|
---|
| 60 | if (obj.nsp && "/" !== obj.nsp) {
|
---|
| 61 | str += obj.nsp + ",";
|
---|
| 62 | }
|
---|
| 63 | // immediately followed by the id
|
---|
| 64 | if (null != obj.id) {
|
---|
| 65 | str += obj.id;
|
---|
| 66 | }
|
---|
| 67 | // json data
|
---|
| 68 | if (null != obj.data) {
|
---|
| 69 | str += JSON.stringify(obj.data);
|
---|
| 70 | }
|
---|
| 71 | debug("encoded %j as %s", obj, str);
|
---|
| 72 | return str;
|
---|
| 73 | }
|
---|
| 74 | /**
|
---|
| 75 | * Encode packet as 'buffer sequence' by removing blobs, and
|
---|
| 76 | * deconstructing packet into object with placeholders and
|
---|
| 77 | * a list of buffers.
|
---|
| 78 | */
|
---|
| 79 | encodeAsBinary(obj) {
|
---|
| 80 | const deconstruction = binary_1.deconstructPacket(obj);
|
---|
| 81 | const pack = this.encodeAsString(deconstruction.packet);
|
---|
| 82 | const buffers = deconstruction.buffers;
|
---|
| 83 | buffers.unshift(pack); // add packet info to beginning of data list
|
---|
| 84 | return buffers; // write all the buffers
|
---|
| 85 | }
|
---|
| 86 | }
|
---|
| 87 | exports.Encoder = Encoder;
|
---|
| 88 | /**
|
---|
| 89 | * A socket.io Decoder instance
|
---|
| 90 | *
|
---|
| 91 | * @return {Object} decoder
|
---|
| 92 | */
|
---|
| 93 | class Decoder extends Emitter {
|
---|
| 94 | constructor() {
|
---|
| 95 | super();
|
---|
| 96 | }
|
---|
| 97 | /**
|
---|
| 98 | * Decodes an encoded packet string into packet JSON.
|
---|
| 99 | *
|
---|
| 100 | * @param {String} obj - encoded packet
|
---|
| 101 | */
|
---|
| 102 | add(obj) {
|
---|
| 103 | let packet;
|
---|
| 104 | if (typeof obj === "string") {
|
---|
| 105 | packet = this.decodeString(obj);
|
---|
| 106 | if (packet.type === PacketType.BINARY_EVENT ||
|
---|
| 107 | packet.type === PacketType.BINARY_ACK) {
|
---|
| 108 | // binary packet's json
|
---|
| 109 | this.reconstructor = new BinaryReconstructor(packet);
|
---|
| 110 | // no attachments, labeled binary but no binary data to follow
|
---|
| 111 | if (packet.attachments === 0) {
|
---|
| 112 | super.emit("decoded", packet);
|
---|
| 113 | }
|
---|
| 114 | }
|
---|
| 115 | else {
|
---|
| 116 | // non-binary full packet
|
---|
| 117 | super.emit("decoded", packet);
|
---|
| 118 | }
|
---|
| 119 | }
|
---|
| 120 | else if (is_binary_1.isBinary(obj) || obj.base64) {
|
---|
| 121 | // raw binary data
|
---|
| 122 | if (!this.reconstructor) {
|
---|
| 123 | throw new Error("got binary data when not reconstructing a packet");
|
---|
| 124 | }
|
---|
| 125 | else {
|
---|
| 126 | packet = this.reconstructor.takeBinaryData(obj);
|
---|
| 127 | if (packet) {
|
---|
| 128 | // received final buffer
|
---|
| 129 | this.reconstructor = null;
|
---|
| 130 | super.emit("decoded", packet);
|
---|
| 131 | }
|
---|
| 132 | }
|
---|
| 133 | }
|
---|
| 134 | else {
|
---|
| 135 | throw new Error("Unknown type: " + obj);
|
---|
| 136 | }
|
---|
| 137 | }
|
---|
| 138 | /**
|
---|
| 139 | * Decode a packet String (JSON data)
|
---|
| 140 | *
|
---|
| 141 | * @param {String} str
|
---|
| 142 | * @return {Object} packet
|
---|
| 143 | */
|
---|
| 144 | decodeString(str) {
|
---|
| 145 | let i = 0;
|
---|
| 146 | // look up type
|
---|
| 147 | const p = {
|
---|
| 148 | type: Number(str.charAt(0)),
|
---|
| 149 | };
|
---|
| 150 | if (PacketType[p.type] === undefined) {
|
---|
| 151 | throw new Error("unknown packet type " + p.type);
|
---|
| 152 | }
|
---|
| 153 | // look up attachments if type binary
|
---|
| 154 | if (p.type === PacketType.BINARY_EVENT ||
|
---|
| 155 | p.type === PacketType.BINARY_ACK) {
|
---|
| 156 | const start = i + 1;
|
---|
| 157 | while (str.charAt(++i) !== "-" && i != str.length) { }
|
---|
| 158 | const buf = str.substring(start, i);
|
---|
| 159 | if (buf != Number(buf) || str.charAt(i) !== "-") {
|
---|
| 160 | throw new Error("Illegal attachments");
|
---|
| 161 | }
|
---|
| 162 | p.attachments = Number(buf);
|
---|
| 163 | }
|
---|
| 164 | // look up namespace (if any)
|
---|
| 165 | if ("/" === str.charAt(i + 1)) {
|
---|
| 166 | const start = i + 1;
|
---|
| 167 | while (++i) {
|
---|
| 168 | const c = str.charAt(i);
|
---|
| 169 | if ("," === c)
|
---|
| 170 | break;
|
---|
| 171 | if (i === str.length)
|
---|
| 172 | break;
|
---|
| 173 | }
|
---|
| 174 | p.nsp = str.substring(start, i);
|
---|
| 175 | }
|
---|
| 176 | else {
|
---|
| 177 | p.nsp = "/";
|
---|
| 178 | }
|
---|
| 179 | // look up id
|
---|
| 180 | const next = str.charAt(i + 1);
|
---|
| 181 | if ("" !== next && Number(next) == next) {
|
---|
| 182 | const start = i + 1;
|
---|
| 183 | while (++i) {
|
---|
| 184 | const c = str.charAt(i);
|
---|
| 185 | if (null == c || Number(c) != c) {
|
---|
| 186 | --i;
|
---|
| 187 | break;
|
---|
| 188 | }
|
---|
| 189 | if (i === str.length)
|
---|
| 190 | break;
|
---|
| 191 | }
|
---|
| 192 | p.id = Number(str.substring(start, i + 1));
|
---|
| 193 | }
|
---|
| 194 | // look up json data
|
---|
| 195 | if (str.charAt(++i)) {
|
---|
| 196 | const payload = tryParse(str.substr(i));
|
---|
| 197 | if (Decoder.isPayloadValid(p.type, payload)) {
|
---|
| 198 | p.data = payload;
|
---|
| 199 | }
|
---|
| 200 | else {
|
---|
| 201 | throw new Error("invalid payload");
|
---|
| 202 | }
|
---|
| 203 | }
|
---|
| 204 | debug("decoded %s as %j", str, p);
|
---|
| 205 | return p;
|
---|
| 206 | }
|
---|
| 207 | static isPayloadValid(type, payload) {
|
---|
| 208 | switch (type) {
|
---|
| 209 | case PacketType.CONNECT:
|
---|
| 210 | return typeof payload === "object";
|
---|
| 211 | case PacketType.DISCONNECT:
|
---|
| 212 | return payload === undefined;
|
---|
| 213 | case PacketType.CONNECT_ERROR:
|
---|
| 214 | return typeof payload === "string" || typeof payload === "object";
|
---|
| 215 | case PacketType.EVENT:
|
---|
| 216 | case PacketType.BINARY_EVENT:
|
---|
| 217 | return Array.isArray(payload) && payload.length > 0;
|
---|
| 218 | case PacketType.ACK:
|
---|
| 219 | case PacketType.BINARY_ACK:
|
---|
| 220 | return Array.isArray(payload);
|
---|
| 221 | }
|
---|
| 222 | }
|
---|
| 223 | /**
|
---|
| 224 | * Deallocates a parser's resources
|
---|
| 225 | */
|
---|
| 226 | destroy() {
|
---|
| 227 | if (this.reconstructor) {
|
---|
| 228 | this.reconstructor.finishedReconstruction();
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 | }
|
---|
| 232 | exports.Decoder = Decoder;
|
---|
| 233 | function tryParse(str) {
|
---|
| 234 | try {
|
---|
| 235 | return JSON.parse(str);
|
---|
| 236 | }
|
---|
| 237 | catch (e) {
|
---|
| 238 | return false;
|
---|
| 239 | }
|
---|
| 240 | }
|
---|
| 241 | /**
|
---|
| 242 | * A manager of a binary event's 'buffer sequence'. Should
|
---|
| 243 | * be constructed whenever a packet of type BINARY_EVENT is
|
---|
| 244 | * decoded.
|
---|
| 245 | *
|
---|
| 246 | * @param {Object} packet
|
---|
| 247 | * @return {BinaryReconstructor} initialized reconstructor
|
---|
| 248 | */
|
---|
| 249 | class BinaryReconstructor {
|
---|
| 250 | constructor(packet) {
|
---|
| 251 | this.packet = packet;
|
---|
| 252 | this.buffers = [];
|
---|
| 253 | this.reconPack = packet;
|
---|
| 254 | }
|
---|
| 255 | /**
|
---|
| 256 | * Method to be called when binary data received from connection
|
---|
| 257 | * after a BINARY_EVENT packet.
|
---|
| 258 | *
|
---|
| 259 | * @param {Buffer | ArrayBuffer} binData - the raw binary data received
|
---|
| 260 | * @return {null | Object} returns null if more binary data is expected or
|
---|
| 261 | * a reconstructed packet object if all buffers have been received.
|
---|
| 262 | */
|
---|
| 263 | takeBinaryData(binData) {
|
---|
| 264 | this.buffers.push(binData);
|
---|
| 265 | if (this.buffers.length === this.reconPack.attachments) {
|
---|
| 266 | // done with buffer list
|
---|
| 267 | const packet = binary_1.reconstructPacket(this.reconPack, this.buffers);
|
---|
| 268 | this.finishedReconstruction();
|
---|
| 269 | return packet;
|
---|
| 270 | }
|
---|
| 271 | return null;
|
---|
| 272 | }
|
---|
| 273 | /**
|
---|
| 274 | * Cleans up binary packet reconstruction variables.
|
---|
| 275 | */
|
---|
| 276 | finishedReconstruction() {
|
---|
| 277 | this.reconPack = null;
|
---|
| 278 | this.buffers = [];
|
---|
| 279 | }
|
---|
| 280 | }
|
---|