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