index.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. }