You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

strophe.ping.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { getLogger } from 'jitsi-meet-logger';
  2. import { $iq, Strophe } from 'strophe.js';
  3. import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
  4. import ConnectionPlugin from './ConnectionPlugin';
  5. const logger = getLogger(__filename);
  6. /**
  7. * Ping every 10 sec
  8. */
  9. const PING_INTERVAL = 10000;
  10. /**
  11. * Ping timeout error after 15 sec of waiting.
  12. */
  13. const PING_TIMEOUT = 15000;
  14. /**
  15. * Will close the connection after 3 consecutive ping errors.
  16. */
  17. const PING_THRESHOLD = 3;
  18. /**
  19. * The number of timestamps of send pings to keep.
  20. * The current value is 2 minutes.
  21. * @type {number} number of timestamps.
  22. */
  23. const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
  24. /**
  25. * XEP-0199 ping plugin.
  26. *
  27. * Registers "urn:xmpp:ping" namespace under Strophe.NS.PING.
  28. */
  29. export default class PingConnectionPlugin extends ConnectionPlugin {
  30. /**
  31. * Contructs new object
  32. * @param {XMPP} xmpp the xmpp module.
  33. * @constructor
  34. */
  35. constructor(xmpp) {
  36. super();
  37. this.failedPings = 0;
  38. this.xmpp = xmpp;
  39. this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);
  40. }
  41. /**
  42. * Initializes the plugin. Method called by Strophe.
  43. * @param connection Strophe connection instance.
  44. */
  45. init(connection) {
  46. super.init(connection);
  47. Strophe.addNamespace('PING', 'urn:xmpp:ping');
  48. }
  49. /* eslint-disable max-params */
  50. /**
  51. * Sends "ping" to given <tt>jid</tt>
  52. * @param jid the JID to which ping request will be sent.
  53. * @param success callback called on success.
  54. * @param error callback called on error.
  55. * @param timeout ms how long are we going to wait for the response. On
  56. * timeout <tt>error<//t> callback is called with undefined error argument.
  57. */
  58. ping(jid, success, error, timeout) {
  59. this._addPingExecutionTimestamp();
  60. const iq = $iq({
  61. type: 'get',
  62. to: jid
  63. });
  64. iq.c('ping', { xmlns: Strophe.NS.PING });
  65. this.connection.sendIQ2(iq, { timeout })
  66. .then(success, error);
  67. }
  68. /* eslint-enable max-params */
  69. /**
  70. * Starts to send ping in given interval to specified remote JID.
  71. * This plugin supports only one such task and <tt>stopInterval</tt>
  72. * must be called before starting a new one.
  73. * @param remoteJid remote JID to which ping requests will be sent to.
  74. * @param interval task interval in ms.
  75. */
  76. startInterval(remoteJid, interval = PING_INTERVAL) {
  77. clearInterval(this.intervalId);
  78. this.intervalId = window.setInterval(() => {
  79. this.ping(remoteJid, () => {
  80. this.failedPings = 0;
  81. }, error => {
  82. this.failedPings += 1;
  83. const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
  84. if (this.failedPings >= PING_THRESHOLD) {
  85. GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
  86. logger.error(errmsg, error);
  87. // FIXME it doesn't help to disconnect when 3rd PING
  88. // times out, it only stops Strophe from retrying.
  89. // Not really sure what's the right thing to do in that
  90. // situation, but just closing the connection makes no
  91. // sense.
  92. // self.connection.disconnect();
  93. } else {
  94. logger.warn(errmsg, error);
  95. }
  96. }, PING_TIMEOUT);
  97. }, interval);
  98. logger.info(`XMPP pings will be sent every ${interval} ms`);
  99. }
  100. /**
  101. * Stops current "ping" interval task.
  102. */
  103. stopInterval() {
  104. if (this.intervalId) {
  105. window.clearInterval(this.intervalId);
  106. this.intervalId = null;
  107. this.failedPings = 0;
  108. logger.info('Ping interval cleared');
  109. }
  110. }
  111. /**
  112. * Adds the current time to the array of send ping timestamps.
  113. * @private
  114. */
  115. _addPingExecutionTimestamp() {
  116. this.pingExecIntervals.push(new Date().getTime());
  117. // keep array length to PING_TIMESTAMPS_TO_KEEP
  118. if (this.pingExecIntervals.length > PING_TIMESTAMPS_TO_KEEP) {
  119. this.pingExecIntervals.shift();
  120. }
  121. }
  122. /**
  123. * Returns the maximum time between the recent sent pings, if there is a
  124. * big value it means the computer was inactive for some time(suspended).
  125. * Checks the maximum gap between sending pings, considering and the
  126. * current time. Trying to detect computer inactivity (sleep).
  127. *
  128. * @returns {int} the time ping was suspended, if it was not 0 is returned.
  129. */
  130. getPingSuspendTime() {
  131. const pingIntervals = this.pingExecIntervals.slice();
  132. // we need current time, as if ping was sent now
  133. // if computer sleeps we will get correct interval after next
  134. // scheduled ping, bet we sometimes need that interval before waiting
  135. // for the next ping, on closing the connection on error.
  136. pingIntervals.push(new Date().getTime());
  137. let maxInterval = 0;
  138. let previousTS = pingIntervals[0];
  139. pingIntervals.forEach(e => {
  140. const currentInterval = e - previousTS;
  141. if (currentInterval > maxInterval) {
  142. maxInterval = currentInterval;
  143. }
  144. previousTS = e;
  145. });
  146. // remove the interval between the ping sent
  147. // this way in normal execution there is no suspend and the return
  148. // will be 0 or close to 0.
  149. maxInterval -= PING_INTERVAL;
  150. // make sure we do not return less than 0
  151. return Math.max(maxInterval, 0);
  152. }
  153. }