aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/serverLib/MainServer.js
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/serverLib/MainServer.js')
-rw-r--r--server/src/serverLib/MainServer.js138
1 files changed, 85 insertions, 53 deletions
diff --git a/server/src/serverLib/MainServer.js b/server/src/serverLib/MainServer.js
index 628d8ab..c7a4bbe 100644
--- a/server/src/serverLib/MainServer.js
+++ b/server/src/serverLib/MainServer.js
@@ -7,15 +7,15 @@
*
*/
-const wsServer = require('ws').Server;
-const socketReady = require('ws').OPEN;
-const crypto = require('crypto');
-const ipSalt = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
-const internalCmdKey = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
+const WsServer = require('ws').Server;
+const SocketReady = require('ws').OPEN;
+const Crypto = require('crypto');
const RateLimiter = require('./RateLimiter');
-const pulseSpeed = 16000; // ping all clients every X ms
+const PulseSpeed = 16000; // ping all clients every X ms
+const IpSalt = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
+const InternalCmdKey = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
-class MainServer extends wsServer {
+class MainServer extends WsServer {
/**
* Create a HackChat server instance.
*
@@ -24,13 +24,32 @@ class MainServer extends wsServer {
constructor (core) {
super({ port: core.config.websocketPort });
- this._core = core;
- this._hooks = {};
- this._police = new RateLimiter();
- this._cmdBlacklist = {};
- this._cmdKey = internalCmdKey;
+ this.core = core;
+ this.hooks = {};
+ this.police = new RateLimiter();
+ this.cmdBlacklist = {};
- this._heartBeat = setInterval(() => this.beatHeart(), pulseSpeed);
+ this.setupServer();
+ this.loadHooks();
+ }
+
+ /**
+ * Internal command key getter. Used to verify that internal only commands
+ * originate internally and not from a connected client.
+ * TODO: update to a structure that cannot be passed through json
+ *
+ * @type {String} readonly
+ */
+ get cmdKey () {
+ return InternalCmdKey;
+ }
+
+ /**
+ * Create ping interval and setup server event listeners
+ *
+ */
+ setupServer () {
+ this.heartBeat = setInterval(() => this.beatHeart(), PulseSpeed);
this.on('error', (err) => {
this.handleError('server', err);
@@ -39,8 +58,6 @@ class MainServer extends wsServer {
this.on('connection', (socket, request) => {
this.newConnection(socket, request);
});
-
- this.loadHooks();
}
/**
@@ -56,7 +73,7 @@ class MainServer extends wsServer {
for (let i = 0, l = targetSockets.length; i < l; i++) {
try {
- if (targetSockets[i].readyState === socketReady) {
+ if (targetSockets[i].readyState === SocketReady) {
targetSockets[i].ping();
}
} catch (e) { }
@@ -93,18 +110,18 @@ class MainServer extends wsServer {
*/
handleData (socket, data) {
// Don't penalize yet, but check whether IP is rate-limited
- if (this._police.frisk(socket.remoteAddress, 0)) {
- this._core.commands.handleCommand(this, socket, {
+ if (this.police.frisk(socket.remoteAddress, 0)) {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
- text: 'Your IP is being rate-limited or blocked.'
+ cmdKey: this.cmdKey,
+ text: 'You are being rate-limited or blocked.'
});
return;
}
// Penalize here, but don't do anything about it
- this._police.frisk(socket.remoteAddress, 1);
+ this.police.frisk(socket.remoteAddress, 1);
// Ignore ridiculously large packets
if (data.length > 65536) {
@@ -112,7 +129,7 @@ class MainServer extends wsServer {
}
// Start sent data verification
- var payload = null;
+ let payload = null;
try {
payload = JSON.parse(data);
} catch (e) {
@@ -124,6 +141,11 @@ class MainServer extends wsServer {
return;
}
+ // TODO: make this more flexible
+ /*
+ * Issue #1: hard coded `cmd` check
+ * Issue #2: hard coded `cmd` value checks
+ */
if (typeof payload.cmd === 'undefined') {
return;
}
@@ -136,18 +158,19 @@ class MainServer extends wsServer {
return;
}
- if (typeof this._cmdBlacklist[payload.cmd] === 'function') {
+ if (typeof this.cmdBlacklist[payload.cmd] === 'function') {
return;
}
+ // End TODO //
// Execute `in` (incoming data) hooks and process results
payload = this.executeHooks('in', socket, payload);
if (typeof payload === 'string') {
// A hook malfunctioned, reply with error
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
+ cmdKey: this.cmdKey,
text: payload
});
@@ -158,7 +181,7 @@ class MainServer extends wsServer {
}
// Finished verification & hooks, pass to command modules
- this._core.commands.handleCommand(this, socket, payload);
+ this.core.commands.handleCommand(this, socket, payload);
}
/**
@@ -167,9 +190,9 @@ class MainServer extends wsServer {
* @param {Object} socket Closing socket object
*/
handleClose (socket) {
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'disconnect',
- cmdKey: this._cmdKey
+ cmdKey: this.cmdKey
});
}
@@ -198,9 +221,9 @@ class MainServer extends wsServer {
if (typeof payload === 'string') {
// A hook malfunctioned, reply with error
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
+ cmdKey: this.cmdKey,
text: payload
});
@@ -211,7 +234,7 @@ class MainServer extends wsServer {
}
try {
- if (socket.readyState === socketReady) {
+ if (socket.readyState === SocketReady) {
socket.send(JSON.stringify(payload));
}
} catch (e) { }
@@ -232,6 +255,8 @@ class MainServer extends wsServer {
*
* @param {Object} payload Object to convert to json for transmission
* @param {Object} filter see `this.findSockets()`
+ *
+ * @return {Boolean} False if no clients matched the filter, true if data sent
*/
broadcast (payload, filter) {
let targetSockets = this.findSockets(filter);
@@ -255,6 +280,8 @@ class MainServer extends wsServer {
* = {} // matches all
* = { channel: 'programming' } // matches any socket where (`socket.channel` === 'programming')
* = { channel: 'programming', nick: 'Marzavec' } // matches any socket where (`socket.channel` === 'programming' && `socket.nick` === 'Marzavec')
+ *
+ * @return {Array} Clients who matched the filter requirements
*/
findSockets (filter) {
let filterAttribs = Object.keys(filter);
@@ -310,14 +337,16 @@ class MainServer extends wsServer {
* encodes and shortens the output, returns that value
*
* @param {Object||String} target Either the target socket or ip as string
+ *
+ * @return {String} Hashed client connection string
*/
getSocketHash (target) {
- let sha = crypto.createHash('sha256');
+ let sha = Crypto.createHash('sha256');
if (typeof target === 'string') {
- sha.update(target + ipSalt);
+ sha.update(target + IpSalt);
} else {
- sha.update(target.remoteAddress + ipSalt);
+ sha.update(target.remoteAddress + IpSalt);
}
return sha.digest('base64').substr(0, 15);
@@ -332,26 +361,26 @@ class MainServer extends wsServer {
// clear current hooks (if any)
this.clearHooks();
// notify each module to register their hooks (if any)
- this._core.commands.initCommandHooks(this);
+ this.core.commands.initCommandHooks(this);
- if (typeof this._hooks['in'] !== 'undefined') {
+ if (typeof this.hooks['in'] !== 'undefined') {
// start sorting, with incoming first
- let curHooks = [ ...this._hooks['in'].keys() ];
+ let curHooks = [ ...this.hooks['in'].keys() ];
let hookObj = [];
for (let i = 0, j = curHooks.length; i < j; i++) {
- hookObj = this._hooks['in'].get(curHooks[i]);
+ hookObj = this.hooks['in'].get(curHooks[i]);
hookObj.sort( (h1, h2) => h1.priority - h2.priority );
- this._hooks['in'].set(hookObj);
+ this.hooks['in'].set(hookObj);
}
}
- if (typeof this._hooks['out'] !== 'undefined') {
+ if (typeof this.hooks['out'] !== 'undefined') {
// then outgoing
- curHooks = [ ...this._hooks['out'].keys() ];
+ curHooks = [ ...this.hooks['out'].keys() ];
for (let i = 0, j = curHooks.length; i < j; i++) {
- hookObj = this._hooks['out'].get(curHooks[i]);
+ hookObj = this.hooks['out'].get(curHooks[i]);
hookObj.sort( (h1, h2) => h1.priority - h2.priority );
- this._hooks['out'].set(hookObj);
+ this.hooks['out'].set(hookObj);
}
}
}
@@ -371,15 +400,15 @@ class MainServer extends wsServer {
priority = 25;
}
- if (typeof this._hooks[type] === 'undefined') {
- this._hooks[type] = new Map();
+ if (typeof this.hooks[type] === 'undefined') {
+ this.hooks[type] = new Map();
}
- if (!this._hooks[type].has(command)) {
- this._hooks[type].set(command, []);
+ if (!this.hooks[type].has(command)) {
+ this.hooks[type].set(command, []);
}
- this._hooks[type].get(command).push({
+ this.hooks[type].get(command).push({
run: hookFunction,
priority: priority
});
@@ -395,17 +424,19 @@ class MainServer extends wsServer {
* @param {String} type The type of event, typically `in` (incoming) or `out` (outgoing)
* @param {Object} socket Either the target client or the client triggering the hook (depending on `type`)
* @param {Object} payload Either incoming data from client or outgoing data (depending on `type`)
+ *
+ * @return {Object || Boolean}
*/
executeHooks (type, socket, payload) {
let command = payload.cmd;
- if (typeof this._hooks[type] !== 'undefined') {
- if (this._hooks[type].has(command)) {
- let hooks = this._hooks[type].get(command);
+ if (typeof this.hooks[type] !== 'undefined') {
+ if (this.hooks[type].has(command)) {
+ let hooks = this.hooks[type].get(command);
for (let i = 0, j = hooks.length; i < j; i++) {
try {
- payload = hooks[i].run(this._core, this, socket, payload);
+ payload = hooks[i].run(this.core, this, socket, payload);
} catch (err) {
let errText = `Hook failure, '${type}', '${command}': ${err}`;
console.log(errText);
@@ -425,9 +456,10 @@ class MainServer extends wsServer {
/**
* Wipe server hooks to make ready for module reload calls
+ *
*/
clearHooks () {
- this._hooks = {};
+ this.hooks = {};
}
}