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.

793 lines
23 KiB

7 years ago
  1. /*!
  2. * ws: a node.js websocket client
  3. * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
  4. * MIT Licensed
  5. */
  6. var util = require('util')
  7. , Validation = require('./Validation').Validation
  8. , ErrorCodes = require('./ErrorCodes')
  9. , BufferPool = require('./BufferPool')
  10. , bufferUtil = require('./BufferUtil').BufferUtil
  11. , PerMessageDeflate = require('./PerMessageDeflate');
  12. /**
  13. * HyBi Receiver implementation
  14. */
  15. function Receiver (extensions,maxPayload) {
  16. if (this instanceof Receiver === false) {
  17. throw new TypeError("Classes can't be function-called");
  18. }
  19. if(typeof extensions==='number'){
  20. maxPayload=extensions;
  21. extensions={};
  22. }
  23. // memory pool for fragmented messages
  24. var fragmentedPoolPrevUsed = -1;
  25. this.fragmentedBufferPool = new BufferPool(1024, function(db, length) {
  26. return db.used + length;
  27. }, function(db) {
  28. return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ?
  29. Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) :
  30. db.used;
  31. });
  32. // memory pool for unfragmented messages
  33. var unfragmentedPoolPrevUsed = -1;
  34. this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) {
  35. return db.used + length;
  36. }, function(db) {
  37. return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ?
  38. Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) :
  39. db.used;
  40. });
  41. this.extensions = extensions || {};
  42. this.maxPayload = maxPayload || 0;
  43. this.currentPayloadLength = 0;
  44. this.state = {
  45. activeFragmentedOperation: null,
  46. lastFragment: false,
  47. masked: false,
  48. opcode: 0,
  49. fragmentedOperation: false
  50. };
  51. this.overflow = [];
  52. this.headerBuffer = new Buffer(10);
  53. this.expectOffset = 0;
  54. this.expectBuffer = null;
  55. this.expectHandler = null;
  56. this.currentMessage = [];
  57. this.currentMessageLength = 0;
  58. this.messageHandlers = [];
  59. this.expectHeader(2, this.processPacket);
  60. this.dead = false;
  61. this.processing = false;
  62. this.onerror = function() {};
  63. this.ontext = function() {};
  64. this.onbinary = function() {};
  65. this.onclose = function() {};
  66. this.onping = function() {};
  67. this.onpong = function() {};
  68. }
  69. module.exports = Receiver;
  70. /**
  71. * Add new data to the parser.
  72. *
  73. * @api public
  74. */
  75. Receiver.prototype.add = function(data) {
  76. if (this.dead) return;
  77. var dataLength = data.length;
  78. if (dataLength == 0) return;
  79. if (this.expectBuffer == null) {
  80. this.overflow.push(data);
  81. return;
  82. }
  83. var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset);
  84. fastCopy(toRead, data, this.expectBuffer, this.expectOffset);
  85. this.expectOffset += toRead;
  86. if (toRead < dataLength) {
  87. this.overflow.push(data.slice(toRead));
  88. }
  89. while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) {
  90. var bufferForHandler = this.expectBuffer;
  91. this.expectBuffer = null;
  92. this.expectOffset = 0;
  93. this.expectHandler.call(this, bufferForHandler);
  94. }
  95. };
  96. /**
  97. * Releases all resources used by the receiver.
  98. *
  99. * @api public
  100. */
  101. Receiver.prototype.cleanup = function() {
  102. this.dead = true;
  103. this.overflow = null;
  104. this.headerBuffer = null;
  105. this.expectBuffer = null;
  106. this.expectHandler = null;
  107. this.unfragmentedBufferPool = null;
  108. this.fragmentedBufferPool = null;
  109. this.state = null;
  110. this.currentMessage = null;
  111. this.onerror = null;
  112. this.ontext = null;
  113. this.onbinary = null;
  114. this.onclose = null;
  115. this.onping = null;
  116. this.onpong = null;
  117. };
  118. /**
  119. * Waits for a certain amount of header bytes to be available, then fires a callback.
  120. *
  121. * @api private
  122. */
  123. Receiver.prototype.expectHeader = function(length, handler) {
  124. if (length == 0) {
  125. handler(null);
  126. return;
  127. }
  128. this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length);
  129. this.expectHandler = handler;
  130. var toRead = length;
  131. while (toRead > 0 && this.overflow.length > 0) {
  132. var fromOverflow = this.overflow.pop();
  133. if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
  134. var read = Math.min(fromOverflow.length, toRead);
  135. fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
  136. this.expectOffset += read;
  137. toRead -= read;
  138. }
  139. };
  140. /**
  141. * Waits for a certain amount of data bytes to be available, then fires a callback.
  142. *
  143. * @api private
  144. */
  145. Receiver.prototype.expectData = function(length, handler) {
  146. if (length == 0) {
  147. handler(null);
  148. return;
  149. }
  150. this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
  151. this.expectHandler = handler;
  152. var toRead = length;
  153. while (toRead > 0 && this.overflow.length > 0) {
  154. var fromOverflow = this.overflow.pop();
  155. if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
  156. var read = Math.min(fromOverflow.length, toRead);
  157. fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
  158. this.expectOffset += read;
  159. toRead -= read;
  160. }
  161. };
  162. /**
  163. * Allocates memory from the buffer pool.
  164. *
  165. * @api private
  166. */
  167. Receiver.prototype.allocateFromPool = function(length, isFragmented) {
  168. return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length);
  169. };
  170. /**
  171. * Start processing a new packet.
  172. *
  173. * @api private
  174. */
  175. Receiver.prototype.processPacket = function (data) {
  176. if (this.extensions[PerMessageDeflate.extensionName]) {
  177. if ((data[0] & 0x30) != 0) {
  178. this.error('reserved fields (2, 3) must be empty', 1002);
  179. return;
  180. }
  181. } else {
  182. if ((data[0] & 0x70) != 0) {
  183. this.error('reserved fields must be empty', 1002);
  184. return;
  185. }
  186. }
  187. this.state.lastFragment = (data[0] & 0x80) == 0x80;
  188. this.state.masked = (data[1] & 0x80) == 0x80;
  189. var compressed = (data[0] & 0x40) == 0x40;
  190. var opcode = data[0] & 0xf;
  191. if (opcode === 0) {
  192. if (compressed) {
  193. this.error('continuation frame cannot have the Per-message Compressed bits', 1002);
  194. return;
  195. }
  196. // continuation frame
  197. this.state.fragmentedOperation = true;
  198. this.state.opcode = this.state.activeFragmentedOperation;
  199. if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
  200. this.error('continuation frame cannot follow current opcode', 1002);
  201. return;
  202. }
  203. }
  204. else {
  205. if (opcode < 3 && this.state.activeFragmentedOperation != null) {
  206. this.error('data frames after the initial data frame must have opcode 0', 1002);
  207. return;
  208. }
  209. if (opcode >= 8 && compressed) {
  210. this.error('control frames cannot have the Per-message Compressed bits', 1002);
  211. return;
  212. }
  213. this.state.compressed = compressed;
  214. this.state.opcode = opcode;
  215. if (this.state.lastFragment === false) {
  216. this.state.fragmentedOperation = true;
  217. this.state.activeFragmentedOperation = opcode;
  218. }
  219. else this.state.fragmentedOperation = false;
  220. }
  221. var handler = opcodes[this.state.opcode];
  222. if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002);
  223. else {
  224. handler.start.call(this, data);
  225. }
  226. };
  227. /**
  228. * Endprocessing a packet.
  229. *
  230. * @api private
  231. */
  232. Receiver.prototype.endPacket = function() {
  233. if (this.dead) return;
  234. if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
  235. else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true);
  236. this.expectOffset = 0;
  237. this.expectBuffer = null;
  238. this.expectHandler = null;
  239. if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) {
  240. // end current fragmented operation
  241. this.state.activeFragmentedOperation = null;
  242. }
  243. this.currentPayloadLength = 0;
  244. this.state.lastFragment = false;
  245. this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
  246. this.state.masked = false;
  247. this.expectHeader(2, this.processPacket);
  248. };
  249. /**
  250. * Reset the parser state.
  251. *
  252. * @api private
  253. */
  254. Receiver.prototype.reset = function() {
  255. if (this.dead) return;
  256. this.state = {
  257. activeFragmentedOperation: null,
  258. lastFragment: false,
  259. masked: false,
  260. opcode: 0,
  261. fragmentedOperation: false
  262. };
  263. this.fragmentedBufferPool.reset(true);
  264. this.unfragmentedBufferPool.reset(true);
  265. this.expectOffset = 0;
  266. this.expectBuffer = null;
  267. this.expectHandler = null;
  268. this.overflow = [];
  269. this.currentMessage = [];
  270. this.currentMessageLength = 0;
  271. this.messageHandlers = [];
  272. this.currentPayloadLength = 0;
  273. };
  274. /**
  275. * Unmask received data.
  276. *
  277. * @api private
  278. */
  279. Receiver.prototype.unmask = function (mask, buf, binary) {
  280. if (mask != null && buf != null) bufferUtil.unmask(buf, mask);
  281. if (binary) return buf;
  282. return buf != null ? buf.toString('utf8') : '';
  283. };
  284. /**
  285. * Handles an error
  286. *
  287. * @api private
  288. */
  289. Receiver.prototype.error = function (reason, protocolErrorCode) {
  290. if (this.dead) return;
  291. this.reset();
  292. if(typeof reason == 'string'){
  293. this.onerror(new Error(reason), protocolErrorCode);
  294. }
  295. else if(reason.constructor == Error){
  296. this.onerror(reason, protocolErrorCode);
  297. }
  298. else{
  299. this.onerror(new Error("An error occured"),protocolErrorCode);
  300. }
  301. return this;
  302. };
  303. /**
  304. * Execute message handler buffers
  305. *
  306. * @api private
  307. */
  308. Receiver.prototype.flush = function() {
  309. if (this.processing || this.dead) return;
  310. var handler = this.messageHandlers.shift();
  311. if (!handler) return;
  312. this.processing = true;
  313. var self = this;
  314. handler(function() {
  315. self.processing = false;
  316. self.flush();
  317. });
  318. };
  319. /**
  320. * Apply extensions to message
  321. *
  322. * @api private
  323. */
  324. Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) {
  325. var self = this;
  326. if (compressed) {
  327. this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) {
  328. if (self.dead) return;
  329. if (err) {
  330. callback(new Error('invalid compressed data'));
  331. return;
  332. }
  333. callback(null, buffer);
  334. });
  335. } else {
  336. callback(null, messageBuffer);
  337. }
  338. };
  339. /**
  340. * Checks payload size, disconnects socket when it exceeds maxPayload
  341. *
  342. * @api private
  343. */
  344. Receiver.prototype.maxPayloadExceeded = function(length) {
  345. if (this.maxPayload=== undefined || this.maxPayload === null || this.maxPayload < 1) {
  346. return false;
  347. }
  348. var fullLength = this.currentPayloadLength + length;
  349. if (fullLength < this.maxPayload) {
  350. this.currentPayloadLength = fullLength;
  351. return false;
  352. }
  353. this.error('payload cannot exceed ' + this.maxPayload + ' bytes', 1009);
  354. this.messageBuffer=[];
  355. this.cleanup();
  356. return true;
  357. };
  358. /**
  359. * Buffer utilities
  360. */
  361. function readUInt16BE(start) {
  362. return (this[start]<<8) +
  363. this[start+1];
  364. }
  365. function readUInt32BE(start) {
  366. return (this[start]<<24) +
  367. (this[start+1]<<16) +
  368. (this[start+2]<<8) +
  369. this[start+3];
  370. }
  371. function fastCopy(length, srcBuffer, dstBuffer, dstOffset) {
  372. switch (length) {
  373. default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break;
  374. case 16: dstBuffer[dstOffset+15] = srcBuffer[15];
  375. case 15: dstBuffer[dstOffset+14] = srcBuffer[14];
  376. case 14: dstBuffer[dstOffset+13] = srcBuffer[13];
  377. case 13: dstBuffer[dstOffset+12] = srcBuffer[12];
  378. case 12: dstBuffer[dstOffset+11] = srcBuffer[11];
  379. case 11: dstBuffer[dstOffset+10] = srcBuffer[10];
  380. case 10: dstBuffer[dstOffset+9] = srcBuffer[9];
  381. case 9: dstBuffer[dstOffset+8] = srcBuffer[8];
  382. case 8: dstBuffer[dstOffset+7] = srcBuffer[7];
  383. case 7: dstBuffer[dstOffset+6] = srcBuffer[6];
  384. case 6: dstBuffer[dstOffset+5] = srcBuffer[5];
  385. case 5: dstBuffer[dstOffset+4] = srcBuffer[4];
  386. case 4: dstBuffer[dstOffset+3] = srcBuffer[3];
  387. case 3: dstBuffer[dstOffset+2] = srcBuffer[2];
  388. case 2: dstBuffer[dstOffset+1] = srcBuffer[1];
  389. case 1: dstBuffer[dstOffset] = srcBuffer[0];
  390. }
  391. }
  392. function clone(obj) {
  393. var cloned = {};
  394. for (var k in obj) {
  395. if (obj.hasOwnProperty(k)) {
  396. cloned[k] = obj[k];
  397. }
  398. }
  399. return cloned;
  400. }
  401. /**
  402. * Opcode handlers
  403. */
  404. var opcodes = {
  405. // text
  406. '1': {
  407. start: function(data) {
  408. var self = this;
  409. // decode length
  410. var firstLength = data[1] & 0x7f;
  411. if (firstLength < 126) {
  412. if (self.maxPayloadExceeded(firstLength)){
  413. self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
  414. return;
  415. }
  416. opcodes['1'].getData.call(self, firstLength);
  417. }
  418. else if (firstLength == 126) {
  419. self.expectHeader(2, function(data) {
  420. var length = readUInt16BE.call(data, 0);
  421. if (self.maxPayloadExceeded(length)){
  422. self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
  423. return;
  424. }
  425. opcodes['1'].getData.call(self, length);
  426. });
  427. }
  428. else if (firstLength == 127) {
  429. self.expectHeader(8, function(data) {
  430. if (readUInt32BE.call(data, 0) != 0) {
  431. self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
  432. return;
  433. }
  434. var length = readUInt32BE.call(data, 4);
  435. if (self.maxPayloadExceeded(length)){
  436. self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
  437. return;
  438. }
  439. opcodes['1'].getData.call(self, readUInt32BE.call(data, 4));
  440. });
  441. }
  442. },
  443. getData: function(length) {
  444. var self = this;
  445. if (self.state.masked) {
  446. self.expectHeader(4, function(data) {
  447. var mask = data;
  448. self.expectData(length, function(data) {
  449. opcodes['1'].finish.call(self, mask, data);
  450. });
  451. });
  452. }
  453. else {
  454. self.expectData(length, function(data) {
  455. opcodes['1'].finish.call(self, null, data);
  456. });
  457. }
  458. },
  459. finish: function(mask, data) {
  460. var self = this;
  461. var packet = this.unmask(mask, data, true) || new Buffer(0);
  462. var state = clone(this.state);
  463. this.messageHandlers.push(function(callback) {
  464. self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
  465. if (err) {
  466. if(err.type===1009){
  467. return self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
  468. }
  469. return self.error(err.message, 1007);
  470. }
  471. if (buffer != null) {
  472. if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){
  473. self.currentMessage.push(buffer);
  474. }
  475. else{
  476. self.currentMessage=null;
  477. self.currentMessage = [];
  478. self.currentMessageLength = 0;
  479. self.error(new Error('Maximum payload exceeded. maxPayload: '+self.maxPayload), 1009);
  480. return;
  481. }
  482. self.currentMessageLength += buffer.length;
  483. }
  484. if (state.lastFragment) {
  485. var messageBuffer = Buffer.concat(self.currentMessage);
  486. self.currentMessage = [];
  487. self.currentMessageLength = 0;
  488. if (!Validation.isValidUTF8(messageBuffer)) {
  489. self.error('invalid utf8 sequence', 1007);
  490. return;
  491. }
  492. self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer});
  493. }
  494. callback();
  495. });
  496. });
  497. this.flush();
  498. this.endPacket();
  499. }
  500. },
  501. // binary
  502. '2': {
  503. start: function(data) {
  504. var self = this;
  505. // decode length
  506. var firstLength = data[1] & 0x7f;
  507. if (firstLength < 126) {
  508. if (self.maxPayloadExceeded(firstLength)){
  509. self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
  510. return;
  511. }
  512. opcodes['2'].getData.call(self, firstLength);
  513. }
  514. else if (firstLength == 126) {
  515. self.expectHeader(2, function(data) {
  516. var length = readUInt16BE.call(data, 0);
  517. if (self.maxPayloadExceeded(length)){
  518. self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
  519. return;
  520. }
  521. opcodes['2'].getData.call(self, length);
  522. });
  523. }
  524. else if (firstLength == 127) {
  525. self.expectHeader(8, function(data) {
  526. if (readUInt32BE.call(data, 0) != 0) {
  527. self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
  528. return;
  529. }
  530. var length = readUInt32BE.call(data, 4, true);
  531. if (self.maxPayloadExceeded(length)){
  532. self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
  533. return;
  534. }
  535. opcodes['2'].getData.call(self, length);
  536. });
  537. }
  538. },
  539. getData: function(length) {
  540. var self = this;
  541. if (self.state.masked) {
  542. self.expectHeader(4, function(data) {
  543. var mask = data;
  544. self.expectData(length, function(data) {
  545. opcodes['2'].finish.call(self, mask, data);
  546. });
  547. });
  548. }
  549. else {
  550. self.expectData(length, function(data) {
  551. opcodes['2'].finish.call(self, null, data);
  552. });
  553. }
  554. },
  555. finish: function(mask, data) {
  556. var self = this;
  557. var packet = this.unmask(mask, data, true) || new Buffer(0);
  558. var state = clone(this.state);
  559. this.messageHandlers.push(function(callback) {
  560. self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
  561. if (err) {
  562. if(err.type===1009){
  563. return self.error('Max payload exceeded in compressed binary message. Aborting...', 1009);
  564. }
  565. return self.error(err.message, 1007);
  566. }
  567. if (buffer != null) {
  568. if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){
  569. self.currentMessage.push(buffer);
  570. }
  571. else{
  572. self.currentMessage=null;
  573. self.currentMessage = [];
  574. self.currentMessageLength = 0;
  575. self.error(new Error('Maximum payload exceeded'), 1009);
  576. return;
  577. }
  578. self.currentMessageLength += buffer.length;
  579. }
  580. if (state.lastFragment) {
  581. var messageBuffer = Buffer.concat(self.currentMessage);
  582. self.currentMessage = [];
  583. self.currentMessageLength = 0;
  584. self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer});
  585. }
  586. callback();
  587. });
  588. });
  589. this.flush();
  590. this.endPacket();
  591. }
  592. },
  593. // close
  594. '8': {
  595. start: function(data) {
  596. var self = this;
  597. if (self.state.lastFragment == false) {
  598. self.error('fragmented close is not supported', 1002);
  599. return;
  600. }
  601. // decode length
  602. var firstLength = data[1] & 0x7f;
  603. if (firstLength < 126) {
  604. opcodes['8'].getData.call(self, firstLength);
  605. }
  606. else {
  607. self.error('control frames cannot have more than 125 bytes of data', 1002);
  608. }
  609. },
  610. getData: function(length) {
  611. var self = this;
  612. if (self.state.masked) {
  613. self.expectHeader(4, function(data) {
  614. var mask = data;
  615. self.expectData(length, function(data) {
  616. opcodes['8'].finish.call(self, mask, data);
  617. });
  618. });
  619. }
  620. else {
  621. self.expectData(length, function(data) {
  622. opcodes['8'].finish.call(self, null, data);
  623. });
  624. }
  625. },
  626. finish: function(mask, data) {
  627. var self = this;
  628. data = self.unmask(mask, data, true);
  629. var state = clone(this.state);
  630. this.messageHandlers.push(function() {
  631. if (data && data.length == 1) {
  632. self.error('close packets with data must be at least two bytes long', 1002);
  633. return;
  634. }
  635. var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000;
  636. if (!ErrorCodes.isValidErrorCode(code)) {
  637. self.error('invalid error code', 1002);
  638. return;
  639. }
  640. var message = '';
  641. if (data && data.length > 2) {
  642. var messageBuffer = data.slice(2);
  643. if (!Validation.isValidUTF8(messageBuffer)) {
  644. self.error('invalid utf8 sequence', 1007);
  645. return;
  646. }
  647. message = messageBuffer.toString('utf8');
  648. }
  649. self.onclose(code, message, {masked: state.masked});
  650. self.reset();
  651. });
  652. this.flush();
  653. },
  654. },
  655. // ping
  656. '9': {
  657. start: function(data) {
  658. var self = this;
  659. if (self.state.lastFragment == false) {
  660. self.error('fragmented ping is not supported', 1002);
  661. return;
  662. }
  663. // decode length
  664. var firstLength = data[1] & 0x7f;
  665. if (firstLength < 126) {
  666. opcodes['9'].getData.call(self, firstLength);
  667. }
  668. else {
  669. self.error('control frames cannot have more than 125 bytes of data', 1002);
  670. }
  671. },
  672. getData: function(length) {
  673. var self = this;
  674. if (self.state.masked) {
  675. self.expectHeader(4, function(data) {
  676. var mask = data;
  677. self.expectData(length, function(data) {
  678. opcodes['9'].finish.call(self, mask, data);
  679. });
  680. });
  681. }
  682. else {
  683. self.expectData(length, function(data) {
  684. opcodes['9'].finish.call(self, null, data);
  685. });
  686. }
  687. },
  688. finish: function(mask, data) {
  689. var self = this;
  690. data = this.unmask(mask, data, true);
  691. var state = clone(this.state);
  692. this.messageHandlers.push(function(callback) {
  693. self.onping(data, {masked: state.masked, binary: true});
  694. callback();
  695. });
  696. this.flush();
  697. this.endPacket();
  698. }
  699. },
  700. // pong
  701. '10': {
  702. start: function(data) {
  703. var self = this;
  704. if (self.state.lastFragment == false) {
  705. self.error('fragmented pong is not supported', 1002);
  706. return;
  707. }
  708. // decode length
  709. var firstLength = data[1] & 0x7f;
  710. if (firstLength < 126) {
  711. opcodes['10'].getData.call(self, firstLength);
  712. }
  713. else {
  714. self.error('control frames cannot have more than 125 bytes of data', 1002);
  715. }
  716. },
  717. getData: function(length) {
  718. var self = this;
  719. if (this.state.masked) {
  720. this.expectHeader(4, function(data) {
  721. var mask = data;
  722. self.expectData(length, function(data) {
  723. opcodes['10'].finish.call(self, mask, data);
  724. });
  725. });
  726. }
  727. else {
  728. this.expectData(length, function(data) {
  729. opcodes['10'].finish.call(self, null, data);
  730. });
  731. }
  732. },
  733. finish: function(mask, data) {
  734. var self = this;
  735. data = self.unmask(mask, data, true);
  736. var state = clone(this.state);
  737. this.messageHandlers.push(function(callback) {
  738. self.onpong(data, {masked: state.masked, binary: true});
  739. callback();
  740. });
  741. this.flush();
  742. this.endPacket();
  743. }
  744. }
  745. }