/* global $iq, Strophe */ import { getLogger } from "jitsi-meet-logger"; const logger = getLogger(__filename); import ConnectionPlugin from "./ConnectionPlugin"; import GlobalOnErrorHandler from "../util/GlobalOnErrorHandler"; /** * Ping every 10 sec */ const PING_INTERVAL = 10000; /** * Ping timeout error after 15 sec of waiting. */ const PING_TIMEOUT = 15000; /** * Will close the connection after 3 consecutive ping errors. */ const PING_THRESHOLD = 3; /** * XEP-0199 ping plugin. * * Registers "urn:xmpp:ping" namespace under Strophe.NS.PING. */ class PingConnectionPlugin extends ConnectionPlugin { /** * Contructs new object * @param {XMPP} xmpp the xmpp module. * @constructor */ constructor(xmpp) { super(); this.failedPings = 0; this.xmpp = xmpp; } /** * Initializes the plugin. Method called by Strophe. * @param connection Strophe connection instance. */ init (connection) { super.init(connection); Strophe.addNamespace('PING', "urn:xmpp:ping"); } /** * Sends "ping" to given jid * @param jid the JID to which ping request will be sent. * @param success callback called on success. * @param error callback called on error. * @param timeout ms how long are we going to wait for the response. On * timeout error callback is called with undefined error * argument. */ ping (jid, success, error, timeout) { const iq = $iq({type: 'get', to: jid}); iq.c('ping', {xmlns: Strophe.NS.PING}); this.connection.sendIQ(iq, success, error, timeout); } /** * Checks if given jid has XEP-0199 ping support. * @param jid the JID to be checked for ping support. * @param callback function with boolean argument which will be * true if XEP-0199 ping is supported by given jid */ hasPingSupport (jid, callback) { this.xmpp.caps.getFeatures(jid).then(features => callback(features.has("urn:xmpp:ping")), error => { const errmsg = "Ping feature discovery error"; GlobalOnErrorHandler.callErrorHandler(new Error( errmsg + ": " + error)); logger.error(errmsg, error); callback(false); }); } /** * Starts to send ping in given interval to specified remote JID. * This plugin supports only one such task and stopInterval * must be called before starting a new one. * @param remoteJid remote JID to which ping requests will be sent to. * @param interval task interval in ms. */ startInterval (remoteJid, interval = PING_INTERVAL) { if (this.intervalId) { const errmsg = "Ping task scheduled already"; GlobalOnErrorHandler.callErrorHandler(new Error(errmsg)); logger.error(errmsg); return; } this.intervalId = window.setInterval(() => { this.ping(remoteJid, () => { this.failedPings = 0; }, (error) => { this.failedPings += 1; const errmsg = "Ping " + (error ? "error" : "timeout"); if (this.failedPings >= PING_THRESHOLD) { GlobalOnErrorHandler.callErrorHandler(new Error(errmsg)); logger.error(errmsg, error); // FIXME it doesn't help to disconnect when 3rd PING // times out, it only stops Strophe from retrying. // Not really sure what's the right thing to do in that // situation, but just closing the connection makes no // sense. //self.connection.disconnect(); } else { logger.warn(errmsg, error); } }, PING_TIMEOUT); }, interval); logger.info("XMPP pings will be sent every " + interval + " ms"); } /** * Stops current "ping" interval task. */ stopInterval () { if (this.intervalId) { window.clearInterval(this.intervalId); this.intervalId = null; this.failedPings = 0; logger.info("Ping interval cleared"); } } } export default function (xmpp) { Strophe.addConnectionPlugin('ping', new PingConnectionPlugin(xmpp)); }