socketioMEANseed/server/node_modules/ws/lib/Receiver.hixie.js
server/node_modules/ws/lib/Receiver.hixie.jsRaw
1/*!
2 * ws: a node.js websocket client
3 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
4 * MIT Licensed
5 */
6
7var util = require('util');
8
9/**
10 * State constants
11 */
12
13var EMPTY = 0
14 , BODY = 1;
15var BINARYLENGTH = 2
16 , BINARYBODY = 3;
17
18/**
19 * Hixie Receiver implementation
20 */
21
22function Receiver () {
23 if (this instanceof Receiver === false) {
24 throw new TypeError("Classes can't be function-called");
25 }
26
27 this.state = EMPTY;
28 this.buffers = [];
29 this.messageEnd = -1;
30 this.spanLength = 0;
31 this.dead = false;
32
33 this.onerror = function() {};
34 this.ontext = function() {};
35 this.onbinary = function() {};
36 this.onclose = function() {};
37 this.onping = function() {};
38 this.onpong = function() {};
39}
40
41module.exports = Receiver;
42
43/**
44 * Add new data to the parser.
45 *
46 * @api public
47 */
48
49Receiver.prototype.add = function(data) {
50 if (this.dead) return;
51 var self = this;
52 function doAdd() {
53 if (self.state === EMPTY) {
54 if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
55 self.reset();
56 self.onclose();
57 return;
58 }
59 if (data[0] === 0x80) {
60 self.messageEnd = 0;
61 self.state = BINARYLENGTH;
62 data = data.slice(1);
63 } else {
64
65 if (data[0] !== 0x00) {
66 self.error('payload must start with 0x00 byte', true);
67 return;
68 }
69 data = data.slice(1);
70 self.state = BODY;
71
72 }
73 }
74 if (self.state === BINARYLENGTH) {
75 var i = 0;
76 while ((i < data.length) && (data[i] & 0x80)) {
77 self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
78 ++i;
79 }
80 if (i < data.length) {
81 self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
82 self.state = BINARYBODY;
83 ++i;
84 }
85 if (i > 0)
86 data = data.slice(i);
87 }
88 if (self.state === BINARYBODY) {
89 var dataleft = self.messageEnd - self.spanLength;
90 if (data.length >= dataleft) {
91 // consume the whole buffer to finish the frame
92 self.buffers.push(data);
93 self.spanLength += dataleft;
94 self.messageEnd = dataleft;
95 return self.parse();
96 }
97 // frame's not done even if we consume it all
98 self.buffers.push(data);
99 self.spanLength += data.length;
100 return;
101 }
102 self.buffers.push(data);
103 if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
104 self.spanLength += self.messageEnd;
105 return self.parse();
106 }
107 else self.spanLength += data.length;
108 }
109 while(data) data = doAdd();
110};
111
112/**
113 * Releases all resources used by the receiver.
114 *
115 * @api public
116 */
117
118Receiver.prototype.cleanup = function() {
119 this.dead = true;
120 this.state = EMPTY;
121 this.buffers = [];
122};
123
124/**
125 * Process buffered data.
126 *
127 * @api public
128 */
129
130Receiver.prototype.parse = function() {
131 var output = new Buffer(this.spanLength);
132 var outputIndex = 0;
133 for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
134 var buffer = this.buffers[bi];
135 buffer.copy(output, outputIndex);
136 outputIndex += buffer.length;
137 }
138 var lastBuffer = this.buffers[this.buffers.length - 1];
139 if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
140 if (this.state !== BODY) --this.messageEnd;
141 var tail = null;
142 if (this.messageEnd < lastBuffer.length - 1) {
143 tail = lastBuffer.slice(this.messageEnd + 1);
144 }
145 this.reset();
146 this.ontext(output.toString('utf8'));
147 return tail;
148};
149
150/**
151 * Handles an error
152 *
153 * @api private
154 */
155
156Receiver.prototype.error = function (reason, terminate) {
157 if (this.dead) return;
158 this.reset();
159 if(typeof reason == 'string'){
160 this.onerror(new Error(reason), terminate);
161 }
162 else if(reason.constructor == Error){
163 this.onerror(reason, terminate);
164 }
165 else{
166 this.onerror(new Error("An error occured"),terminate);
167 }
168 return this;
169};
170
171/**
172 * Reset parser state
173 *
174 * @api private
175 */
176
177Receiver.prototype.reset = function (reason) {
178 if (this.dead) return;
179 this.state = EMPTY;
180 this.buffers = [];
181 this.messageEnd = -1;
182 this.spanLength = 0;
183};
184
185/**
186 * Internal api
187 */
188
189function bufferIndex(buffer, byte) {
190 for (var i = 0, l = buffer.length; i < l; ++i) {
191 if (buffer[i] === byte) return i;
192 }
193 return -1;
194}