From a5c1900e3f57f96c1f246c672d2944cd1beba40c Mon Sep 17 00:00:00 2001 From: hippoz Date: Wed, 8 Sep 2021 00:18:44 +0300 Subject: [PATCH] add gateway pings --- brainlet/api/v2/gateway/index.js | 42 ++++++++++++++---------- brainlet/api/v2/gateway/messageparser.js | 2 +- brainlet/config.js | 5 ++- brainlet/index.js | 2 +- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/brainlet/api/v2/gateway/index.js b/brainlet/api/v2/gateway/index.js index 95de834..106fef6 100644 --- a/brainlet/api/v2/gateway/index.js +++ b/brainlet/api/v2/gateway/index.js @@ -3,7 +3,7 @@ const EventEmitter = require("events"); const uuid = require("uuid"); const werift = require("werift"); -const { policies } = require("../../../config"); +const { policies, gatewayPingInterval, gatewayPingCheckInterval, clientFacingPingInterval } = require("../../../config"); const { experiments } = require("../../../experiments"); const User = require("../../../models/User"); const Channel = require("../../../models/Channel"); @@ -18,9 +18,9 @@ const wsCloseCodes = { TOO_MANY_SESSIONS: [4005, "Too many sessions"], NOT_AUTHORIZED: [4006, "Not authorized"], FLOODING: [4007, "Flooding"], + NO_PING: [4008, "No ping"], }; const closeConnectionWithCode = (ws, code) => ws.close(code[0], code[1]); -const pingCheckDelay = 10000; class GatewayRTCConnection { constructor(ws, server) { @@ -56,36 +56,31 @@ class GatewayServer extends EventEmitter { this.wss = new websockets.Server({ server: server, path: "/gateway" }); this.sessionCounters = {}; - this.pingInterval = setInterval(() => { + this.pingCheckIntervalFunction = setInterval(() => { this.wss.clients.forEach((client) => { - if (!client.alive) { - client.terminate(); + if ((new Date() - client.session.lastPing) >= gatewayPingInterval) { + closeConnectionWithCode(client, wsCloseCodes.NO_PING); } - client.alive = false; - client.ping(() => {}); }); - }, pingCheckDelay); + }, gatewayPingCheckInterval); this.wss.on("close", () => { - clearInterval(this.pingInterval); + clearInterval(this.pingCheckIntervalFunction); console.log("gateway: websocket server closed"); }); this.wss.on("connection", (ws) => { if (!policies.allowGatewayConnection) return closeConnectionWithCode(ws, wsCloseCodes.SERVER_DENIED_CONNECTION); - ws.send(packet("HELLO", {})); + ws.send(packet("HELLO", { pingInterval: clientFacingPingInterval })); ws.session = { authenticated: false, user: null, - sessionId: uuid.v4() + sessionId: uuid.v4(), + lastPing: new Date(), + token: null }; - ws.alive = true; - ws.on("pong", () => { - ws.alive = true; - }); - ws.on("close", async (code, data) => { - console.log(code, data.toString()); + ws.on("close", async () => { if (ws.session.user && ws.channels) { if (this.sessionCounters[ws.session.user._id] <= 1) { this.inChannel(ws.channels[0], (client) => { @@ -107,7 +102,7 @@ class GatewayServer extends EventEmitter { }); ws.on("message", async (data, isBinary) => { try { - if (isBinary) return closeConnectionWithCode(wsCloseCodes.PAYLOAD_ERROR); + if (isBinary) return closeConnectionWithCode(ws, wsCloseCodes.PAYLOAD_ERROR); const message = parseMessage(data.toString()); switch (message.opcodeType) { case "YOO": { @@ -117,6 +112,7 @@ class GatewayServer extends EventEmitter { if (!user) return closeConnectionWithCode(ws, wsCloseCodes.AUTHENTICATION_ERROR); ws.session.user = user; ws.session.authenticated = true; + ws.session.token = message.data.token; if (!this.sessionCounters[user._id]) this.sessionCounters[user._id] = 0; this.sessionCounters[user._id]++; @@ -164,6 +160,16 @@ class GatewayServer extends EventEmitter { } break; } + case "ACTION_PING": { + if (!this.authMessage(ws)) return; + if (message.data !== "1") throw new Error("msg: ACTION_PING payload data should be a `1`"); + + const user = await checkToken(ws.session.token); + if (!user) return closeConnectionWithCode(ws, wsCloseCodes.AUTHENTICATION_ERROR); + + ws.session.lastPing = new Date(); + break; + } case "ACTION_CREATE_MESSAGE": { if (!this.authMessage(ws)) return; if (typeof message.data.content !== "string" || typeof message.data.channel !== "object" || typeof message.data.channel._id !== "string") throw new Error("msg: invalid fields in json payload"); diff --git a/brainlet/api/v2/gateway/messageparser.js b/brainlet/api/v2/gateway/messageparser.js index 217e819..eb649c5 100644 --- a/brainlet/api/v2/gateway/messageparser.js +++ b/brainlet/api/v2/gateway/messageparser.js @@ -6,7 +6,7 @@ const opcodes = { 4: { name: "EVENT_CREATE_MESSAGE", data: "JSON" }, 5: { name: "ACTION_UPDATE_STATUS", data: "JSON" }, 6: { name: "EVENT_CHANNEL_MEMBERS", data: "JSON" }, - 7: { name: "EVENT_CHANNEL_MEMBER_UPDATE", data: "JSON" }, + 7: { name: "ACTION_PING", data: "string" }, 21: { name: "ACTION_VOICE_REQUEST_SESSION", data: "JSON" }, 22: { name: "EVENT_VOICE_ASSIGN_SERVER", data: "JSON" }, 23: { name: "ACTION_VOICE_CONNECTION_REQUEST", data: "JSON" }, diff --git a/brainlet/config.js b/brainlet/config.js index 0fd5d88..8a17772 100755 --- a/brainlet/config.js +++ b/brainlet/config.js @@ -32,7 +32,10 @@ module.exports = { }, */ address: "localhost", - tokenExpiresIn: "8h", + tokenExpiresIn: "2m", + gatewayPingInterval: 15000, + gatewayPingCheckInterval: 4500, + clientFacingPingInterval: 14750, bcryptRounds: 10, roleMap: { "BANNED": 0, diff --git a/brainlet/index.js b/brainlet/index.js index 243e890..4a703ce 100755 --- a/brainlet/index.js +++ b/brainlet/index.js @@ -31,7 +31,7 @@ app.use(express.static("app")); app.use((err, req, res, next) => { console.error("error: server: internal server error", err); - res.status(500).json({ error: true, status: 500, message: "ERR_INTERNAL_SERVER_ERROR" }); + res.status(500).json({ error: true, status: 500, message: "ERROR_INTERNAL_SERVER_ERROR" }); }); httpServer.listen(config.ports.mainServerPort, () => {