Make uws listener configurable

This commit is contained in:
Calvin Montgomery 2018-07-11 21:24:39 -07:00
parent 2340da1cb5
commit 3322e5404f
7 changed files with 95 additions and 21 deletions

View File

@ -6,11 +6,47 @@ export default class IOConfiguration {
getSocketEndpoints() { getSocketEndpoints() {
return this.config.endpoints.slice(); return this.config.endpoints.slice();
} }
getUWSEndpoints() {
return this.config.uwsEndpoints.slice();
}
}
function getUWSEndpoints(oldConfig) {
const uwsEndpoints = oldConfig.get('listen').filter(it => it.uws)
.map(it => {
let domain;
if (it.https) {
domain = oldConfig.get('https.domain')
.replace(/^https/, 'wss');
} else {
domain = oldConfig.get('io.domain')
.replace(/^http/, 'ws');
}
return {
secure: !!it.https,
url: `${domain}:${it.port}`
};
});
uwsEndpoints.sort((a, b) => {
if (a.secure && !b.secure) {
return -1;
} else if (b.secure && !a.secure) {
return 1;
} else {
return 0;
}
});
return uwsEndpoints;
} }
IOConfiguration.fromOldConfig = function (oldConfig) { IOConfiguration.fromOldConfig = function (oldConfig) {
const config = { const config = {
endpoints: [] endpoints: [],
uwsEndpoints: getUWSEndpoints(oldConfig)
}; };
if (oldConfig.get('io.ipv4-ssl')) { if (oldConfig.get('io.ipv4-ssl')) {

View File

@ -7,8 +7,10 @@ export default class NullClusterClient {
getSocketConfig(_channel) { getSocketConfig(_channel) {
const servers = this.ioConfig.getSocketEndpoints(); const servers = this.ioConfig.getSocketEndpoints();
const uwsServers = this.ioConfig.getUWSEndpoints();
return Promise.resolve({ return Promise.resolve({
servers: servers servers: servers,
uwsServers: uwsServers
}); });
} }
} }

View File

@ -278,18 +278,20 @@ class IOServer {
uws.use(this.cookieParsingMiddleware.bind(this)); uws.use(this.cookieParsingMiddleware.bind(this));
uws.use(this.ipSessionCookieMiddleware.bind(this)); uws.use(this.ipSessionCookieMiddleware.bind(this));
uws.use(this.authUserMiddleware.bind(this)); uws.use(this.authUserMiddleware.bind(this));
uws.use(this.metricsEmittingMiddleware.bind(this));
uws.on('connection', this.handleConnection.bind(this)); uws.on('connection', this.handleConnection.bind(this));
} }
bindTo(servers) { bindTo(sioServers, uwsServers) {
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');
} }
servers.forEach(server => { sioServers.forEach(server => {
this.io.attach(server); this.io.attach(server);
}); });
uwsServers.forEach(server => {
this.uws.attach(server);
});
} }
} }
@ -425,12 +427,19 @@ module.exports = {
const uniqueListenAddresses = new Set(); const uniqueListenAddresses = new Set();
const servers = []; const servers = [];
const uwsServers = [];
Config.get("listen").forEach(function (bind) { Config.get("listen").forEach(function (bind) {
if (!bind.io) { if (bind.io && bind.uws) {
throw new Error(
'Cannot bind both socket.io and uws to the same listener'
);
} else if (!bind.io && !bind.uws) {
return; return;
} }
const serverList = bind.io ? servers : uwsServers;
const id = bind.ip + ":" + bind.port; const id = bind.ip + ":" + bind.port;
if (uniqueListenAddresses.has(id)) { if (uniqueListenAddresses.has(id)) {
LOGGER.warn("Ignoring duplicate listen address %s", id); LOGGER.warn("Ignoring duplicate listen address %s", id);
@ -438,16 +447,16 @@ module.exports = {
} }
if (srv.servers.hasOwnProperty(id)) { if (srv.servers.hasOwnProperty(id)) {
servers.push(srv.servers[id]); serverList.push(srv.servers[id]);
} else { } else {
const server = http.createServer().listen(bind.port, bind.ip); const server = http.createServer().listen(bind.port, bind.ip);
servers.push(server); serverList.push(server);
} }
uniqueListenAddresses.add(id); uniqueListenAddresses.add(id);
}); });
ioServer.bindTo(servers); ioServer.bindTo(servers, uwsServers);
}, },
IOServer: IOServer, IOServer: IOServer,

View File

