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.

180 lines
3.8 KiB

  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. /**
  8. * State constants
  9. */
  10. var EMPTY = 0
  11. , BODY = 1;
  12. var BINARYLENGTH = 2
  13. , BINARYBODY = 3;
  14. /**
  15. * Hixie Receiver implementation
  16. */
  17. function Receiver () {
  18. this.state = EMPTY;
  19. this.buffers = [];
  20. this.messageEnd = -1;
  21. this.spanLength = 0;
  22. this.dead = false;
  23. this.onerror = function() {};
  24. this.ontext = function() {};
  25. this.onbinary = function() {};
  26. this.onclose = function() {};
  27. this.onping = function() {};
  28. this.onpong = function() {};
  29. }
  30. module.exports = Receiver;
  31. /**
  32. * Add new data to the parser.
  33. *
  34. * @api public
  35. */
  36. Receiver.prototype.add = function(data) {
  37. var self = this;
  38. function doAdd() {
  39. if (self.state === EMPTY) {
  40. if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
  41. self.reset();
  42. self.onclose();
  43. return;
  44. }
  45. if (data[0] === 0x80) {
  46. self.messageEnd = 0;
  47. self.state = BINARYLENGTH;
  48. data = data.slice(1);
  49. } else {
  50. if (data[0] !== 0x00) {
  51. self.error('payload must start with 0x00 byte', true);
  52. return;
  53. }
  54. data = data.slice(1);
  55. self.state = BODY;
  56. }
  57. }
  58. if (self.state === BINARYLENGTH) {
  59. var i = 0;
  60. while ((i < data.length) && (data[i] & 0x80)) {
  61. self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
  62. ++i;
  63. }
  64. if (i < data.length) {
  65. self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
  66. self.state = BINARYBODY;
  67. ++i;
  68. }
  69. if (i > 0)
  70. data = data.slice(i);
  71. }
  72. if (self.state === BINARYBODY) {
  73. var dataleft = self.messageEnd - self.spanLength;
  74. if (data.length >= dataleft) {
  75. // consume the whole buffer to finish the frame
  76. self.buffers.push(data);
  77. self.spanLength += dataleft;
  78. self.messageEnd = dataleft;
  79. return self.parse();
  80. }
  81. // frame's not done even if we consume it all
  82. self.buffers.push(data);
  83. self.spanLength += data.length;
  84. return;
  85. }
  86. self.buffers.push(data);
  87. if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
  88. self.spanLength += self.messageEnd;
  89. return self.parse();
  90. }
  91. else self.spanLength += data.length;
  92. }
  93. while(data) data = doAdd();
  94. }
  95. /**
  96. * Releases all resources used by the receiver.
  97. *
  98. * @api public
  99. */
  100. Receiver.prototype.cleanup = function() {
  101. this.dead = true;
  102. this.state = EMPTY;
  103. this.buffers = [];
  104. }
  105. /**
  106. * Process buffered data.
  107. *
  108. * @api public
  109. */
  110. Receiver.prototype.parse = function() {
  111. var output = new Buffer(this.spanLength);
  112. var outputIndex = 0;
  113. for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
  114. var buffer = this.buffers[bi];
  115. buffer.copy(output, outputIndex);
  116. outputIndex += buffer.length;
  117. }
  118. var lastBuffer = this.buffers[this.buffers.length - 1];
  119. if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
  120. if (this.state !== BODY) --this.messageEnd;
  121. var tail = null;
  122. if (this.messageEnd < lastBuffer.length - 1) {
  123. tail = lastBuffer.slice(this.messageEnd + 1);
  124. }
  125. this.reset();
  126. this.ontext(output.toString('utf8'));
  127. return tail;
  128. }
  129. /**
  130. * Handles an error
  131. *
  132. * @api private
  133. */
  134. Receiver.prototype.error = function (reason, terminate) {
  135. this.reset();
  136. this.onerror(reason, terminate);
  137. return this;
  138. }
  139. /**
  140. * Reset parser state
  141. *
  142. * @api private
  143. */
  144. Receiver.prototype.reset = function (reason) {
  145. if (this.dead) return;
  146. this.state = EMPTY;
  147. this.buffers = [];
  148. this.messageEnd = -1;
  149. this.spanLength = 0;
  150. }
  151. /**
  152. * Internal api
  153. */
  154. function bufferIndex(buffer, byte) {
  155. for (var i = 0, l = buffer.length; i < l; ++i) {
  156. if (buffer[i] === byte) return i;
  157. }
  158. return -1;
  159. }