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