import config from "config"; import cookie from "cookie"; import cookieParser from "cookie-parser"; import {Express, Request} from "express"; import {Session} from "express-session"; import WebSocket from "ws"; import Application from "../Application.js"; import ApplicationComponent from "../ApplicationComponent.js"; import ViewEngine from "../frontend/ViewEngine.js"; import {logger} from "../Logger.js"; import WebSocketListener from "../WebSocketListener.js"; import ExpressAppComponent from "./ExpressAppComponent.js"; import RedisComponent from "./RedisComponent.js"; export default class WebSocketServerComponent extends ApplicationComponent { private wss?: WebSocket.Server; public constructor( private readonly application: Application, private readonly expressAppComponent: ExpressAppComponent, private readonly storeComponent: RedisComponent, ) { super(); ViewEngine.setGlobal('websocketUrl', config.get('public_websocket_url')); } public async start(_app: Express): Promise { const listeners: { [p: string]: WebSocketListener } = this.application.getWebSocketListeners(); this.wss = new WebSocket.Server({ server: this.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 = this.storeComponent.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)); } } }