index.js 7.4 KB

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