cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

Receiver.hixie.js (4228B)


      1/*!
      2 * ws: a node.js websocket client
      3 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
      4 * MIT Licensed
      5 */
      6
      7var util = require('util');
      8
      9/**
     10 * State constants
     11 */
     12
     13var EMPTY = 0
     14  , BODY = 1;
     15var BINARYLENGTH = 2
     16  , BINARYBODY = 3;
     17
     18/**
     19 * Hixie Receiver implementation
     20 */
     21
     22function Receiver () {
     23  if (this instanceof Receiver === false) {
     24    throw new TypeError("Classes can't be function-called");
     25  }
     26
     27  this.state = EMPTY;
     28  this.buffers = [];
     29  this.messageEnd = -1;
     30  this.spanLength = 0;
     31  this.dead = false;
     32
     33  this.onerror = function() {};
     34  this.ontext = function() {};
     35  this.onbinary = function() {};
     36  this.onclose = function() {};
     37  this.onping = function() {};
     38  this.onpong = function() {};
     39}
     40
     41module.exports = Receiver;
     42
     43/**
     44 * Add new data to the parser.
     45 *
     46 * @api public
     47 */
     48
     49Receiver.prototype.add = function(data) {
     50  if (this.dead) return;
     51  var self = this;
     52  function doAdd() {
     53    if (self.state === EMPTY) {
     54      if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
     55        self.reset();
     56        self.onclose();
     57        return;
     58      }
     59      if (data[0] === 0x80) {
     60        self.messageEnd = 0;
     61        self.state = BINARYLENGTH;
     62        data = data.slice(1);
     63      } else {
     64
     65      if (data[0] !== 0x00) {
     66        self.error('payload must start with 0x00 byte', true);
     67        return;
     68      }
     69      data = data.slice(1);
     70      self.state = BODY;
     71
     72      }
     73    }
     74    if (self.state === BINARYLENGTH) {
     75      var i = 0;
     76      while ((i < data.length) && (data[i] & 0x80)) {
     77        self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
     78        ++i;
     79      }
     80      if (i < data.length) {
     81        self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
     82        self.state = BINARYBODY;
     83        ++i;
     84      }
     85      if (i > 0)
     86        data = data.slice(i);
     87    }
     88    if (self.state === BINARYBODY) {
     89      var dataleft = self.messageEnd - self.spanLength;
     90      if (data.length >= dataleft) {
     91        // consume the whole buffer to finish the frame
     92        self.buffers.push(data);
     93        self.spanLength += dataleft;
     94        self.messageEnd = dataleft;
     95        return self.parse();
     96      }
     97      // frame's not done even if we consume it all
     98      self.buffers.push(data);
     99      self.spanLength += data.length;
    100      return;
    101    }
    102    self.buffers.push(data);
    103    if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
    104      self.spanLength += self.messageEnd;
    105      return self.parse();
    106    }
    107    else self.spanLength += data.length;
    108  }
    109  while(data) data = doAdd();
    110};
    111
    112/**
    113 * Releases all resources used by the receiver.
    114 *
    115 * @api public
    116 */
    117
    118Receiver.prototype.cleanup = function() {
    119  this.dead = true;
    120  this.state = EMPTY;
    121  this.buffers = [];
    122};
    123
    124/**
    125 * Process buffered data.
    126 *
    127 * @api public
    128 */
    129
    130Receiver.prototype.parse = function() {
    131  var output = new Buffer(this.spanLength);
    132  var outputIndex = 0;
    133  for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
    134    var buffer = this.buffers[bi];
    135    buffer.copy(output, outputIndex);
    136    outputIndex += buffer.length;
    137  }
    138  var lastBuffer = this.buffers[this.buffers.length - 1];
    139  if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
    140  if (this.state !== BODY) --this.messageEnd;
    141  var tail = null;
    142  if (this.messageEnd < lastBuffer.length - 1) {
    143    tail = lastBuffer.slice(this.messageEnd + 1);
    144  }
    145  this.reset();
    146  this.ontext(output.toString('utf8'));
    147  return tail;
    148};
    149
    150/**
    151 * Handles an error
    152 *
    153 * @api private
    154 */
    155
    156Receiver.prototype.error = function (reason, terminate) {
    157  if (this.dead) return;
    158  this.reset();
    159  if(typeof reason == 'string'){
    160    this.onerror(new Error(reason), terminate);
    161  }
    162  else if(reason.constructor == Error){
    163    this.onerror(reason, terminate);
    164  }
    165  else{
    166    this.onerror(new Error("An error occured"),terminate);
    167  }
    168  return this;
    169};
    170
    171/**
    172 * Reset parser state
    173 *
    174 * @api private
    175 */
    176
    177Receiver.prototype.reset = function (reason) {
    178  if (this.dead) return;
    179  this.state = EMPTY;
    180  this.buffers = [];
    181  this.messageEnd = -1;
    182  this.spanLength = 0;
    183};
    184
    185/**
    186 * Internal api
    187 */
    188
    189function bufferIndex(buffer, byte) {
    190  for (var i = 0, l = buffer.length; i < l; ++i) {
    191    if (buffer[i] === byte) return i;
    192  }
    193  return -1;
    194}