PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/lychee/source/net/protocol/WS.js

http://github.com/martensms/lycheeJS
JavaScript | 640 lines | 315 code | 253 blank | 72 comment | 73 complexity | ff05d3309bcb7ffcf2409a8b09271e12 MD5 | raw file
  1. lychee.define('lychee.net.protocol.WS').requires([
  2. 'lychee.codec.JSON'
  3. ]).exports((lychee, global, attachments) => {
  4. const _JSON = lychee.import('lychee.codec.JSON');
  5. /*
  6. * HELPERS
  7. */
  8. /*
  9. * WebSocket Framing Protocol
  10. *
  11. * |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  12. * +-+-+-+-+-------+-+-------------+-------------------------------+
  13. * |F|R|R|R| opcode|M| Payload len | Extended payload length |
  14. * |I|S|S|S| (4) |A| (7) | (16/64) |
  15. * |N|V|V|V| |S| | (if payload len==126/127) |
  16. * | |1|2|3| |K| | |
  17. * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
  18. * | Extended payload length continued, if payload len == 127 |
  19. * + - - - - - - - - - - - - - - - +-------------------------------+
  20. * | |Masking-key, if MASK set to 1 |
  21. * +-------------------------------+-------------------------------+
  22. * | Masking-key (continued) | Payload Data |
  23. * +-------------------------------- - - - - - - - - - - - - - - - +
  24. * : Payload Data continued ... :
  25. * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  26. * | Payload Data continued ... |
  27. * +---------------------------------------------------------------+
  28. *
  29. */
  30. const _on_ping_frame = function() {
  31. let type = this.type;
  32. if (type === Composite.TYPE.remote) {
  33. let buffer = Buffer.alloc(2);
  34. // FIN, Pong
  35. // Unmasked, 0 payload
  36. buffer[0] = 128 + 0x0a;
  37. buffer[1] = 0 + 0x00;
  38. return buffer;
  39. }
  40. return null;
  41. };
  42. const _on_pong_frame = function() {
  43. let type = this.type;
  44. if (type === Composite.TYPE.client) {
  45. let buffer = Buffer.alloc(6);
  46. // FIN, Ping
  47. // Masked, 0 payload
  48. buffer[0] = 128 + 0x09;
  49. buffer[1] = 128 + 0x00;
  50. buffer[2] = (Math.random() * 0xff) | 0;
  51. buffer[3] = (Math.random() * 0xff) | 0;
  52. buffer[4] = (Math.random() * 0xff) | 0;
  53. buffer[5] = (Math.random() * 0xff) | 0;
  54. return buffer;
  55. }
  56. return null;
  57. };
  58. const _encode_buffer = function(payload, headers, binary) {
  59. let buffer = null;
  60. let data = _JSON.encode({
  61. headers: headers,
  62. payload: payload
  63. });
  64. let mask = false;
  65. let mask_data = null;
  66. let payload_data = null;
  67. let payload_length = data.length;
  68. let type = this.type;
  69. if (type === Composite.TYPE.client) {
  70. mask = true;
  71. mask_data = Buffer.alloc(4);
  72. mask_data[0] = (Math.random() * 0xff) | 0;
  73. mask_data[1] = (Math.random() * 0xff) | 0;
  74. mask_data[2] = (Math.random() * 0xff) | 0;
  75. mask_data[3] = (Math.random() * 0xff) | 0;
  76. payload_data = data.map((value, index) => value ^ mask_data[index % 4]);
  77. } else {
  78. mask = false;
  79. mask_data = Buffer.alloc(4);
  80. payload_data = data.map(value => value);
  81. }
  82. // 64 Bit Extended Payload Length
  83. if (payload_length > 0xffff) {
  84. let lo = payload_length | 0;
  85. let hi = (payload_length - lo) / 4294967296;
  86. buffer = Buffer.alloc((mask === true ? 14 : 10) + payload_length);
  87. buffer[0] = 128 + (binary === true ? 0x02 : 0x01);
  88. buffer[1] = (mask === true ? 128 : 0) + 127;
  89. buffer[2] = (hi >> 24) & 0xff;
  90. buffer[3] = (hi >> 16) & 0xff;
  91. buffer[4] = (hi >> 8) & 0xff;
  92. buffer[5] = (hi >> 0) & 0xff;
  93. buffer[6] = (lo >> 24) & 0xff;
  94. buffer[7] = (lo >> 16) & 0xff;
  95. buffer[8] = (lo >> 8) & 0xff;
  96. buffer[9] = (lo >> 0) & 0xff;
  97. if (mask === true) {
  98. mask_data.copy(buffer, 10);
  99. payload_data.copy(buffer, 14);
  100. } else {
  101. payload_data.copy(buffer, 10);
  102. }
  103. // 16 Bit Extended Payload Length
  104. } else if (payload_length > 125) {
  105. buffer = Buffer.alloc((mask === true ? 8 : 4) + payload_length);
  106. buffer[0] = 128 + (binary === true ? 0x02 : 0x01);
  107. buffer[1] = (mask === true ? 128 : 0) + 126;
  108. buffer[2] = (payload_length >> 8) & 0xff;
  109. buffer[3] = (payload_length >> 0) & 0xff;
  110. if (mask === true) {
  111. mask_data.copy(buffer, 4);
  112. payload_data.copy(buffer, 8);
  113. } else {
  114. payload_data.copy(buffer, 4);
  115. }
  116. // 7 Bit Payload Length
  117. } else {
  118. buffer = Buffer.alloc((mask === true ? 6 : 2) + payload_length);
  119. buffer[0] = 128 + (binary === true ? 0x02 : 0x01);
  120. buffer[1] = (mask === true ? 128 : 0) + payload_length;
  121. if (mask === true) {
  122. mask_data.copy(buffer, 2);
  123. payload_data.copy(buffer, 6);
  124. } else {
  125. payload_data.copy(buffer, 2);
  126. }
  127. }
  128. return buffer;
  129. };
  130. const _decode_buffer = function(buffer) {
  131. let fragment = this.__fragment;
  132. let chunk = {
  133. bytes: -1,
  134. headers: {},
  135. payload: null
  136. };
  137. if (buffer.length <= 2) {
  138. return chunk;
  139. }
  140. let fin = (buffer[0] & 128) === 128;
  141. // let rsv1 = (buffer[0] & 64) === 64;
  142. // let rsv2 = (buffer[0] & 32) === 32;
  143. // let rsv3 = (buffer[0] & 16) === 16;
  144. let operator = buffer[0] & 15;
  145. let mask = (buffer[1] & 128) === 128;
  146. let mask_data = Buffer.alloc(4);
  147. let payload_length = buffer[1] & 127;
  148. let payload_data = null;
  149. if (payload_length <= 125) {
  150. if (mask === true) {
  151. mask_data = buffer.slice(2, 6);
  152. payload_data = buffer.slice(6, 6 + payload_length);
  153. chunk.bytes = 6 + payload_length;
  154. } else {
  155. mask_data = null;
  156. payload_data = buffer.slice(2, 2 + payload_length);
  157. chunk.bytes = 2 + payload_length;
  158. }
  159. } else if (payload_length === 126) {
  160. payload_length = (buffer[2] << 8) + buffer[3];
  161. if (payload_length > buffer.length) {
  162. return chunk;
  163. }
  164. if (mask === true) {
  165. mask_data = buffer.slice(4, 8);
  166. payload_data = buffer.slice(8, 8 + payload_length);
  167. chunk.bytes = 8 + payload_length;
  168. } else {
  169. mask_data = null;
  170. payload_data = buffer.slice(4, 4 + payload_length);
  171. chunk.bytes = 4 + payload_length;
  172. }
  173. } else if (payload_length === 127) {
  174. let hi = (buffer[2] * 0x1000000) + ((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
  175. let lo = (buffer[6] * 0x1000000) + ((buffer[7] << 16) | (buffer[8] << 8) | buffer[9]);
  176. payload_length = (hi * 4294967296) + lo;
  177. if (payload_length > buffer.length) {
  178. return chunk;
  179. }
  180. if (mask === true) {
  181. mask_data = buffer.slice(10, 14);
  182. payload_data = buffer.slice(14, 14 + payload_length);
  183. chunk.bytes = 14 + payload_length;
  184. } else {
  185. mask_data = null;
  186. payload_data = buffer.slice(10, 10 + payload_length);
  187. chunk.bytes = 10 + payload_length;
  188. }
  189. }
  190. if (mask_data !== null) {
  191. payload_data = payload_data.map((value, index) => value ^ mask_data[index % 4]);
  192. }
  193. // 0: Continuation Frame (Fragmentation)
  194. if (operator === 0x00) {
  195. if (payload_data !== null) {
  196. let payload = Buffer.alloc(fragment.payload.length + payload_length);
  197. fragment.payload.copy(payload, 0);
  198. payload_data.copy(payload, fragment.payload.length);
  199. fragment.payload = payload;
  200. }
  201. if (fin === true) {
  202. let tmp0 = _JSON.decode(fragment.payload);
  203. if (tmp0 !== null) {
  204. chunk.headers = tmp0.headers || {};
  205. chunk.payload = tmp0.payload || null;
  206. }
  207. fragment.operator = 0x00;
  208. fragment.payload = Buffer.alloc(0);
  209. }
  210. // 1: Text Frame
  211. } else if (operator === 0x01) {
  212. if (fin === true) {
  213. let tmp1 = _JSON.decode(payload_data);
  214. if (tmp1 !== null) {
  215. chunk.headers = tmp1.headers || {};
  216. chunk.payload = tmp1.payload || null;
  217. }
  218. } else if (payload_data !== null) {
  219. let payload = Buffer.alloc(fragment.payload.length + payload_length);
  220. fragment.payload.copy(payload, 0);
  221. payload_data.copy(payload, fragment.payload.length);
  222. fragment.payload = payload;
  223. fragment.operator = operator;
  224. }
  225. // 2: Binary Frame
  226. } else if (operator === 0x02) {
  227. if (fin === true) {
  228. let tmp2 = _JSON.decode(payload_data);
  229. if (tmp2 !== null) {
  230. chunk.headers = tmp2.headers || {};
  231. chunk.payload = tmp2.payload || null;
  232. }
  233. } else if (payload_data !== null) {
  234. let payload = Buffer.alloc(fragment.payload.length + payload_length);
  235. fragment.payload.copy(payload, 0);
  236. payload_data.copy(payload, fragment.payload.length);
  237. fragment.payload = payload;
  238. fragment.operator = operator;
  239. }
  240. // 8: Connection Close
  241. } else if (operator === 0x08) {
  242. chunk.payload = this.close(Composite.STATUS.normal_closure);
  243. // 9: Ping Frame
  244. } else if (operator === 0x09) {
  245. chunk.payload = _on_ping_frame.call(this);
  246. // 10: Pong Frame
  247. } else if (operator === 0x0a) {
  248. chunk.payload = _on_pong_frame.call(this);
  249. // 3-7: Reserved Non-Control Frames, 11-15: Reserved Control Frames
  250. } else {
  251. chunk.payload = this.close(Composite.STATUS.protocol_error);
  252. }
  253. return chunk;
  254. };
  255. /*
  256. * IMPLEMENTATION
  257. */
  258. const Composite = function(data) {
  259. let states = Object.assign({}, data);
  260. this.type = lychee.enumof(Composite.TYPE, states.type) ? states.type : null;
  261. this.__buffer = Buffer.alloc(0);
  262. this.__fragment = {
  263. operator: 0x00,
  264. payload: Buffer.alloc(0)
  265. };
  266. this.__isClosed = false;
  267. if (lychee.debug === true) {
  268. if (this.type === null) {
  269. console.error('lychee.net.protocol.WS: Invalid (lychee.net.protocol.WS.TYPE) type.');
  270. }
  271. }
  272. states = null;
  273. };
  274. // Composite.FRAMESIZE = 32768; // 32kB
  275. Composite.FRAMESIZE = 0x800000; // 8MiB
  276. Composite.STATUS = {
  277. // IESG_HYBI
  278. normal_closure: 1000,
  279. protocol_error: 1002,
  280. message_too_big: 1009
  281. // IESG_HYBI
  282. // going_away: 1001,
  283. // unsupported_data: 1003,
  284. // no_status_received: 1005,
  285. // abnormal_closure: 1006,
  286. // invalid_payload: 1007,
  287. // policy_violation: 1008,
  288. // missing_extension: 1010,
  289. // internal_error: 1011,
  290. // IESG_HYBI Current
  291. // service_restart: 1012,
  292. // service_overload: 1013,
  293. // IESG_HYBI
  294. // tls_handshake: 1015
  295. };
  296. Composite.TYPE = {
  297. // 'default': 0, (deactivated)
  298. 'client': 1,
  299. 'remote': 2
  300. };
  301. Composite.prototype = {
  302. /*
  303. * ENTITY API
  304. */
  305. // deserialize: function(blob) {},
  306. serialize: function() {
  307. let states = {};
  308. if (this.type !== null) states.type = this.type;
  309. return {
  310. 'constructor': 'lychee.net.protocol.WS',
  311. 'arguments': [ states ],
  312. 'blob': null
  313. };
  314. },
  315. /*
  316. * PROTOCOL API
  317. */
  318. send: function(payload, headers, binary) {
  319. payload = payload instanceof Buffer ? payload : null;
  320. headers = headers instanceof Object ? headers : null;
  321. binary = binary === true;
  322. if (payload !== null) {
  323. if (this.__isClosed === false) {
  324. return _encode_buffer.call(this, payload, headers, binary);
  325. }
  326. }
  327. return null;
  328. },
  329. receive: function(blob) {
  330. blob = blob instanceof Buffer ? blob : null;
  331. let chunks = [];
  332. if (blob !== null) {
  333. if (blob.length > Composite.FRAMESIZE) {
  334. chunks.push({
  335. payload: this.close(Composite.STATUS.message_too_big)
  336. });
  337. } else if (this.__isClosed === false) {
  338. let buf = this.__buffer;
  339. let tmp = Buffer.alloc(buf.length + blob.length);
  340. buf.copy(tmp);
  341. blob.copy(tmp, buf.length);
  342. buf = tmp;
  343. let chunk = _decode_buffer.call(this, buf);
  344. while (chunk.bytes !== -1) {
  345. if (chunk.payload !== null) {
  346. chunks.push(chunk);
  347. }
  348. if (buf.length - chunk.bytes > 0) {
  349. tmp = Buffer.alloc(buf.length - chunk.bytes);
  350. buf.copy(tmp, 0, chunk.bytes);
  351. buf = tmp;
  352. } else {
  353. buf = Buffer.alloc(0);
  354. }
  355. chunk = null;
  356. chunk = _decode_buffer.call(this, buf);
  357. }
  358. this.__buffer = buf;
  359. }
  360. }
  361. return chunks;
  362. },
  363. close: function(status) {
  364. status = typeof status === 'number' ? status : Composite.STATUS.normal_closure;
  365. if (this.__isClosed === false) {
  366. let buffer = Buffer.alloc(4);
  367. buffer[0] = 128 + 0x08;
  368. buffer[1] = 0 + 0x02;
  369. buffer.write(String.fromCharCode((status >> 8) & 0xff) + String.fromCharCode((status >> 0) & 0xff), 2, 'binary');
  370. this.__isClosed = true;
  371. return buffer;
  372. }
  373. return null;
  374. },
  375. /*
  376. * CUSTOM API
  377. */
  378. ping: function() {
  379. let buffer = _on_pong_frame.call(this);
  380. if (buffer !== null) {
  381. return buffer;
  382. }
  383. return null;
  384. }
  385. };
  386. return Composite;
  387. });