2020-12-08 03:20:59 -05:00
|
|
|
'use strict';
|
|
|
|
|
2024-02-05 21:13:02 +01:00
|
|
|
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
|
|
|
|
2024-03-08 18:50:29 +01:00
|
|
|
import events from 'events';
|
2020-11-23 13:24:19 -05:00
|
|
|
const express = require('../express');
|
2024-03-08 18:50:29 +01:00
|
|
|
import log4js from 'log4js';
|
2020-10-06 19:44:34 -04:00
|
|
|
const proxyaddr = require('proxy-addr');
|
2020-11-23 13:24:19 -05:00
|
|
|
const settings = require('../../utils/Settings');
|
2024-03-08 18:50:29 +01:00
|
|
|
import {Server, Socket} from 'socket.io'
|
2020-11-23 13:24:19 -05:00
|
|
|
const socketIORouter = require('../../handler/SocketIORouter');
|
2020-12-08 03:20:59 -05:00
|
|
|
const hooks = require('../../../static/js/pluginfw/hooks');
|
2020-11-23 13:24:19 -05:00
|
|
|
const padMessageHandler = require('../../handler/PadMessageHandler');
|
2020-12-07 15:48:28 -05:00
|
|
|
|
2024-02-05 21:13:02 +01:00
|
|
|
let io:any;
|
2021-02-13 01:15:14 -05:00
|
|
|
const logger = log4js.getLogger('socket.io');
|
|
|
|
const sockets = new Set();
|
|
|
|
const socketsEvents = new events.EventEmitter();
|
2020-12-07 15:48:28 -05:00
|
|
|
|
2024-03-08 18:50:29 +01:00
|
|
|
export const expressCloseServer = async () => {
|
2021-02-13 01:15:14 -05:00
|
|
|
if (io == null) return;
|
|
|
|
logger.info('Closing socket.io engine...');
|
|
|
|
// Close the socket.io engine to disconnect existing clients and reject new clients. Don't call
|
|
|
|
// io.close() because that closes the underlying HTTP server, which is already done elsewhere.
|
|
|
|
// (Closing an HTTP server twice throws an exception.) The `engine` property of socket.io Server
|
|
|
|
// objects is undocumented, but I don't see any other way to shut down socket.io without also
|
|
|
|
// closing the HTTP server.
|
2020-12-07 15:48:28 -05:00
|
|
|
io.engine.close();
|
2021-02-13 01:15:14 -05:00
|
|
|
// Closing the socket.io engine should disconnect all clients but it is not documented. Wait for
|
|
|
|
// all of the connections to close to make sure, and log the progress so that we can troubleshoot
|
|
|
|
// if socket.io's behavior ever changes.
|
|
|
|
//
|
|
|
|
// Note: `io.sockets.clients()` should not be used here to track the remaining clients.
|
|
|
|
// `io.sockets.clients()` works with socket.io 2.x, but not with 3.x: With socket.io 2.x all
|
|
|
|
// clients are always added to the default namespace (`io.sockets`) even if they specified a
|
|
|
|
// different namespace upon connection, but with socket.io 3.x clients are NOT added to the
|
|
|
|
// default namespace if they have specified a different namespace. With socket.io 3.x there does
|
|
|
|
// not appear to be a way to get all clients across all namespaces without tracking them
|
|
|
|
// ourselves, so that is what we do.
|
|
|
|
let lastLogged = 0;
|
|
|
|
while (sockets.size > 0) {
|
|
|
|
if (Date.now() - lastLogged > 1000) { // Rate limit to avoid filling logs.
|
|
|
|
logger.info(`Waiting for ${sockets.size} socket.io clients to disconnect...`);
|
|
|
|
lastLogged = Date.now();
|
|
|
|
}
|
|
|
|
await events.once(socketsEvents, 'updated');
|
|
|
|
}
|
|
|
|
logger.info('All socket.io clients have disconnected');
|
2020-12-07 15:48:28 -05:00
|
|
|
};
|
2012-02-25 16:44:37 +01:00
|
|
|
|
2024-03-08 18:50:29 +01:00
|
|
|
const socketSessionMiddleware = (args: any) => (socket: any, next: Function) => {
|
2024-02-18 00:06:26 +03:30
|
|
|
const req = socket.request;
|
|
|
|
// Express sets req.ip but socket.io does not. Replicate Express's behavior here.
|
|
|
|
if (req.ip == null) {
|
|
|
|
if (settings.trustProxy) {
|
|
|
|
req.ip = proxyaddr(req, args.app.get('trust proxy fn'));
|
|
|
|
} else {
|
|
|
|
req.ip = socket.handshake.address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!req.headers.cookie) {
|
|
|
|
// socketio.js-client on node.js doesn't support cookies, so pass them via a query parameter.
|
|
|
|
req.headers.cookie = socket.handshake.query.cookie;
|
|
|
|
}
|
|
|
|
express.sessionMiddleware(req, {}, next);
|
|
|
|
};
|
|
|
|
|
2024-03-08 18:50:29 +01:00
|
|
|
export const expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
2020-11-23 13:24:19 -05:00
|
|
|
// init socket.io and redirect all requests to the MessageHandler
|
2014-11-25 22:38:22 +01:00
|
|
|
// there shouldn't be a browser that isn't compatible to all
|
|
|
|
// transports in this list at once
|
|
|
|
// e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling
|
2024-03-08 18:50:29 +01:00
|
|
|
io = new Server(args.server,{
|
2020-11-23 13:24:19 -05:00
|
|
|
transports: settings.socketTransportProtocols,
|
2024-02-25 14:33:55 +03:30
|
|
|
cookie: false,
|
|
|
|
maxHttpBufferSize: settings.socketIo.maxHttpBufferSize,
|
2024-03-08 18:50:29 +01:00
|
|
|
})
|
2024-02-19 22:29:33 +01:00
|
|
|
|
2024-02-25 14:33:55 +03:30
|
|
|
io.on('connection', (socket:any) => {
|
|
|
|
sockets.add(socket);
|
|
|
|
socketsEvents.emit('updated');
|
|
|
|
// https://socket.io/docs/v3/faq/index.html
|
|
|
|
const session = socket.request.session;
|
|
|
|
session.connections++;
|
|
|
|
session.save();
|
|
|
|
socket.on('disconnect', () => {
|
|
|
|
sockets.delete(socket);
|
|
|
|
socketsEvents.emit('updated');
|
|
|
|
});
|
|
|
|
});
|
2021-02-13 01:15:14 -05:00
|
|
|
|
2024-03-08 18:50:29 +01:00
|
|
|
io.use(socketSessionMiddleware(args));
|
|
|
|
|
2024-02-19 22:29:33 +01:00
|
|
|
// Temporary workaround so all clients go through middleware and handle connection
|
2024-03-08 18:50:29 +01:00
|
|
|
io.of('/pluginfw/installer').use(socketSessionMiddleware(args))
|
|
|
|
io.of('/settings').use(socketSessionMiddleware(args))
|
2024-02-19 22:29:33 +01:00
|
|
|
|
2024-02-05 21:13:02 +01:00
|
|
|
io.use((socket:any, next:Function) => {
|
|
|
|
socket.conn.on('packet', (packet:string) => {
|
2021-12-23 01:45:38 -05:00
|
|
|
// Tell express-session that the session is still active. The session store can use these
|
|
|
|
// touch events to defer automatic session cleanup, and if express-session is configured with
|
|
|
|
// rolling=true the cookie's expiration time will be renewed. (Note that WebSockets does not
|
|
|
|
// have a standard mechanism for periodically updating the browser's cookies, so the browser
|
|
|
|
// will not see the new cookie expiration time unless it makes a new HTTP request or the new
|
|
|
|
// cookie value is sent to the client in a custom socket.io message.)
|
|
|
|
if (socket.request.session != null) socket.request.session.touch();
|
|
|
|
});
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
2014-11-04 19:11:06 +00:00
|
|
|
// var socketIOLogger = log4js.getLogger("socket.io");
|
|
|
|
// Debug logging now has to be set at an environment level, this is stupid.
|
|
|
|
// https://github.com/Automattic/socket.io/wiki/Migrating-to-1.0
|
|
|
|
// This debug logging environment is set in Settings.js
|
2012-02-25 16:44:37 +01:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
// minify socket.io javascript
|
2014-11-04 23:25:18 +00:00
|
|
|
// Due to a shitty decision by the SocketIO team minification is
|
|
|
|
// no longer available, details available at:
|
|
|
|
// http://stackoverflow.com/questions/23981741/minify-socket-io-socket-io-js-with-1-0
|
|
|
|
// if(settings.minify) io.enable('browser client minification');
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2021-02-03 00:30:07 +01:00
|
|
|
// Initialize the Socket.IO Router
|
2012-02-25 16:44:37 +01:00
|
|
|
socketIORouter.setSocketIO(io);
|
2020-11-23 13:24:19 -05:00
|
|
|
socketIORouter.addComponent('pad', padMessageHandler);
|
2012-02-25 16:44:37 +01:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
hooks.callAll('socketio', {app: args.app, io, server: args.server});
|
2020-10-10 22:51:26 -04:00
|
|
|
|
|
|
|
return cb();
|
2020-11-23 13:24:19 -05:00
|
|
|
};
|