comunicationLeap/node_modules/engine.io-parser/lib/index.js
node_modules/engine.io-parser/lib/index.jsRaw
1/**
2 * Module dependencies.
3 */
4
5var utf8 = require('utf8');
6var after = require('after');
7var keys = require('./keys');
8
9/**
10 * Current protocol version.
11 */
12exports.protocol = 3;
13
14/**
15 * Packet types.
16 */
17
18var packets = exports.packets = {
19 open: 0 // non-ws
20 , close: 1 // non-ws
21 , ping: 2
22 , pong: 3
23 , message: 4
24 , upgrade: 5
25 , noop: 6
26};
27
28var packetslist = keys(packets);
29
30/**
31 * Premade error packet.
32 */
33
34var err = { type: 'error', data: 'parser error' };
35
36/**
37 * Encodes a packet.
38 *
39 * <packet type id> [ <data> ]
40 *
41 * Example:
42 *
43 * 5hello world
44 * 3
45 * 4
46 *
47 * Binary is encoded in an identical principle
48 *
49 * @api private
50 */
51
52exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
53 if ('function' == typeof supportsBinary) {
54 callback = supportsBinary;
55 supportsBinary = null;
56 }
57
58 if ('function' == typeof utf8encode ) {
59 callback = utf8encode;
60 utf8encode = null;
61 }
62
63 var data = (packet.data === undefined)
64 ? undefined
65 : packet.data.buffer || packet.data;
66
67 if (Buffer.isBuffer(data)) {
68 return encodeBuffer(packet, supportsBinary, callback);
69 } else if (data instanceof ArrayBuffer) {
70 return encodeArrayBuffer(packet, supportsBinary, callback);
71 }
72
73 // Sending data as a utf-8 string
74 var encoded = packets[packet.type];
75
76 // data fragment is optional
77 if (undefined !== packet.data) {
78 encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
79 }
80
81 return callback('' + encoded);
82};
83
84/**
85 * Encode Buffer data
86 */
87
88function encodeBuffer(packet, supportsBinary, callback) {
89 var data = packet.data;
90 if (!supportsBinary) {
91 return exports.encodeBase64Packet(packet, callback);
92 }
93
94 var typeBuffer = new Buffer(1);
95 typeBuffer[0] = packets[packet.type];
96 return callback(Buffer.concat([typeBuffer, data]));
97}
98
99function encodeArrayBuffer(packet, supportsBinary, callback) {
100 var data = (packet.data === undefined)
101 ? undefined
102 : packet.data.buffer || packet.data;
103
104 if (!supportsBinary) {
105 return exports.encodeBase64Packet(packet, callback);
106 }
107
108 var contentArray = new Uint8Array(data);
109 var resultBuffer = new Buffer(1 + data.byteLength);
110
111 resultBuffer[0] = packets[packet.type];
112 for (var i = 0; i < contentArray.length; i++){
113 resultBuffer[i+1] = contentArray[i];
114 }
115 return callback(resultBuffer);
116}
117
118/**
119 * Encodes a packet with binary data in a base64 string
120 *
121 * @param {Object} packet, has `type` and `data`
122 * @return {String} base64 encoded message
123 */
124
125exports.encodeBase64Packet = function(packet, callback){
126 var data = packet.data.buffer || packet.data;
127 if (data instanceof ArrayBuffer) {
128 var buf = new Buffer(data.byteLength);
129 for (var i = 0; i < buf.length; i++) {
130 buf[i] = data[i];
131 }
132 packet.data = buf;
133 }
134
135 var message = 'b' + packets[packet.type];
136 message += packet.data.toString('base64');
137 return callback(message);
138};
139
140/**
141 * Decodes a packet. Data also available as an ArrayBuffer if requested.
142 *
143 * @return {Object} with `type` and `data` (if any)
144 * @api private
145 */
146
147exports.decodePacket = function (data, binaryType, utf8decode) {
148 // String data
149 if (typeof data == 'string' || data === undefined) {
150 if (data.charAt(0) == 'b') {
151 return exports.decodeBase64Packet(data.substr(1), binaryType);
152 }
153
154 var type = data.charAt(0);
155 if (utf8decode) {
156 try {
157 data = utf8.decode(data);
158 } catch (e) {
159 return err;
160 }
161 }
162
163 if (Number(type) != type || !packetslist[type]) {
164 return err;
165 }
166
167 if (data.length > 1) {
168 return { type: packetslist[type], data: data.substring(1) };
169 } else {
170 return { type: packetslist[type] };
171 }
172 }
173
174 // Binary data
175 if (binaryType === 'arraybuffer') {
176 var type = data[0];
177 var intArray = new Uint8Array(data.length - 1);
178 for (var i = 1; i < data.length; i++) {
179 intArray[i - 1] = data[i];
180 }
181 return { type: packetslist[type], data: intArray.buffer };
182 }
183 var type = data[0];
184 return { type: packetslist[type], data: data.slice(1) };
185};
186
187/**
188 * Decodes a packet encoded in a base64 string.
189 *
190 * @param {String} base64 encoded message
191 * @return {Object} with `type` and `data` (if any)
192 */
193
194exports.decodeBase64Packet = function(msg, binaryType) {
195 var type = packetslist[msg.charAt(0)];
196 var data = new Buffer(msg.substr(1), 'base64');
197 if (binaryType === 'arraybuffer') {
198 var abv = new Uint8Array(data.length);
199 for (var i = 0; i < abv.length; i++){
200 abv[i] = data[i];
201 }
202 data = abv.buffer;
203 }
204 return { type: type, data: data };
205};
206
207/**
208 * Encodes multiple messages (payload).
209 *
210 * <length>:data
211 *
212 * Example:
213 *
214 * 11:hello world2:hi
215 *
216 * If any contents are binary, they will be encoded as base64 strings. Base64
217 * encoded strings are marked with a b before the length specifier
218 *
219 * @param {Array} packets
220 * @api private
221 */
222
223exports.encodePayload = function (packets, supportsBinary, callback) {
224 if (typeof supportsBinary == 'function') {
225 callback = supportsBinary;
226 supportsBinary = null;
227 }
228
229 if (supportsBinary) {
230 return exports.encodePayloadAsBinary(packets, callback);
231 }
232
233 if (!packets.length) {
234 return callback('0:');
235 }
236
237 function setLengthHeader(message) {
238 return message.length + ':' + message;
239 }
240
241 function encodeOne(packet, doneCallback) {
242 exports.encodePacket(packet, supportsBinary, true, function(message) {
243 doneCallback(null, setLengthHeader(message));
244 });
245 }
246
247 map(packets, encodeOne, function(err, results) {
248 return callback(results.join(''));
249 });
250};
251
252/**
253 * Async array map using after
254 */
255
256function map(ary, each, done) {
257 var result = new Array(ary.length);
258 var next = after(ary.length, done);
259
260 var eachWithIndex = function(i, el, cb) {
261 each(el, function(error, msg) {
262 result[i] = msg;
263 cb(error, result);
264 });
265 };
266
267 for (var i = 0; i < ary.length; i++) {
268 eachWithIndex(i, ary[i], next);
269 }
270}
271
272/*
273 * Decodes data when a payload is maybe expected. Possible binary contents are
274 * decoded from their base64 representation
275 *
276 * @param {String} data, callback method
277 * @api public
278 */
279
280exports.decodePayload = function (data, binaryType, callback) {
281 if ('string' != typeof data) {
282 return exports.decodePayloadAsBinary(data, binaryType, callback);
283 }
284
285 if (typeof binaryType === 'function') {
286 callback = binaryType;
287 binaryType = null;
288 }
289
290 var packet;
291 if (data == '') {
292 // parser error - ignoring payload
293 return callback(err, 0, 1);
294 }
295
296 var length = ''
297 , n, msg;
298
299 for (var i = 0, l = data.length; i < l; i++) {
300 var chr = data.charAt(i);
301
302 if (':' != chr) {
303 length += chr;
304 } else {
305 if ('' == length || (length != (n = Number(length)))) {
306 // parser error - ignoring payload
307 return callback(err, 0, 1);
308 }
309
310 msg = data.substr(i + 1, n);
311
312 if (length != msg.length) {
313 // parser error - ignoring payload
314 return callback(err, 0, 1);
315 }
316
317 if (msg.length) {
318 packet = exports.decodePacket(msg, binaryType, true);
319
320 if (err.type == packet.type && err.data == packet.data) {
321 // parser error in individual packet - ignoring payload
322 return callback(err, 0, 1);
323 }
324
325 var ret = callback(packet, i + n, l);
326 if (false === ret) return;
327 }
328
329 // advance cursor
330 i += n;
331 length = '';
332 }
333 }
334
335 if (length != '') {
336 // parser error - ignoring payload
337 return callback(err, 0, 1);
338 }
339
340};
341
342/**
343 *
344 * Converts a buffer to a utf8.js encoded string
345 *
346 * @api private
347 */
348
349function bufferToString(buffer) {
350 var str = '';
351 for (var i = 0; i < buffer.length; i++) {
352 str += String.fromCharCode(buffer[i]);
353 }
354 return str;
355}
356
357/**
358 *
359 * Converts a utf8.js encoded string to a buffer
360 *
361 * @api private
362 */
363
364function stringToBuffer(string) {
365 var buf = new Buffer(string.length);
366 for (var i = 0; i < string.length; i++) {
367 buf.writeUInt8(string.charCodeAt(i), i);
368 }
369 return buf;
370}
371
372/**
373 * Encodes multiple messages (payload) as binary.
374 *
375 * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
376 * 255><data>
377 *
378 * Example:
379 * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
380 *
381 * @param {Array} packets
382 * @return {Buffer} encoded payload
383 * @api private
384 */
385
386exports.encodePayloadAsBinary = function (packets, callback) {
387 if (!packets.length) {
388 return callback(new Buffer(0));
389 }
390
391 function encodeOne(p, doneCallback) {
392 exports.encodePacket(p, true, true, function(packet) {
393
394 if (typeof packet === 'string') {
395 var encodingLength = '' + packet.length;
396 var sizeBuffer = new Buffer(encodingLength.length + 2);
397 sizeBuffer[0] = 0; // is a string (not true binary = 0)
398 for (var i = 0; i < encodingLength.length; i++) {
399 sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
400 }
401 sizeBuffer[sizeBuffer.length - 1] = 255;
402 return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
403 }
404
405 var encodingLength = '' + packet.length;
406 var sizeBuffer = new Buffer(encodingLength.length + 2);
407 sizeBuffer[0] = 1; // is binary (true binary = 1)
408 for (var i = 0; i < encodingLength.length; i++) {
409 sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
410 }
411 sizeBuffer[sizeBuffer.length - 1] = 255;
412 doneCallback(null, Buffer.concat([sizeBuffer, packet]));
413 });
414 }
415
416 map(packets, encodeOne, function(err, results) {
417 return callback(Buffer.concat(results));
418 });
419};
420
421/*
422 * Decodes data when a payload is maybe expected. Strings are decoded by
423 * interpreting each byte as a key code for entries marked to start with 0. See
424 * description of encodePayloadAsBinary
425
426 * @param {Buffer} data, callback method
427 * @api public
428 */
429
430exports.decodePayloadAsBinary = function (data, binaryType, callback) {
431 if (typeof binaryType === 'function') {
432 callback = binaryType;
433 binaryType = null;
434 }
435
436 var bufferTail = data;
437 var buffers = [];
438
439 while (bufferTail.length > 0) {
440 var strLen = '';
441 var isString = bufferTail[0] === 0;
442 var numberTooLong = false;
443 for (var i = 1; ; i++) {
444 if (bufferTail[i] == 255) break;
445 // 310 = char length of Number.MAX_VALUE
446 if (strLen.length > 310) {
447 numberTooLong = true;
448 break;
449 }
450 strLen += '' + bufferTail[i];
451 }
452 if(numberTooLong) return callback(err, 0, 1);
453 bufferTail = bufferTail.slice(strLen.length + 1);
454
455 var msgLength = parseInt(strLen, 10);
456
457 var msg = bufferTail.slice(1, msgLength + 1);
458 if (isString) msg = bufferToString(msg);
459 buffers.push(msg);
460 bufferTail = bufferTail.slice(msgLength + 1);
461 }
462
463 var total = buffers.length;
464 buffers.forEach(function(buffer, i) {
465 callback(exports.decodePacket(buffer, binaryType, true), i, total);
466 });
467};