import config from "config"; import cookie from "cookie"; import cookieParser from "cookie-parser"; import {Express, Request} from "express"; import {Session} from "express-session"; import {WebSocketServer} from "ws"; import Application from "../Application.js"; import ApplicationComponent from "../ApplicationComponent.js"; import {logger} from "../Logger.js"; import WebSocketListener from "../WebSocketListener.js"; import ExpressAppComponent from "./ExpressAppComponent.js"; import FrontendToolsComponent from "./FrontendToolsComponent.js"; import RedisComponent from "./RedisComponent.js"; export default class WebSocketServerComponent extends ApplicationComponent { private wss?: WebSocketServer; public async init(): Promise { const app = this.getApp(); app.require(ExpressAppComponent); app.require(RedisComponent); const globals = app.asOptional(FrontendToolsComponent)?.getGlobals(); if (globals) { globals.set('websocketUrl', config.get('app.public_websocket_url')); } } public async start(_app: Express): Promise { const app = this.getApp(); const listeners: { [p: string]: WebSocketListener } = app.getWebSocketListeners(); this.wss = new WebSocketServer({ server: app.as(ExpressAppComponent).getServer(), }, () => { logger.info(`Websocket server started over webserver.`); }).on('error', (err) => { logger.error(err, 'An error occurred in the websocket server.'); }).on('connection', (socket, request) => { const listener = request.url ? listeners[request.url] : null; if (!listener) { socket.close(1002, `Path not found ${request.url}`); return; } else if (!request.headers.cookie) { listener.handle(socket, request, null).catch(err => { logger.error(err, 'Error in websocket listener.'); }); return; } logger.debug(`Websocket on ${request.url}`); const cookies = cookie.parse(request.headers.cookie); const sid = cookieParser.signedCookie(cookies['connect.sid'], config.get('session.secret')); if (!sid) { socket.close(1002); return; } const store = app.as(RedisComponent).getStore(); store.get(sid, (err, session) => { if (err || !session) { logger.error(err, 'Error while initializing session in websocket.'); socket.close(1011); return; } session.id = sid; store.createSession(request, session); listener.handle(socket, request, session as Session).catch(err => { logger.error(err, 'Error in websocket listener.'); }); }); }); } public async stop(): Promise { const wss = this.wss; if (wss) { await this.close('WebSocket server', callback => wss.close(callback)); } } }