arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/server/node_modules/engine.io-parser/lib/browser.js
server/node_modules/engine.io-parser/lib/browser.jsRaw
  1/**
  2 * Module dependencies.
  3 */
  4
  5var keys = require('./keys');
  6var hasBinary = require('has-binary');
  7var sliceBuffer = require('arraybuffer.slice');
  8var after = require('after');
  9var utf8 = require('wtf-8');
 10
 11var base64encoder;
 12if (global && global.ArrayBuffer) {
 13  base64encoder = require('base64-arraybuffer');
 14}
 15
 16/**
 17 * Check if we are running an android browser. That requires us to use
 18 * ArrayBuffer with polling transports...
 19 *
 20 * http://ghinda.net/jpeg-blob-ajax-android/
 21 */
 22
 23var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent);
 24
 25/**
 26 * Check if we are running in PhantomJS.
 27 * Uploading a Blob with PhantomJS does not work correctly, as reported here:
 28 * https://github.com/ariya/phantomjs/issues/11395
 29 * @type boolean
 30 */
 31var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent);
 32
 33/**
 34 * When true, avoids using Blobs to encode payloads.
 35 * @type boolean
 36 */
 37var dontSendBlobs = isAndroid || isPhantomJS;
 38
 39/**
 40 * Current protocol version.
 41 */
 42
 43exports.protocol = 3;
 44
 45/**
 46 * Packet types.
 47 */
 48
 49var packets = exports.packets = {
 50    open:     0    // non-ws
 51  , close:    1    // non-ws
 52  , ping:     2
 53  , pong:     3
 54  , message:  4
 55  , upgrade:  5
 56  , noop:     6
 57};
 58
 59var packetslist = keys(packets);
 60
 61/**
 62 * Premade error packet.
 63 */
 64
 65var err = { type: 'error', data: 'parser error' };
 66
 67/**
 68 * Create a blob api even for blob builder when vendor prefixes exist
 69 */
 70
 71var Blob = require('blob');
 72
 73/**
 74 * Encodes a packet.
 75 *
 76 *     <packet type id> [ <data> ]
 77 *
 78 * Example:
 79 *
 80 *     5hello world
 81 *     3
 82 *     4
 83 *
 84 * Binary is encoded in an identical principle
 85 *
 86 * @api private
 87 */
 88
 89exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
 90  if ('function' == typeof supportsBinary) {
 91    callback = supportsBinary;
 92    supportsBinary = false;
 93  }
 94
 95  if ('function' == typeof utf8encode) {
 96    callback = utf8encode;
 97    utf8encode = null;
 98  }
 99
