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