You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

packet.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. 'use strict';
  2. const assert = require('bsert');
  3. const types = {
  4. CONNECT: 0,
  5. DISCONNECT: 1,
  6. EVENT: 2,
  7. ACK: 3,
  8. ERROR: 4,
  9. BINARY_EVENT: 5,
  10. BINARY_ACK: 6
  11. };
  12. class Packet {
  13. constructor(type) {
  14. this.type = type || 0;
  15. this.attachments = 0;
  16. this.nsp = '/';
  17. this.id = -1;
  18. this.data = '';
  19. this.buffers = [];
  20. }
  21. setData(data) {
  22. assert(data !== undefined);
  23. assert(typeof data !== 'number');
  24. assert(typeof data !== 'function');
  25. const [str, buffers] = deconstruct(data);
  26. this.data = str;
  27. this.buffers = buffers;
  28. this.attachments = buffers.length;
  29. if (this.attachments > 0) {
  30. switch (this.type) {
  31. case types.EVENT:
  32. this.type = types.BINARY_EVENT;
  33. break;
  34. case types.ACK:
  35. this.type = types.BINARY_ACK;
  36. break;
  37. }
  38. }
  39. return this;
  40. }
  41. getData() {
  42. if (this.data.length === 0)
  43. return null;
  44. return reconstruct(this.data, this.buffers);
  45. }
  46. toString() {
  47. let str = this.type.toString(10);
  48. switch (this.type) {
  49. case types.BINARY_EVENT:
  50. case types.BINARY_ACK:
  51. str += this.attachments.toString(10) + '-';
  52. break;
  53. }
  54. if (this.nsp !== '/')
  55. str += this.nsp + ',';
  56. if (this.id !== -1)
  57. str += this.id.toString(10);
  58. str += this.data;
  59. return str;
  60. }
  61. static fromString(str) {
  62. assert(typeof str === 'string');
  63. assert(str.length > 0);
  64. let i = 0;
  65. let type = 0;
  66. let attachments = 0;
  67. let nsp = '/';
  68. let id = -1;
  69. let data = '';
  70. [i, type] = readChar(str, i);
  71. assert(type !== -1);
  72. assert(type <= types.BINARY_ACK);
  73. switch (type) {
  74. case types.BINARY_EVENT:
  75. case types.BINARY_ACK: {
  76. [i, attachments] = readInt(str, i);
  77. assert(attachments !== -1);
  78. assert(i < str.length);
  79. assert(str[i] === '-');
  80. i += 1;
  81. break;
  82. }
  83. }
  84. if (i < str.length && str[i] === '/')
  85. [i, nsp] = readTo(str, i, ',');
  86. [i, id] = readInt(str, i);
  87. if (i < str.length)
  88. data = str.substring(i);
  89. const packet = new this();
  90. packet.type = type;
  91. packet.attachments = attachments;
  92. packet.nsp = nsp;
  93. packet.id = id;
  94. packet.data = data;
  95. return packet;
  96. }
  97. }
  98. Packet.types = types;
  99. function isPlaceholder(obj) {
  100. return obj !== null
  101. && typeof obj === 'object'
  102. && obj._placeholder === true
  103. && (obj.num >>> 0) === obj.num;
  104. }
  105. function deconstruct(obj) {
  106. const buffers = [];
  107. const out = replace('', obj, buffers, new Map());
  108. const str = JSON.stringify(out);
  109. return [str, buffers];
  110. }
  111. function replace(key, value, buffers, seen) {
  112. if (value === null || typeof value !== 'object')
  113. return value;
  114. if (Buffer.isBuffer(value)) {
  115. const placeholder = seen.get(value);
  116. // De-duplicate.
  117. if (placeholder != null)
  118. return placeholder;
  119. const out = { _placeholder: true, num: buffers.length };
  120. seen.set(value, out);
  121. buffers.push(value);
  122. return out;
  123. }
  124. if (seen.has(value))
  125. throw new TypeError('Converting circular structure to JSON.');
  126. if (Array.isArray(value)) {
  127. const out = [];
  128. seen.set(value, null);
  129. for (let i = 0; i < value.length; i++)
  130. out.push(replace(i, value[i], buffers, seen));
  131. seen.delete(value);
  132. return out;
  133. }
  134. const out = Object.create(null);
  135. const json = typeof value.toJSON === 'function'
  136. ? value.toJSON(key)
  137. : value;
  138. seen.set(value, null);
  139. for (const key of Object.keys(json))
  140. out[key] = replace(key, json[key], buffers, seen);
  141. seen.delete(value);
  142. return out;
  143. }
  144. function reconstruct(str, buffers) {
  145. return JSON.parse(str, (key, value) => {
  146. if (isPlaceholder(value)) {
  147. if (value.num < buffers.length)
  148. return buffers[value.num];
  149. }
  150. return value;
  151. });
  152. }
  153. function readChar(str, i) {
  154. const ch = str.charCodeAt(i) - 0x30;
  155. if (ch < 0 || ch > 9)
  156. return -1;
  157. return [i + 1, ch];
  158. }
  159. function readInt(str, i) {
  160. let len = 0;
  161. let num = 0;
  162. for (; i < str.length; i++) {
  163. const ch = str.charCodeAt(i) - 0x30;
  164. if (ch < 0 || ch > 9)
  165. break;
  166. num *= 10;
  167. num += ch;
  168. len += 1;
  169. assert(len <= 10);
  170. }
  171. assert(num <= 0xffffffff);
  172. if (len === 0)
  173. num = -1;
  174. return [i, num];
  175. }
  176. function readTo(str, i, ch) {
  177. let j = i;
  178. for (; j < str.length; j++) {
  179. if (str[j] === ch)
  180. break;
  181. }
  182. assert(j < str.length);
  183. return [j + 1, str.substring(i, j)];
  184. }
  185. /*
  186. * Expose
  187. */
  188. module.exports = Packet;