parser.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*!
  2. * socket.io-node
  3. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. /**
  10. * Packet types.
  11. */
  12. var packets = exports.packets = {
  13. 'disconnect': 0
  14. , 'connect': 1
  15. , 'heartbeat': 2
  16. , 'message': 3
  17. , 'json': 4
  18. , 'event': 5
  19. , 'ack': 6
  20. , 'error': 7
  21. , 'noop': 8
  22. }
  23. , packetslist = Object.keys(packets);
  24. /**
  25. * Errors reasons.
  26. */
  27. var reasons = exports.reasons = {
  28. 'transport not supported': 0
  29. , 'client not handshaken': 1
  30. , 'unauthorized': 2
  31. }
  32. , reasonslist = Object.keys(reasons);
  33. /**
  34. * Errors advice.
  35. */
  36. var advice = exports.advice = {
  37. 'reconnect': 0
  38. }
  39. , advicelist = Object.keys(advice);
  40. /**
  41. * Encodes a packet.
  42. *
  43. * @api private
  44. */
  45. exports.encodePacket = function (packet) {
  46. var type = packets[packet.type]
  47. , id = packet.id || ''
  48. , endpoint = packet.endpoint || ''
  49. , ack = packet.ack
  50. , data = null;
  51. switch (packet.type) {
  52. case 'message':
  53. if (packet.data !== '')
  54. data = packet.data;
  55. break;
  56. case 'event':
  57. var ev = { name: packet.name };
  58. if (packet.args && packet.args.length) {
  59. ev.args = packet.args;
  60. }
  61. data = JSON.stringify(ev);
  62. break;
  63. case 'json':
  64. data = JSON.stringify(packet.data);
  65. break;
  66. case 'ack':
  67. data = packet.ackId
  68. + (packet.args && packet.args.length
  69. ? '+' + JSON.stringify(packet.args) : '');
  70. break;
  71. case 'connect':
  72. if (packet.qs)
  73. data = packet.qs;
  74. break;
  75. case 'error':
  76. var reason = packet.reason ? reasons[packet.reason] : ''
  77. , adv = packet.advice ? advice[packet.advice] : ''
  78. if (reason !== '' || adv !== '')
  79. data = reason + (adv !== '' ? ('+' + adv) : '')
  80. break;
  81. }
  82. // construct packet with required fragments
  83. var encoded = type + ':' + id + (ack == 'data' ? '+' : '') + ':' + endpoint;
  84. // data fragment is optional
  85. if (data !== null && data !== undefined)
  86. encoded += ':' + data;
  87. return encoded;
  88. };
  89. /**
  90. * Encodes multiple messages (payload).
  91. *
  92. * @param {Array} messages
  93. * @api private
  94. */
  95. exports.encodePayload = function (packets) {
  96. var decoded = '';
  97. if (packets.length == 1)
  98. return packets[0];
  99. for (var i = 0, l = packets.length; i < l; i++) {
  100. var packet = packets[i];
  101. decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]
  102. }
  103. return decoded;
  104. };
  105. /**
  106. * Decodes a packet
  107. *
  108. * @api private
  109. */
  110. var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
  111. /**
  112. * Wrap the JSON.parse in a seperate function the crankshaft optimizer will
  113. * only punish this function for the usage for try catch
  114. *
  115. * @api private
  116. */
  117. function parse (data) {
  118. try { return JSON.parse(data) }
  119. catch (e) { return false }
  120. }
  121. exports.decodePacket = function (data) {
  122. var pieces = data.match(regexp);
  123. if (!pieces) return {};
  124. var id = pieces[2] || ''
  125. , data = pieces[5] || ''
  126. , packet = {
  127. type: packetslist[pieces[1]]
  128. , endpoint: pieces[4] || ''
  129. };
  130. // whether we need to acknowledge the packet
  131. if (id) {
  132. packet.id = id;
  133. if (pieces[3])
  134. packet.ack = 'data';
  135. else
  136. packet.ack = true;
  137. }
  138. // handle different packet types
  139. switch (packet.type) {
  140. case 'message':
  141. packet.data = data || '';
  142. break;
  143. case 'event':
  144. pieces = parse(data);
  145. if (pieces) {
  146. packet.name = pieces.name;
  147. packet.args = pieces.args;
  148. }
  149. packet.args = packet.args || [];
  150. break;
  151. case 'json':
  152. packet.data = parse(data);
  153. break;
  154. case 'connect':
  155. packet.qs = data || '';
  156. break;
  157. case 'ack':
  158. pieces = data.match(/^([0-9]+)(\+)?(.*)/);
  159. if (pieces) {
  160. packet.ackId = pieces[1];
  161. packet.args = [];
  162. if (pieces[3]) {
  163. packet.args = parse(pieces[3]) || [];
  164. }
  165. }
  166. break;
  167. case 'error':
  168. pieces = data.split('+');
  169. packet.reason = reasonslist[pieces[0]] || '';
  170. packet.advice = advicelist[pieces[1]] || '';
  171. }
  172. return packet;
  173. };
  174. /**
  175. * Decodes data payload. Detects multiple messages
  176. *
  177. * @return {Array} messages
  178. * @api public
  179. */
  180. exports.decodePayload = function (data) {
  181. if (undefined == data || null == data) {
  182. return [];
  183. }
  184. if (data[0] == '\ufffd') {
  185. var ret = [];
  186. for (var i = 1, length = ''; i < data.length; i++) {
  187. if (data[i] == '\ufffd') {
  188. ret.push(exports.decodePacket(data.substr(i + 1, length)));
  189. i += Number(length) + 1;
  190. length = '';
  191. } else {
  192. length += data[i];
  193. }
  194. }
  195. return ret;
  196. } else {
  197. return [exports.decodePacket(data)];
  198. }
  199. };