mirror of https://github.com/calzoneman/sync.git
Add uws middleware
This commit is contained in:
parent
61b856c2c9
commit
d369f1ebe4
|
@ -18,6 +18,7 @@ import { Counter, Gauge } from 'prom-client';
|
||||||
import Socket from 'socket.io/lib/socket';
|
import Socket from 'socket.io/lib/socket';
|
||||||
import { TokenBucket } from '../util/token-bucket';
|
import { TokenBucket } from '../util/token-bucket';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
|
import { UWSServer } from './uws';
|
||||||
|
|
||||||
const LOGGER = require('@calzoneman/jsli')('ioserver');
|
const LOGGER = require('@calzoneman/jsli')('ioserver');
|
||||||
|
|
||||||
|
@ -268,6 +269,19 @@ class IOServer {
|
||||||
io.on('connection', this.handleConnection.bind(this));
|
io.on('connection', this.handleConnection.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initUWS() {
|
||||||
|
const uws = this.uws = new UWSServer();
|
||||||
|
|
||||||
|
uws.use(this.ipProxyMiddleware.bind(this));
|
||||||
|
uws.use(this.ipBanMiddleware.bind(this));
|
||||||
|
uws.use(this.ipThrottleMiddleware.bind(this));
|
||||||
|
uws.use(this.cookieParsingMiddleware.bind(this));
|
||||||
|
uws.use(this.ipSessionCookieMiddleware.bind(this));
|
||||||
|
uws.use(this.authUserMiddleware.bind(this));
|
||||||
|
uws.use(this.metricsEmittingMiddleware.bind(this));
|
||||||
|
uws.on('connection', this.handleConnection.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
bindTo(servers) {
|
bindTo(servers) {
|
||||||
if (!this.io) {
|
if (!this.io) {
|
||||||
throw new Error('Cannot bind: socket.io has not been initialized yet');
|
throw new Error('Cannot bind: socket.io has not been initialized yet');
|
||||||
|
@ -407,6 +421,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
|
|
||||||
ioServer.initSocketIO();
|
ioServer.initSocketIO();
|
||||||
|
ioServer.initUWS();
|
||||||
|
|
||||||
const uniqueListenAddresses = new Set();
|
const uniqueListenAddresses = new Set();
|
||||||
const servers = [];
|
const servers = [];
|
||||||
|
|
106
src/io/uws.js
106
src/io/uws.js
|
@ -1,17 +1,43 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Multimap } from '../util/multimap';
|
import { Multimap } from '../util/multimap';
|
||||||
import User from '../user';
|
import clone from 'clone';
|
||||||
|
import uws from 'uws';
|
||||||
|
|
||||||
const rooms = new Multimap();
|
const rooms = new Multimap();
|
||||||
|
|
||||||
|
class UWSContext {
|
||||||
|
constructor(upgradeReq) {
|
||||||
|
this.upgradeReq = upgradeReq;
|
||||||
|
this.ipAddress = null;
|
||||||
|
this.torConnection = null;
|
||||||
|
this.ipSessionFirstSeen = null;
|
||||||
|
this.user = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UWSWrapper extends EventEmitter {
|
class UWSWrapper extends EventEmitter {
|
||||||
constructor(socket, context) {
|
constructor(socket) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._uwsSocket = socket;
|
this._uwsSocket = socket;
|
||||||
this._joined = new Set();
|
this._joined = new Set();
|
||||||
this._connected = true;
|
this._connected = true;
|
||||||
this.context = context;
|
|
||||||
|
this.context = new UWSContext({
|
||||||
|
connection: {
|
||||||
|
remoteAddress: socket._socket.remoteAddress
|
||||||
|
},
|
||||||
|
headers: clone(socket.upgradeReq.headers)
|
||||||
|
});
|
||||||
|
// socket.io metrics compatibility
|
||||||
|
this.client = {
|
||||||
|
conn: {
|
||||||
|
on: function(){},
|
||||||
|
transport: {
|
||||||
|
name: 'uws'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this._uwsSocket.on('message', message => {
|
this._uwsSocket.on('message', message => {
|
||||||
this._emit.apply(this, this._decode(message));
|
this._emit.apply(this, this._decode(message));
|
||||||
|
@ -67,6 +93,61 @@ class UWSWrapper extends EventEmitter {
|
||||||
|
|
||||||
Object.assign(UWSWrapper.prototype, { _emit: EventEmitter.prototype.emit });
|
Object.assign(UWSWrapper.prototype, { _emit: EventEmitter.prototype.emit });
|
||||||
|
|
||||||
|
class UWSServer extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._server = new uws.Server({ port: 3000, host: '127.0.0.1' });
|
||||||
|
this._middleware = [];
|
||||||
|
|
||||||
|
this._server.on('connection', socket => this._onConnection(socket));
|
||||||
|
this._server.on('listening', () => this.emit('listening'));
|
||||||
|
this._server.on('error', e => this.emit('error', e));
|
||||||
|
}
|
||||||
|
|
||||||
|
use(cb) {
|
||||||
|
this._middleware.push(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onConnection(uwsSocket) {
|
||||||
|
const socket = new UWSWrapper(uwsSocket);
|
||||||
|
|
||||||
|
if (this._middleware.length === 0) {
|
||||||
|
this._acceptConnection(socket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
const self = this;
|
||||||
|
function next(error) {
|
||||||
|
if (error) {
|
||||||
|
socket.emit('error', error.message);
|
||||||
|
socket.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= self._middleware.length) {
|
||||||
|
self._acceptConnection(socket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.nextTick(self._middleware[i], socket, next);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.nextTick(next, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
_acceptConnection(socket) {
|
||||||
|
socket.emit('connect');
|
||||||
|
this.emit('connection', socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
this._server.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function encode(frame, args) {
|
function encode(frame, args) {
|
||||||
return JSON.stringify([frame].concat(args));
|
return JSON.stringify([frame].concat(args));
|
||||||
}
|
}
|
||||||
|
@ -83,22 +164,5 @@ function inRoom(room) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { UWSWrapper };
|
export { UWSServer };
|
||||||
exports['in'] = inRoom;
|
exports['in'] = inRoom;
|
||||||
|
|
||||||
export function init() {
|
|
||||||
const uws = require('uws');
|
|
||||||
|
|
||||||
const server = new uws.Server({ port: 3000 });
|
|
||||||
|
|
||||||
server.on('connection', socket => {
|
|
||||||
const context = {
|
|
||||||
aliases: [],
|
|
||||||
ipSessionFirstSeen: new Date(),
|
|
||||||
torConnection: false,
|
|
||||||
ipAddress: null
|
|
||||||
};
|
|
||||||
const wrap = new UWSWrapper(socket, context);
|
|
||||||
new User(wrap, '127.0.0.1', null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -190,7 +190,6 @@ 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);
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
const { EventEmitter } = require('events');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { UWSServer } = require('../../lib/io/uws');
|
||||||
|
const WebSocket = require('uws');
|
||||||
|
|
||||||
|
describe('UWSServer', () => {
|
||||||
|
const endpoint = 'ws://127.0.0.1:3000';
|
||||||
|
|
||||||
|
let server;
|
||||||
|
let socket;
|
||||||
|
beforeEach(done => {
|
||||||
|
server = new UWSServer();
|
||||||
|
server.on('error', e => { throw e; });
|
||||||
|
server.once('listening', done);
|
||||||
|
});
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
let socket = new WebSocket(endpoint);
|
||||||
|
socket.test = new EventEmitter();
|
||||||
|
|
||||||
|
socket.onmessage = message => {
|
||||||
|
const args = JSON.parse(message.data);
|
||||||
|
const frame = args.shift();
|
||||||
|
socket.test.emit(frame, ...args);
|
||||||
|
};
|
||||||
|
socket.onerror = e => { throw e; };
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (socket) socket.terminate();
|
||||||
|
socket = null;
|
||||||
|
if (server) server.shutdown();
|
||||||
|
server = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a connection immediately if there is no middleware', done => {
|
||||||
|
socket = connect();
|
||||||
|
socket.test.on('connect', done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a connection with middleware', done => {
|
||||||
|
let m1 = false, m2 = false;
|
||||||
|
server.use((socket, next) => {
|
||||||
|
m1 = true;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
server.use((socket, next) => {
|
||||||
|
m2 = true;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket = connect();
|
||||||
|
socket.test.on('connect', () => {
|
||||||
|
assert(m1);
|
||||||
|
assert(m2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects a connection with middleware', done => {
|
||||||
|
let m1 = false, m2 = false;
|
||||||
|
server.use((socket, next) => {
|
||||||
|
m1 = true;
|
||||||
|
next(new Error('broken'));
|
||||||
|
});
|
||||||
|
server.use((socket, next) => {
|
||||||
|
m2 = true;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket = connect();
|
||||||
|
socket.test.on('connect', () => {
|
||||||
|
throw new Error('Unexpected connect callback');
|
||||||
|
});
|
||||||
|
socket.test.on('error', e => {
|
||||||
|
assert.strictEqual(e, 'broken');
|
||||||
|
assert(!m2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,7 +3,6 @@
|
||||||
this._ws = ws;
|
this._ws = ws;
|
||||||
this._listeners = Object.create(null);
|
this._listeners = Object.create(null);
|
||||||
|
|
||||||
this._ws.onopen = this._onopen.bind(this);
|
|
||||||
this._ws.onclose = this._onclose.bind(this);
|
this._ws.onclose = this._onclose.bind(this);
|
||||||
this._ws.onmessage = this._onmessage.bind(this);
|
this._ws.onmessage = this._onmessage.bind(this);
|
||||||
this._ws.onerror = this._onerror.bind(this);
|
this._ws.onerror = this._onerror.bind(this);
|
||||||
|
@ -38,10 +37,6 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WSShim.prototype._onopen = function _onopen() {
|
|
||||||
this._emit('connect');
|
|
||||||
};
|
|
||||||
|
|
||||||
WSShim.prototype._onclose = function _onclose() {
|
WSShim.prototype._onclose = function _onclose() {
|
||||||
// TODO: reconnect logic
|
// TODO: reconnect logic
|
||||||
this._emit('disconnect');
|
this._emit('disconnect');
|
||||||
|
|
Loading…
Reference in New Issue