import * as fs from "fs"; import * as https from "https"; import { WebSocketServer } from "ws"; import * as crypto from "crypto"; import type { AckMessage, TextMessage } from "./lib/ServerMessage"; import { isServerMessage, isTextMessage, MessageType, } from "./lib/ServerMessage"; const SALT = crypto .randomBytes(Math.round(Math.random() * 1024)) .toString("hex"); const port = 8085; const timeout = 15000; const httpsServer = https.createServer({ key: fs.readFileSync("./key.pem"), cert: fs.readFileSync("./cert.pem"), }); const webSocketServer = new WebSocketServer({ server: httpsServer }); console.log("listening on port: " + port); function hash(str: string) { return crypto.createHash("SHAKE256").update(str, "utf-8").digest("hex"); } async function handleTextMessage(message: TextMessage, from: string) { for (const to of webSocketServer.clients) { to.send( JSON.stringify( Object.assign({}, message, { author: from, }) ) ); } } webSocketServer.on("connection", function connection(socket, request) { const close = (reason: string, code: number = 1000) => { socket.send( JSON.stringify({ type: MessageType.ACK, date: Date.now(), __ctx: `closing connection. reason: ${reason}`, } as AckMessage) ); socket.close(code, `closing connection. reason: ${reason}`); }; let authorID: string; { let ip: string | undefined; if (request.headers["x-forwarded-for"] !== undefined) { const forwardedFor = request.headers["x-forwarded-for"]; ip = typeof forwardedFor === "string" ? forwardedFor.split(",")[0]?.trim() : forwardedFor[0]; } else { ip = request.socket.remoteAddress; } if (ip === undefined) { close("could not generate author id", 1008); console.error("connection without IP. closing."); return; } authorID = hash(ip + SALT); } socket.on("message", function (rawMessage: string) { const message = JSON.parse(rawMessage); if (!isServerMessage(message)) { console.error(`Unexpected message received from client \`${message}\``); } if (isTextMessage(message)) { handleTextMessage(message, authorID); } clearTimeout(closeTimeout); closeTimeout = setTimeout(close, timeout); }); socket.on("close", function close() { console.log("closed a connection"); }); console.log("new client connected! ID:", authorID); socket.send( JSON.stringify({ type: MessageType.ACK, date: Date.now(), __ctx: "connected successfully", } as AckMessage) ); let closeTimeout = setTimeout(close, timeout); }); httpsServer.listen(port);