| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 | 
/** * Module dependencies. */var debug = require('debug')('socket.io-parser');var Emitter = require('component-emitter');var binary = require('./binary');var isArray = require('isarray');var isBuf = require('./is-buffer');/** * Protocol version. * * @api public */exports.protocol = 4;/** * Packet types. * * @api public */exports.types = [  'CONNECT',  'DISCONNECT',  'EVENT',  'ACK',  'ERROR',  'BINARY_EVENT',  'BINARY_ACK'];/** * Packet type `connect`. * * @api public */exports.CONNECT = 0;/** * Packet type `disconnect`. * * @api public */exports.DISCONNECT = 1;/** * Packet type `event`. * * @api public */exports.EVENT = 2;/** * Packet type `ack`. * * @api public */exports.ACK = 3;/** * Packet type `error`. * * @api public */exports.ERROR = 4;/** * Packet type 'binary event' * * @api public */exports.BINARY_EVENT = 5;/** * Packet type `binary ack`. For acks with binary arguments. * * @api public */exports.BINARY_ACK = 6;/** * Encoder constructor. * * @api public */exports.Encoder = Encoder;/** * Decoder constructor. * * @api public */exports.Decoder = Decoder;/** * A socket.io Encoder instance * * @api public */function Encoder() {}var ERROR_PACKET = exports.ERROR + '"encode error"';/** * Encode a packet as a single string if non-binary, or as a * buffer sequence, depending on packet type. * * @param {Object} obj - packet object * @param {Function} callback - function to handle encodings (likely engine.write) * @return Calls callback with Array of encodings * @api public */Encoder.prototype.encode = function(obj, callback){  debug('encoding packet %j', obj);  if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {    encodeAsBinary(obj, callback);  } else {    var encoding = encodeAsString(obj);    callback([encoding]);  }};/** * Encode packet as string. * * @param {Object} packet * @return {String} encoded * @api private */function encodeAsString(obj) {  // first is type  var str = '' + obj.type;  // attachments if we have them  if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {    str += obj.attachments + '-';  }  // if we have a namespace other than `/`  // we append it followed by a comma `,`  if (obj.nsp && '/' !== obj.nsp) {    str += obj.nsp + ',';  }  // immediately followed by the id  if (null != obj.id) {    str += obj.id;  }  // json data  if (null != obj.data) {    var payload = tryStringify(obj.data);    if (payload !== false) {      str += payload;    } else {      return ERROR_PACKET;    }  }  debug('encoded %j as %s', obj, str);  return str;}function tryStringify(str) {  try {    return JSON.stringify(str);  } catch(e){    return false;  }}/** * Encode packet as 'buffer sequence' by removing blobs, and * deconstructing packet into object with placeholders and * a list of buffers. * * @param {Object} packet * @return {Buffer} encoded * @api private */function encodeAsBinary(obj, callback) {  function writeEncoding(bloblessData) {    var deconstruction = binary.deconstructPacket(bloblessData);    var pack = encodeAsString(deconstruction.packet);    var buffers = deconstruction.buffers;    buffers.unshift(pack); // add packet info to beginning of data list    callback(buffers); // write all the buffers  }  binary.removeBlobs(obj, writeEncoding);}/** * A socket.io Decoder instance * * @return {Object} decoder * @api public */function Decoder() {  this.reconstructor = null;}/** * Mix in `Emitter` with Decoder. */Emitter(Decoder.prototype);/** * Decodes an encoded packet string into packet JSON. * * @param {String} obj - encoded packet * @return {Object} packet * @api public */Decoder.prototype.add = function(obj) {  var packet;  if (typeof obj === 'string') {    packet = decodeString(obj);    if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json      this.reconstructor = new BinaryReconstructor(packet);      // no attachments, labeled binary but no binary data to follow      if (this.reconstructor.reconPack.attachments === 0) {        this.emit('decoded', packet);      }    } else { // non-binary full packet      this.emit('decoded', packet);    }  } else if (isBuf(obj) || obj.base64) { // raw binary data    if (!this.reconstructor) {      throw new Error('got binary data when not reconstructing a packet');    } else {      packet = this.reconstructor.takeBinaryData(obj);      if (packet) { // received final buffer        this.reconstructor = null;        this.emit('decoded', packet);      }    }  } else {    throw new Error('Unknown type: ' + obj);  }};/** * Decode a packet String (JSON data) * * @param {String} str * @return {Object} packet * @api private */function decodeString(str) {  var i = 0;  // look up type  var p = {    type: Number(str.charAt(0))  };  if (null == exports.types[p.type]) {    return error('unknown packet type ' + p.type);  }  // look up attachments if type binary  if (exports.BINARY_EVENT === p.type || exports.BINARY_ACK === p.type) {    var buf = '';    while (str.charAt(++i) !== '-') {      buf += str.charAt(i);      if (i == str.length) break;    }    if (buf != Number(buf) || str.charAt(i) !== '-') {      throw new Error('Illegal attachments');    }    p.attachments = Number(buf);  }  // look up namespace (if any)  if ('/' === str.charAt(i + 1)) {    p.nsp = '';    while (++i) {      var c = str.charAt(i);      if (',' === c) break;      p.nsp += c;      if (i === str.length) break;    }  } else {    p.nsp = '/';  }  // look up id  var next = str.charAt(i + 1);  if ('' !== next && Number(next) == next) {    p.id = '';    while (++i) {      var c = str.charAt(i);      if (null == c || Number(c) != c) {        --i;        break;      }      p.id += str.charAt(i);      if (i === str.length) break;    }    p.id = Number(p.id);  }  // look up json data  if (str.charAt(++i)) {    var payload = tryParse(str.substr(i));    var isPayloadValid = payload !== false && (p.type === exports.ERROR || isArray(payload));    if (isPayloadValid) {      p.data = payload;    } else {      return error('invalid payload');    }  }  debug('decoded %s as %j', str, p);  return p;}function tryParse(str) {  try {    return JSON.parse(str);  } catch(e){    return false;  }}/** * Deallocates a parser's resources * * @api public */Decoder.prototype.destroy = function() {  if (this.reconstructor) {    this.reconstructor.finishedReconstruction();  }};/** * A manager of a binary event's 'buffer sequence'. Should * be constructed whenever a packet of type BINARY_EVENT is * decoded. * * @param {Object} packet * @return {BinaryReconstructor} initialized reconstructor * @api private */function BinaryReconstructor(packet) {  this.reconPack = packet;  this.buffers = [];}/** * Method to be called when binary data received from connection * after a BINARY_EVENT packet. * * @param {Buffer | ArrayBuffer} binData - the raw binary data received * @return {null | Object} returns null if more binary data is expected or *   a reconstructed packet object if all buffers have been received. * @api private */BinaryReconstructor.prototype.takeBinaryData = function(binData) {  this.buffers.push(binData);  if (this.buffers.length === this.reconPack.attachments) { // done with buffer list    var packet = binary.reconstructPacket(this.reconPack, this.buffers);    this.finishedReconstruction();    return packet;  }  return null;};/** * Cleans up binary packet reconstruction variables. * * @api private */BinaryReconstructor.prototype.finishedReconstruction = function() {  this.reconPack = null;  this.buffers = [];};function error(msg) {  return {    type: exports.ERROR,    data: 'parser error: ' + msg  };}
 |