100  var data = (packet.data === undefined)
101    ? undefined
102    : packet.data.buffer || packet.data;
103
104  if (global.ArrayBuffer && data instanceof ArrayBuffer) {
105    return encodeArrayBuffer(packet, supportsBinary, callback);
106  } else if (Blob && data instanceof global.Blob) {
107    return encodeBlob(packet, supportsBinary, callback);
108  }
109
110  // might be an object with { base64: true, data: dataAsBase64String }
111  if (data && data.base64) {
112    return encodeBase64Object(packet, callback);
113  }
114
115  // Sending data as a utf-8 string
116  var encoded = packets[packet.type];
117
118  // data fragment is optional
119  if (undefined !== packet.data) {
120    encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
121  }
122
123  return callback('' + encoded);
124
125};
126
127function encodeBase64Object(packet, callback) {
128  // packet data is an object { base64: true, data: dataAsBase64String }
129  var message = 'b' + exports.packets[packet.type] + packet.data.data;
130  return callback(message);
131}
132
133/**
134 * Encode packet helpers for binary types
135 */
136
137function encodeArrayBuffer(packet, supportsBinary, callback) {
138  if (!supportsBinary) {
139    return exports.encodeBase64Packet(packet, callback);
140  }
141
142  var data = packet.data;
143  var contentArray = new Uint8Array(data);
144  var resultBuffer = new Uint8Array(1 + data.byteLength);
145
146  resultBuffer[0] = packets[packet.type];
147  for (var i = 0; i < contentArray.length; i++) {
148    resultBuffer[i+1] = contentArray[i];
149  }
150
151  return callback(resultBuffer.buffer);
152}
153
154function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
155  if (!supportsBinary) {
156    return exports.encodeBase64Packet(packet, callback);
157  }
158
159  var fr = new FileReader();
160  fr.onload = function() {
161    packet.data = fr.result;
162    exports.encodePacket(packet, supportsBinary, true, callback);
163  };
164  return fr.readAsArrayBuffer(packet.data);
165}
166
167function encodeBlob(packet, supportsBinary, callback) {
168  if (!supportsBinary) {
169    return exports.encodeBase64Packet(packet, callback);
170  }
171
172  if (dontSendBlobs) {
173    return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
174  }
175
176  var length = new Uint8Array(1);
177  length[0] = packets[packet.type];
178  var blob = new Blob([length.buffer, packet.data]);
179
180  return callback(blob);
181}
182
183/**
184 * Encodes a packet with binary data in a base64 string
185 *
186 * @param {Object} packet, has `type` and `data`
187 * @return {String} base64 encoded message
188 */
189
190exports.encodeBase64Packet = function(packet, callback) {
191  var message = 'b' + exports.packets[packet.type];
192  if (Blob && packet.data instanceof global.Blob) {
193    var fr = new FileReader();
194    fr.onload = function() {
195      var b64 = fr.result.split(',')[1];
196      callback(message + b64);
197    };
198    return fr.readAsDataURL(packet.data);
199  }
200
201  var b64data;
202  try {
203    b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
204  } catch (e) {
205    // iPhone Safari doesn't let you apply with typed arrays
206    var typed = new Uint8Array(packet.data);
207    var basic = new Array(typed.length);
208    for (var i = 0; i < typed.length; i++) {
209      basic[i] = typed[i];
210    }
211    b64data = String.fromCharCode.apply(null, basic);
212  }
213  message += global.btoa(b64data);
214  return callback(message);
215};
216
217/**
218 * Decodes a packet. Changes format to Blob if requested.
219 *
220 * @return {Object} with `type` and `data` (if any)
221 * @api private
222 */
223
224exports.decodePacket = function (data, binaryType, utf8decode) {
225  if (data === undefined) {
226    return err;
227  }
228  // String data
229  if (typeof data == 'string') {
230    if (data.charAt(0) == 'b') {
231      return exports.decodeBase64Packet(data.substr(1), binaryType);
232    }
233
234    if (utf8decode) {
235      data = tryDecode(data);
236      if (data === false) {
237        return err;
238      }
239    }
240    var type = data.charAt(0);
241
242    if (Number(type) != type || !packetslist[type]) {
243      return err;
244    }
245
246    if (data.length > 1) {
247      return { type: packetslist[type], data: data.substring(1) };
248    } else {
249      return { type: packetslist[type] };
250    }
251  }
252
253  var asArray = new Uint8Array(data);
254  var type = asArray[0];
255  var rest = sliceBuffer(data, 1);
256  if (Blob && binaryType === 'blob') {
257    rest = new Blob([rest]);
258  }
259  return { type: packetslist[type], data: rest };
260};
261
262function tryDecode(data) {
263  try {
264    data = utf8.decode(data);
265  } catch (e) {
266    return false;
267  }
268  return data;
269}
270
271/**
272 * Decodes a packet encoded in a base64 string
273 *
274 * @param {String} base64 encoded message
275 * @return {Object} with `type` and `data` (if any)
276 */
277
278exports.decodeBase64Packet = function(msg, binaryType) {
279  var type = packetslist[msg.charAt(0)];
280  if (!base64encoder) {
281    return { type: type, data: { base64: true, data: msg.substr(1) } };
282  }
283
284  var data = base64encoder.decode(msg.substr(1));
285
286  if (binaryType === 'blob' && Blob) {
287    data = new Blob([data]);
288  }
289
290  return { type: type, data: data };
291};
292
293/**
294 * Encodes multiple messages (payload).
295 *
296 *     <length>:data
297 *
298 * Example:
299 *
300 *     11:hello world2:hi
301 *
302 * If any contents are binary, they will be encoded as base64 strings. Base64
303 * encoded strings are marked with a b before the length specifier
304 *
305 * @param {Array} packets
306 * @api private
307 */
308
309exports.encodePayload = function (packets, supportsBinary, callback) {
310  if (typeof supportsBinary == 'function') {
311    callback = supportsBinary;
312    supportsBinary = null;
313  }
314
315  var isBinary = hasBinary(packets);
316
317  if (supportsBinary && isBinary) {
318    if (Blob && !dontSendBlobs) {
319      return exports.encodePayloadAsBlob(packets, callback);
320    }
321
322    return exports.encodePayloadAsArrayBuffer(packets, callback);
323  }
324
325  if (!packets.length) {
326    return callback('0:');
327  }
328
329  function setLengthHeader(message) {
330    return message.length + ':' + message;
331  }
332
333  function encodeOne(packet, doneCallback) {
334    exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
335      doneCallback(null, setLengthHeader(message));
336    });
337  }
338
339  map(packets, encodeOne, function(err, results) {
340    return callback(results.join(''));
341  });
342};
343
344/**
345 * Async array map using after
346 */
347
348function map(ary, each, done) {
349  var result = new Array(ary.length);
350  var next = after(ary.length, done);
351
352  var eachWithIndex = function(i, el, cb) {
353    each(el, function(error, msg) {
354      result[i] = msg;
355      cb(error, result);
356    });
357  };
358
359  for (var i = 0; i < ary.length; i++) {
360    eachWithIndex(i, ary[i], next);
361  }
362}
363
364/*
365 * Decodes data when a payload is maybe expected. Possible binary contents are
366 * decoded from their base64 representation
367 *
368 * @param {String} data, callback method
369 * @api public
370 */
371
372exports.decodePayload = function (data, binaryType, callback) {
373  if (typeof data != 'string') {
374    return exports.decodePayloadAsBinary(data, binaryType, callback);
375  }
376
377  if (typeof binaryType === 'function') {
378    callback = binaryType;
379    binaryType = null;
380  }
381
382  var packet;
383  if (data == '') {
384    // parser error - ignoring payload
385    return callback(err, 0, 1);
386  }
387
388  var length = ''
389    , n, msg;
390
391  for (var i = 0, l = data.length; i < l; i++) {
392    var chr = data.charAt(i);
393
394    if (':' != chr) {
395      length += chr;
396    } else {
397      if ('' == length || (length != (n = Number(length)))) {
398        // parser error - ignoring payload
399        return callback(err, 0, 1);
400      }
401
402      msg = data.substr(i + 1, n);
403
404      if (length != msg.length) {
405        // parser error - ignoring payload
406        return callback(err, 0, 1);
407      }
408
409      if (msg.length) {
410        packet = exports.decodePacket(msg, binaryType, true);
411
412        if (err.type == packet.type && err.data == packet.data) {
413          // parser error in individual packet - ignoring payload
414          return callback(err, 0, 1);
415        }
416
417        var ret = callback(packet, i + n, l);
418        if (false === ret) return;
419      }
420
421      // advance cursor
422      i += n;
423      length = '';
424    }
425  }
426
427  if (length != '') {
428    // parser error - ignoring payload
429    return callback(err, 0, 1);
430  }
431
432};
433
434/**
435 * Encodes multiple messages (payload) as binary.
436 *
437 * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
438 * 255><data>
439 *
440 * Example:
441 * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
442 *
443 * @param {Array} packets
444 * @return {ArrayBuffer} encoded payload
445 * @api private
446 */
447
448exports.encodePayloadAsArrayBuffer = function(packets, callback) {
449  if (!packets.length) {
450    return callback(new ArrayBuffer(0));
451  }
452
453  function encodeOne(packet, doneCallback) {
454    exports.encodePacket(packet, true, true, function(data) {
455      return doneCallback(null, data);
456    });
457  }
458
459  map(packets, encodeOne, function(err, encodedPackets) {
460    var totalLength = encodedPackets.reduce(function(acc, p) {
461      var len;
462      if (typeof p === 'string'){
463        len = p.length;
464      } else {
465        len = p.byteLength;
466      }
467      return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
468    }, 0);
469
470    var resultArray = new Uint8Array(totalLength);
471
472    var bufferIndex = 0;
473    encodedPackets.forEach(function(p) {
474      var isString = typeof p === 'string';
475      var ab = p;
476      if (isString) {
477        var view = new Uint8Array(p.length);
478        for (var i = 0; i < p.length; i++) {
479          view[i] = p.charCodeAt(i);
480        }
481        ab = view.buffer;
482      }
483
484      if (isString) { // not true binary
485        resultArray[bufferIndex++] = 0;
486      } else { // true binary
487        resultArray[bufferIndex++] = 1;
488      }
489
490      var lenStr = ab.byteLength.toString();
491      for (var i = 0; i < lenStr.length; i++) {
492        resultArray[bufferIndex++] = parseInt(lenStr[i]);
493      }
494      resultArray[bufferIndex++] = 255;
495
496      var view = new Uint8Array(ab);
497      for (var i = 0; i < view.length; i++) {
498        resultArray[bufferIndex++] = view[i];
499      }
500    });
501
502    return callback(resultArray.buffer);
503  });
504};
505
506/**
507 * Encode as Blob
508 */
509
510exports.encodePayloadAsBlob = function(packets, callback) {
511  function encodeOne(packet, doneCallback) {
512    exports.encodePacket(packet, true, true, function(encoded) {
513      var binaryIdentifier = new Uint8Array(1);
514      binaryIdentifier[0] = 1;
515      if (typeof encoded === 'string') {
516        var view = new Uint8Array(encoded.length);
517        for (var i = 0; i < encoded.length; i++) {
518          view[i] = encoded.charCodeAt(i);
519        }
520        encoded = view.buffer;
521        binaryIdentifier[0] = 0;
522      }
523
524      var len = (encoded instanceof ArrayBuffer)
525        ? encoded.byteLength
526        : encoded.size;
527
528      var lenStr = len.toString();
529      var lengthAry = new Uint8Array(lenStr.length + 1);
530      for (var i = 0; i < lenStr.length; i++) {
531        lengthAry[i] = parseInt(lenStr[i]);
532      }
533      lengthAry[lenStr.length] = 255;
534
535      if (Blob) {
536        var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
537        doneCallback(null, blob);
538      }
539    });
540  }
541
542  map(packets, encodeOne, function(err, results) {
543    return callback(new Blob(results));
544  });
545};
546
547/*
548 * Decodes data when a payload is maybe expected. Strings are decoded by
549 * interpreting each byte as a key code for entries marked to start with 0. See
550 * description of encodePayloadAsBinary
551 *
552 * @param {ArrayBuffer} data, callback method
553 * @api public
554 */
555
556exports.decodePayloadAsBinary = function (data, binaryType, callback) {
557  if (typeof binaryType === 'function') {
558    callback = binaryType;
559    binaryType = null;
560  }
561
562  var bufferTail = data;
563  var buffers = [];
564
565  var numberTooLong = false;
566  while (bufferTail.byteLength > 0) {
567    var tailArray = new Uint8Array(bufferTail);
568    var isString = tailArray[0] === 0;
569    var msgLength = '';
570
571    for (var i = 1; ; i++) {
572      if (tailArray[i] == 255) break;
573
574      if (msgLength.length > 310) {
575        numberTooLong = true;
576        break;
577      }
578
579      msgLength += tailArray[i];
580    }
581
582    if(numberTooLong) return callback(err, 0, 1);
583
584    bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
585    msgLength = parseInt(msgLength);
586
587    var msg = sliceBuffer(bufferTail, 0, msgLength);
588    if (isString) {
589      try {
590        msg = String.fromCharCode.apply(null, new Uint8Array(msg));
591      } catch (e) {
592        // iPhone Safari doesn't let you apply to typed arrays
593        var typed = new Uint8Array(msg);
594        msg = '';
595        for (var i = 0; i < typed.length; i++) {
596          msg += String.fromCharCode(typed[i]);
597        }
598      }
599    }
600
601    buffers.push(msg);
602    bufferTail = sliceBuffer(bufferTail, msgLength);
603  }
604
605  var total = buffers.length;
606  buffers.forEach(function(buffer, i) {
607    callback(exports.decodePacket(buffer, binaryType, true), i, total);
608  });
609};