Initial experiment for UWS support

This commit is contained in:
Calvin Montgomery 2018-06-13 21:52:22 -07:00
parent 3db751b65f
commit fb5f92b7ee
6 changed files with 188 additions and 0 deletions

View File

@ -41,6 +41,7 @@
"sprintf-js": "^1.0.3", "sprintf-js": "^1.0.3",
"toml": "^2.3.0", "toml": "^2.3.0",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"uws": "^10.148.0",
"yamljs": "^0.2.8" "yamljs": "^0.2.8"
}, },
"scripts": { "scripts": {

View File

@ -725,6 +725,7 @@ Channel.prototype.handleReadLog = function (user) {
Channel.prototype.broadcastToRoom = function (msg, data, ns) { Channel.prototype.broadcastToRoom = function (msg, data, ns) {
sio.instance.in(ns).emit(msg, data); sio.instance.in(ns).emit(msg, data);
require('../io/uws').in(ns).emit(msg, data);
}; };
Channel.prototype.broadcastAll = function (msg, data) { Channel.prototype.broadcastAll = function (msg, data) {

106
src/io/uws.js Normal file
View File

@ -0,0 +1,106 @@
import { EventEmitter } from 'events';
import { Multimap } from '../util/multimap';
import User from '../user';
const rooms = new Multimap();
class UWSWrapper extends EventEmitter {
constructor(socket, context) {
super();
this._uwsSocket = socket;
this._joined = new Set();
this._connected = true;
this.context = context;
this._uwsSocket.on('message', message => {
this._emit.apply(this, this._decode(message));
});
this._uwsSocket.on('close', () => {
this._connected = false;
for (let room of this._joined) {
rooms.delete(room, this);
}
this._joined.clear();
this._emit('disconnect');
});
}
disconnect() {
this._uwsSocket.terminate();
}
get disconnected() {
return !this._connected;
}
emit(frame, ...args) {
this._uwsSocket.send(encode(frame, args));
}
join(room) {
this._joined.add(room);
rooms.set(room, this);
}
leave(room) {
this._joined.delete(room);
rooms.delete(room, this);
}
typecheckedOn(frame, typeDef, cb) {
this.on(frame, cb);
}
typecheckedOnce(frame, typeDef, cb) {
this.once(frame, cb);
}
_decode(message) {
// TODO: handle error and kill clients with protocol violations
return JSON.parse(message);
}
}
Object.assign(UWSWrapper.prototype, { _emit: EventEmitter.prototype.emit });
function encode(frame, args) {
return JSON.stringify([frame].concat(args));
}
function inRoom(room) {
return {
emit(frame, ...args) {
const encoded = encode(frame, args);
for (let wrapper of rooms.get(room)) {
wrapper._uwsSocket.send(encoded);
}
}
};
}
export { UWSWrapper };
exports['in'] = inRoom;
export function init() {
const uws = require('uws');
const server = new uws.Server({ port: 3000 });
server.on('connection', socket => {
const wrap = new UWSWrapper(
socket,
{
aliases: [],
ipSessionFirstSeen: new Date(),
torConnection: false,
ipAddress: null
}
);
new User(wrap, '127.0.0.1', null);
});
}

View File

@ -190,6 +190,7 @@ var Server = function () {
}); });
require("./io/ioserver").init(self, webConfig); require("./io/ioserver").init(self, webConfig);
require("./io/uws").init();
// background tasks init ---------------------------------------------- // background tasks init ----------------------------------------------
require("./bgtask")(self); require("./bgtask")(self);

45
src/util/multimap.js Normal file
View File

@ -0,0 +1,45 @@
class Multimap {
constructor() {
this._items = new Map();
}
get(key) {
if (this._items.has(key)) {
return this._items.get(key);
}
return new Set();
}
has(key, value) {
if (!this._items.has(key)) {
return false;
}
return this._items.get(key).has(value);
}
set(key, value) {
if (!this._items.has(key)) {
this._items.set(key, new Set());
}
return this._items.get(key).add(value);
}
delete(key, value) {
if (!this._items.has(key)) {
return false;
}
const res = this._items.get(key).delete(value);
if (this._items.get(key).size == 0) {
this._items.delete(key);
}
return res;
}
}
export { Multimap };

34
test/util/multimap.js Normal file
View File

@ -0,0 +1,34 @@
const assert = require('assert');
const { Multimap } = require('../../lib/util/multimap');
describe('Multimap', () => {
let map;
beforeEach(() => {
map = new Multimap();
});
it('returns the empty set for an unset key', () => {
assert.deepEqual(map.get('unknown'), new Set());
});
it('returns a set of values for a given key', () => {
map.set('a', 1);
map.set('a', 2);
map.set('a', 1);
assert.deepEqual(map.get('a'), new Set([1, 2]));
});
it('deletes a value for a given key', () => {
map.set('a', 1);
map.set('a', 2);
map.delete('a', 1);
assert.deepEqual(map.get('a'), new Set([2]));
map.delete('a', 2);
assert.deepEqual(map.get('a'), new Set());
});
});