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