arnaucube
arnaucube/raspberryGPIOhtmlserverPublic Self-hosted repository
raspberryGPIOhtmlserver/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};