@ -165,18 +165,26 @@ class UWSServer extends EventEmitter {
constructor() { constructor() {
super(); super();
this._server = new uws.Server({ port: 3000, host: '127.0.0.1' }); this._servers = [];
this._middleware = []; 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) { use(cb) {
this._middleware.push(cb); this._middleware.push(cb);
} }
attach(server) {
const uwsServer = new uws.Server({
server,
perMessageDeflate: false
});
this._servers.push(uwsServer);
uwsServer.on('connection', socket => this._onConnection(socket));
server.on('listening', () => this.emit('listening'));
uwsServer.on('error', e => this.emit('error', e));
}
_onConnection(uwsSocket) { _onConnection(uwsSocket) {
const socket = new UWSWrapper(uwsSocket); const socket = new UWSWrapper(uwsSocket);
@ -212,7 +220,7 @@ class UWSServer extends EventEmitter {
} }
shutdown() { shutdown() {
this._server.close(); this._servers.forEach(sv => sv.close());
} }
} }

View File

@ -3,14 +3,20 @@ const assert = require('assert');
const { UWSServer } = require('../../lib/io/uws'); const { UWSServer } = require('../../lib/io/uws');
const inRoom = require('../../lib/io/uws')['in']; const inRoom = require('../../lib/io/uws')['in'];
const WebSocket = require('uws'); const WebSocket = require('uws');
const http = require('http');
describe('UWSServer', () => { describe('UWSServer', () => {
const endpoint = 'ws://127.0.0.1:3000'; const endpoint = 'ws://127.0.0.1:3000';
let httpServer;
let server; let server;
let socket; let socket;
beforeEach(done => { beforeEach(done => {
httpServer = http.createServer();
httpServer.listen(3000);
server = new UWSServer(); server = new UWSServer();
server.attach(httpServer);
server.on('error', e => { throw e; }); server.on('error', e => { throw e; });
server.once('listening', done); server.once('listening', done);
}); });
@ -38,6 +44,8 @@ describe('UWSServer', () => {
socket = null; socket = null;
if (server) server.shutdown(); if (server) server.shutdown();
server = null; server = null;
if (httpServer) httpServer.close();
httpServer = null;
}); });
it('accepts a connection immediately if there is no middleware', done => { it('accepts a connection immediately if there is no middleware', done => {

View File

@ -1221,7 +1221,19 @@ function ioServerConnect(socketConfig) {
var USING_LETS_ENCRYPT = false; var USING_LETS_ENCRYPT = false;
function initSocket(socketConfig) {
if (socketConfig.uwsServers && socketConfig.uwsServers.length > 0) {
initWS(socketConfig.uwsServers);
} else {
initSocketIO(socketConfig);
}
setupCallbacks();
}
function initSocketIO(socketConfig) { function initSocketIO(socketConfig) {
console.log('Using socket.io');
function genericConnectionError() { function genericConnectionError() {
var message = "The socket.io library could not be loaded from <code>" + var message = "The socket.io library could not be loaded from <code>" +
source + "</code>. Ensure that it is not being blocked " + source + "</code>. Ensure that it is not being blocked " +
@ -1295,16 +1307,16 @@ function checkLetsEncrypt(socketConfig, nonLetsEncryptError) {
}); });
} }
function initWS() { function initWS(servers) {
window.socket = new WSShim('ws://localhost:3000/'); console.log('Using WSShim');
setupCallbacks(); console.log("Connecting to " + JSON.stringify(servers[0]));
window.socket = new WSShim(servers[0].url);
} }
(function () { (function () {
$.getJSON("/socketconfig/" + CHANNEL.name + ".json") $.getJSON("/socketconfig/" + CHANNEL.name + ".json")
.done(function (socketConfig) { .done(function (socketConfig) {
//initSocketIO(socketConfig); initSocket(socketConfig);
initWS();
}).fail(function () { }).fail(function () {
makeAlert("Error", "Failed to retrieve socket.io configuration. " + makeAlert("Error", "Failed to retrieve socket.io configuration. " +
"Please try again in a few minutes.", "Please try again in a few minutes.",

View File

@ -113,7 +113,6 @@
WSShim.prototype._onmessage = function _onmessage(message) { WSShim.prototype._onmessage = function _onmessage(message) {
try { try {
var parsed = JSON.parse(message.data); var parsed = JSON.parse(message.data);
console.log(parsed);
var type = parsed.type; var type = parsed.type;
var frame = parsed.frame; var frame = parsed.frame;
var payload = parsed.payload; var payload = parsed.payload;