123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- /*!
- * ws: a node.js websocket client
- * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
- * MIT Licensed
- */
-
- var util = require('util');
-
- /**
- * State constants
- */
-
- var EMPTY = 0
- , BODY = 1;
- var BINARYLENGTH = 2
- , BINARYBODY = 3;
-
- /**
- * Hixie Receiver implementation
- */
-
- function Receiver () {
- this.state = EMPTY;
- this.buffers = [];
- this.messageEnd = -1;
- this.spanLength = 0;
- this.dead = false;
-
- this.onerror = function() {};
- this.ontext = function() {};
- this.onbinary = function() {};
- this.onclose = function() {};
- this.onping = function() {};
- this.onpong = function() {};
- }
-
- module.exports = Receiver;
-
- /**
- * Add new data to the parser.
- *
- * @api public
- */
-
- Receiver.prototype.add = function(data) {
- var self = this;
- function doAdd() {
- if (self.state === EMPTY) {
- if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
- self.reset();
- self.onclose();
- return;
- }
- if (data[0] === 0x80) {
- self.messageEnd = 0;
- self.state = BINARYLENGTH;
- data = data.slice(1);
- } else {
-
- if (data[0] !== 0x00) {
- self.error('payload must start with 0x00 byte', true);
- return;
- }
- data = data.slice(1);
- self.state = BODY;
-
- }
- }
- if (self.state === BINARYLENGTH) {
- var i = 0;
- while ((i < data.length) && (data[i] & 0x80)) {
- self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
- ++i;
- }
- if (i < data.length) {
- self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
- self.state = BINARYBODY;
- ++i;
- }
- if (i > 0)
- data = data.slice(i);
- }
- if (self.state === BINARYBODY) {
- var dataleft = self.messageEnd - self.spanLength;
- if (data.length >= dataleft) {
- // consume the whole buffer to finish the frame
- self.buffers.push(data);
- self.spanLength += dataleft;
- self.messageEnd = dataleft;
- return self.parse();
- }
- // frame's not done even if we consume it all
- self.buffers.push(data);
- self.spanLength += data.length;
- return;
- }
- self.buffers.push(data);
- if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
- self.spanLength += self.messageEnd;
- return self.parse();
- }
- else self.spanLength += data.length;
- }
- while(data) data = doAdd();
- };
-
- /**
- * Releases all resources used by the receiver.
- *
- * @api public
- */
-
- Receiver.prototype.cleanup = function() {
- this.dead = true;
- this.state = EMPTY;
- this.buffers = [];
- };
-
- /**
- * Process buffered data.
- *
- * @api public
- */
-
- Receiver.prototype.parse = function() {
- var output = new Buffer(this.spanLength);
- var outputIndex = 0;
- for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
- var buffer = this.buffers[bi];
- buffer.copy(output, outputIndex);
- outputIndex += buffer.length;
- }
- var lastBuffer = this.buffers[this.buffers.length - 1];
- if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
- if (this.state !== BODY) --this.messageEnd;
- var tail = null;
- if (this.messageEnd < lastBuffer.length - 1) {
- tail = lastBuffer.slice(this.messageEnd + 1);
- }
- this.reset();
- this.ontext(output.toString('utf8'));
- return tail;
- };
-
- /**
- * Handles an error
- *
- * @api private
- */
-
- Receiver.prototype.error = function (reason, terminate) {
- this.reset();
- this.onerror(reason, terminate);
- return this;
- };
-
- /**
- * Reset parser state
- *
- * @api private
- */
-
- Receiver.prototype.reset = function (reason) {
- if (this.dead) return;
- this.state = EMPTY;
- this.buffers = [];
- this.messageEnd = -1;
- this.spanLength = 0;
- };
-
- /**
- * Internal api
- */
-
- function bufferIndex(buffer, byte) {
- for (var i = 0, l = buffer.length; i < l; ++i) {
- if (buffer[i] === byte) return i;
- }
- return -1;
- }
|