This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.1 KiB
194 lines
4.1 KiB
/*! |
|
* ws: a node.js websocket client |
|
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com> |
|
* MIT Licensed |
|
*/ |
|
|
|
var util = require('util'); |
|
|
|
/** |
|
* State constants |
|
*/ |
|
|
|
var EMPTY = 0 |
|
, BODY = 1; |
|
var BINARYLENGTH = 2 |
|
, BINARYBODY = 3; |
|
|
|
/** |
|
* Hixie Receiver implementation |
|
*/ |
|
|
|
function Receiver () { |
|
if (this instanceof Receiver === false) { |
|
throw new TypeError("Classes can't be function-called"); |
|
} |
|
|
|
this.state = EMPTY; |
|
this.buffers = []; |
|
this.messageEnd = -1; |
|
this.spanLength = 0; |
|
this.dead = false; |
|
|
|
this.onerror = function() {}; |
|
this.ontext = function() {}; |
|
this.onbinary = function() {}; |
|
this.onclose = function() {}; |
|
this.onping = function() {}; |
|
this.onpong = function() {}; |
|
} |
|
|
|
module.exports = Receiver; |
|
|
|
/** |
|
* Add new data to the parser. |
|
* |
|
* @api public |
|
*/ |
|
|
|
Receiver.prototype.add = function(data) { |
|
if (this.dead) return; |
|
var self = this; |
|
function doAdd() { |
|
if (self.state === EMPTY) { |
|
if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) { |
|
self.reset(); |
|
self.onclose(); |
|
return; |
|
} |
|
if (data[0] === 0x80) { |
|
self.messageEnd = 0; |
|
self.state = BINARYLENGTH; |
|
data = data.slice(1); |
|
} else { |
|
|
|
if (data[0] !== 0x00) { |
|
self.error('payload must start with 0x00 byte', true); |
|
return; |
|
} |
|
data = data.slice(1); |
|
self.state = BODY; |
|
|
|
} |
|
} |
|
if (self.state === BINARYLENGTH) { |
|
var i = 0; |
|
while ((i < data.length) && (data[i] & 0x80)) { |
|
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); |
|
++i; |
|
} |
|
if (i < data.length) { |
|
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); |
|
self.state = BINARYBODY; |
|
++i; |
|
} |
|
if (i > 0) |
|
data = data.slice(i); |
|
} |
|
if (self.state === BINARYBODY) { |
|
var dataleft = self.messageEnd - self.spanLength; |
|
if (data.length >= dataleft) { |
|
// consume the whole buffer to finish the frame |
|
self.buffers.push(data); |
|
self.spanLength += dataleft; |
|
self.messageEnd = dataleft; |
|
return self.parse(); |
|
} |
|
// frame's not done even if we consume it all |
|
self.buffers.push(data); |
|
self.spanLength += data.length; |
|
return; |
|
} |
|
self.buffers.push(data); |
|
if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) { |
|
self.spanLength += self.messageEnd; |
|
return self.parse(); |
|
} |
|
else self.spanLength += data.length; |
|
} |
|
while(data) data = doAdd(); |
|
}; |
|
|
|
/** |
|
* Releases all resources used by the receiver. |
|
* |
|
* @api public |
|
*/ |
|
|
|
Receiver.prototype.cleanup = function() { |
|
this.dead = true; |
|
this.state = EMPTY; |
|
this.buffers = []; |
|
}; |
|
|
|
/** |
|
* Process buffered data. |
|
* |
|
* @api public |
|
*/ |
|
|
|
Receiver.prototype.parse = function() { |
|
var output = new Buffer(this.spanLength); |
|
var outputIndex = 0; |
|
for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) { |
|
var buffer = this.buffers[bi]; |
|
buffer.copy(output, outputIndex); |
|
outputIndex += buffer.length; |
|
} |
|
var lastBuffer = this.buffers[this.buffers.length - 1]; |
|
if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd); |
|
if (this.state !== BODY) --this.messageEnd; |
|
var tail = null; |
|
if (this.messageEnd < lastBuffer.length - 1) { |
|
tail = lastBuffer.slice(this.messageEnd + 1); |
|
} |
|
this.reset(); |
|
this.ontext(output.toString('utf8')); |
|
return tail; |
|
}; |
|
|
|
/** |
|
* Handles an error |
|
* |
|
* @api private |
|
*/ |
|
|
|
Receiver.prototype.error = function (reason, terminate) { |
|
if (this.dead) return; |
|
this.reset(); |
|
if(typeof reason == 'string'){ |
|
this.onerror(new Error(reason), terminate); |
|
} |
|
else if(reason.constructor == Error){ |
|
this.onerror(reason, terminate); |
|
} |
|
else{ |
|
this.onerror(new Error("An error occured"),terminate); |
|
} |
|
return this; |
|
}; |
|
|
|
/** |
|
* Reset parser state |
|
* |
|
* @api private |
|
*/ |
|
|
|
Receiver.prototype.reset = function (reason) { |
|
if (this.dead) return; |
|
this.state = EMPTY; |
|
this.buffers = []; |
|
this.messageEnd = -1; |
|
this.spanLength = 0; |
|
}; |
|
|
|
/** |
|
* Internal api |
|
*/ |
|
|
|
function bufferIndex(buffer, byte) { |
|
for (var i = 0, l = buffer.length; i < l; ++i) { |
|
if (buffer[i] === byte) return i; |
|
} |
|
return -1; |
|
}
|
|
|