2 * ws: a node.js websocket client
3 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
7 var util = require('util');
19 * Hixie Receiver implementation
22 function Receiver () {
29 this.onerror = function() {};
30 this.ontext = function() {};
31 this.onbinary = function() {};
32 this.onclose = function() {};
33 this.onping = function() {};
34 this.onpong = function() {};
37 module.exports = Receiver;
40 * Add new data to the parser.
45 Receiver.prototype.add = function(data) {
48 if (self.state === EMPTY) {
49 if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
54 if (data[0] === 0x80) {
56 self.state = BINARYLENGTH;
60 if (data[0] !== 0x00) {
61 self.error('payload must start with 0x00 byte', true);
69 if (self.state === BINARYLENGTH) {
71 while ((i < data.length) && (data[i] & 0x80)) {
72 self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
75 if (i < data.length) {
76 self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
77 self.state = BINARYBODY;
83 if (self.state === BINARYBODY) {
84 var dataleft = self.messageEnd - self.spanLength;
85 if (data.length >= dataleft) {
86 // consume the whole buffer to finish the frame
87 self.buffers.push(data);
88 self.spanLength += dataleft;
89 self.messageEnd = dataleft;
92 // frame's not done even if we consume it all
93 self.buffers.push(data);
94 self.spanLength += data.length;
97 self.buffers.push(data);
98 if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
99 self.spanLength += self.messageEnd;
102 else self.spanLength += data.length;
104 while(data) data = doAdd();
108 * Releases all resources used by the receiver.
113 Receiver.prototype.cleanup = function() {
120 * Process buffered data.
125 Receiver.prototype.parse = function() {
126 var output = new Buffer(this.spanLength);
128 for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
129 var buffer = this.buffers[bi];
130 buffer.copy(output, outputIndex);
131 outputIndex += buffer.length;
133 var lastBuffer = this.buffers[this.buffers.length - 1];
134 if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
135 if (this.state !== BODY) --this.messageEnd;
137 if (this.messageEnd < lastBuffer.length - 1) {
138 tail = lastBuffer.slice(this.messageEnd + 1);
141 this.ontext(output.toString('utf8'));
151 Receiver.prototype.error = function (reason, terminate) {
153 this.onerror(reason, terminate);
163 Receiver.prototype.reset = function (reason) {
164 if (this.dead) return;
167 this.messageEnd = -1;
175 function bufferIndex(buffer, byte) {
176 for (var i = 0, l = buffer.length; i < l; ++i) {
177 if (buffer[i] === byte) return i;