Receiver.hixie.js 3.9 KB

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