socketioMEANseed/server/node_modules/socket.io-parser/index.js
server/node_modules/socket.io-parser/index.jsRaw
1
2/**
3 * Module dependencies.
4 */
5
6var debug = require('debug')('socket.io-parser');
7var json = require('json3');
8var Emitter = require('component-emitter');
9var binary = require('./binary');
10var isBuf = require('./is-buffer');
11
12/**
13 * Protocol version.
14 *
15 * @api public
16 */
17
18exports.protocol = 4;
19
20/**
21 * Packet types.
22 *
23 * @api public
24 */
25
26exports.types = [
27 'CONNECT',
28 'DISCONNECT',
29 'EVENT',
30 'ACK',
31 'ERROR',
32 'BINARY_EVENT',
33 'BINARY_ACK'
34];
35
36/**
37 * Packet type `connect`.
38 *
39 * @api public
40 */
41
42exports.CONNECT = 0;
43
44/**
45 * Packet type `disconnect`.
46 *
47 * @api public
48 */
49
50exports.DISCONNECT = 1;
51
52/**
53 * Packet type `event`.
54 *
55 * @api public
56 */
57
58exports.EVENT = 2;
59
60/**
61 * Packet type `ack`.
62 *
63 * @api public
64 */
65
66exports.ACK = 3;
67
68/**
69 * Packet type `error`.
70 *
71 * @api public
72 */
73
74exports.ERROR = 4;
75
76/**
77 * Packet type 'binary event'
78 *
79 * @api public
80 */
81
82exports.BINARY_EVENT = 5;
83
84/**
85 * Packet type `binary ack`. For acks with binary arguments.
86 *
87 * @api public
88 */
89
90exports.BINARY_ACK = 6;
91
92/**
93 * Encoder constructor.
94 *
95 * @api public
96 */
97
98exports.Encoder = Encoder;
99
100/**
101 * Decoder constructor.
102 *
103 * @api public
104 */
105
106exports.Decoder = Decoder;
107
108/**
109 * A socket.io Encoder instance
110 *
111 * @api public
112 */
113
114function Encoder() {}
115
116/**
117 * Encode a packet as a single string if non-binary, or as a
118 * buffer sequence, depending on packet type.
119 *
120 * @param {Object} obj - packet object
121 * @param {Function} callback - function to handle encodings (likely engine.write)
122 * @return Calls callback with Array of encodings
123 * @api public
124 */
125
126Encoder.prototype.encode = function(obj, callback){
127 debug('encoding packet %j', obj);
128
129 if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
130 encodeAsBinary(obj, callback);
131 }
132 else {
133 var encoding = encodeAsString(obj);
134 callback([encoding]);
135 }
136};
137
138/**
139 * Encode packet as string.
140 *
141 * @param {Object} packet
142 * @return {String} encoded
143 * @api private
144 */
145
146function encodeAsString(obj) {
147 var str = '';
148 var nsp = false;
149
150 // first is type
151 str += obj.type;
152
153 // attachments if we have them
154 if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
155 str += obj.attachments;
156 str += '-';
157 }
158
159 // if we have a namespace other than `/`
160 // we append it followed by a comma `,`
161 if (obj.nsp && '/' != obj.nsp) {
162 nsp = true;
163 str += obj.nsp;
164 }
165
166 // immediately followed by the id
167 if (null != obj.id) {
168 if (nsp) {
169 str += ',';
170 nsp = false;
171 }
172 str += obj.id;
173 }
174
175 // json data
176 if (null != obj.data) {
177 if (nsp) str += ',';
178 str += json.stringify(obj.data);
179 }
180
181 debug('encoded %j as %s', obj, str);
182 return str;
183}
184
185/**
186 * Encode packet as 'buffer sequence' by removing blobs, and
187 * deconstructing packet into object with placeholders and
188 * a list of buffers.
189 *
190 * @param {Object} packet
191 * @return {Buffer} encoded
192 * @api private
193 */
194
195function encodeAsBinary(obj, callback) {
196
197 function writeEncoding(bloblessData) {
198 var deconstruction = binary.deconstructPacket(bloblessData);
199 var pack = encodeAsString(deconstruction.packet);
200 var buffers = deconstruction.buffers;
201
202 buffers.unshift(pack); // add packet info to beginning of data list
203 callback(buffers); // write all the buffers
204 }
205
206 binary.removeBlobs(obj, writeEncoding);
207}
208
209/**
210 * A socket.io Decoder instance
211 *
212 * @return {Object} decoder
213 * @api public
214 */
215
216function Decoder() {
217 this.reconstructor = null;
218}
219
220/**
221 * Mix in `Emitter` with Decoder.
222 */
223
224Emitter(Decoder.prototype);
225
226/**
227 * Decodes an ecoded packet string into packet JSON.
228 *
229 * @param {String} obj - encoded packet
230 * @return {Object} packet
231 * @api public
232 */
233
234Decoder.prototype.add = function(obj) {
235 var packet;
236 if ('string' == typeof obj) {
237 packet = decodeString(obj);
238 if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) { // binary packet's json
239 this.reconstructor = new BinaryReconstructor(packet);
240
241 // no attachments, labeled binary but no binary data to follow
242 if (this.reconstructor.reconPack.attachments === 0) {
243 this.emit('decoded', packet);
244 }
245 } else { // non-binary full packet
246 this.emit('decoded', packet);
247 }
248 }
249 else if (isBuf(obj) || obj.base64) { // raw binary data
250 if (!this.reconstructor) {
251 throw new Error('got binary data when not reconstructing a packet');
252 } else {
253 packet = this.reconstructor.takeBinaryData(obj);
254 if (packet) { // received final buffer
255 this.reconstructor = null;
256 this.emit('decoded', packet);
257 }
258 }
259 }
260 else {
261 throw new Error('Unknown type: ' + obj);
262 }
263};
264
265/**
266 * Decode a packet String (JSON data)
267 *
268 * @param {String} str
269 * @return {Object} packet
270 * @api private
271 */
272
273function decodeString(str) {
274 var p = {};
275 var i = 0;
276
277 // look up type
278 p.type = Number(str.charAt(0));
279 if (null == exports.types[p.type]) return error();
280
281 // look up attachments if type binary
282 if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) {
283 var buf = '';
284 while (str.charAt(++i) != '-') {
285 buf += str.charAt(i);
286 if (i == str.length) break;
287 }
288 if (buf != Number(buf) || str.charAt(i) != '-') {
289 throw new Error('Illegal attachments');
290 }
291 p.attachments = Number(buf);
292 }
293
294 // look up namespace (if any)
295 if ('/' == str.charAt(i + 1)) {
296 p.nsp = '';
297 while (++i) {
298 var c = str.charAt(i);
299 if (',' == c) break;
300 p.nsp += c;
301 if (i == str.length) break;
302 }
303 } else {
304 p.nsp = '/';
305 }
306
307 // look up id
308 var next = str.charAt(i + 1);
309 if ('' !== next && Number(next) == next) {
310 p.id = '';
311 while (++i) {
312 var c = str.charAt(i);
313 if (null == c || Number(c) != c) {
314 --i;
315 break;
316 }
317 p.id += str.charAt(i);
318 if (i == str.length) break;
319 }
320 p.id = Number(p.id);
321 }
322
323 // look up json data
324 if (str.charAt(++i)) {
325 p = tryParse(p, str.substr(i));
326 }
327
328 debug('decoded %s as %j', str, p);
329 return p;
330}
331
332function tryParse(p, str) {
333 try {
334 p.data = json.parse(str);
335 } catch(e){
336 return error();
337 }
338 return p;
339};
340
341/**
342 * Deallocates a parser's resources
343 *
344 * @api public
345 */
346
347Decoder.prototype.destroy = function() {
348 if (this.reconstructor) {
349 this.reconstructor.finishedReconstruction();
350 }
351};
352
353/**
354 * A manager of a binary event's 'buffer sequence'. Should
355 * be constructed whenever a packet of type BINARY_EVENT is
356 * decoded.
357 *
358 * @param {Object} packet
359 * @return {BinaryReconstructor} initialized reconstructor
360 * @api private
361 */
362
363function BinaryReconstructor(packet) {
364 this.reconPack = packet;
365 this.buffers = [];
366}
367
368/**
369 * Method to be called when binary data received from connection
370 * after a BINARY_EVENT packet.
371 *
372 * @param {Buffer | ArrayBuffer} binData - the raw binary data received
373 * @return {null | Object} returns null if more binary data is expected or
374 * a reconstructed packet object if all buffers have been received.
375 * @api private
376 */
377
378BinaryReconstructor.prototype.takeBinaryData = function(binData) {
379 this.buffers.push(binData);
380 if (this.buffers.length == this.reconPack.attachments) { // done with buffer list
381 var packet = binary.reconstructPacket(this.reconPack, this.buffers);
382 this.finishedReconstruction();
383 return packet;
384 }
385 return null;
386};
387
388/**
389 * Cleans up binary packet reconstruction variables.
390 *
391 * @api private
392 */
393
394BinaryReconstructor.prototype.finishedReconstruction = function() {
395 this.reconPack = null;
396 this.buffers = [];
397};
398
399function error(data){
400 return {
401 type: exports.ERROR,
402 data: 'parser error'
403 };
404}