modified lib-jitsi-meet dev repo
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 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. 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.sendIQ(iq, success, error, timeout);
  66. }
  67. /* eslint-enable max-params */
  68. /**
  69. * Checks if given <tt>jid</tt> has XEP-0199 ping support.
  70. * @param jid the JID to be checked for ping support.
  71. * @param callback function with boolean argument which will be
  72. * <tt>true</tt> if XEP-0199 ping is supported by given <tt>jid</tt>
  73. */
  74. hasPingSupport(jid, callback) {
  75. this.xmpp.caps.getFeatures(jid).then(features =>
  76. callback(features.has('urn:xmpp:ping')), error => {
  77. const errmsg = 'Ping feature discovery error';
  78. GlobalOnErrorHandler.callErrorHandler(
  79. new Error(`${errmsg}: ${error}`));
  80. logger.error(errmsg, error);
  81. callback(false);
  82. });
  83. }
  84. /**
  85. * Starts to send ping in given interval to specified remote JID.
  86. * This plugin supports only one such task and <tt>stopInterval</tt>
  87. * must be called before starting a new one.
  88. * @param remoteJid remote JID to which ping requests will be sent to.
  89. * @param interval task interval in ms.
  90. */
  91. startInterval(remoteJid, interval = PING_INTERVAL) {
  92. if (this.intervalId) {
  93. const errmsg = 'Ping task scheduled already';
  94. GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
  95. logger.error(errmsg);
  96. return;
  97. }
  98. this.intervalId = window.setInterval(() => {
  99. this.ping(remoteJid, () => {
  100. this.failedPings = 0;
  101. }, error => {
  102. this.failedPings += 1;
  103. const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
  104. if (this.failedPings >= PING_THRESHOLD) {
  105. GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
  106. logger.error(errmsg, error);
  107. // FIXME it doesn't help to disconnect when 3rd PING
  108. // times out, it only stops Strophe from retrying.
  109. // Not really sure what's the right thing to do in that
  110. // situation, but just closing the connection makes no
  111. // sense.
  112. // self.connection.disconnect();
  113. } else {
  114. logger.warn(errmsg, error);
  115. }
  116. }, PING_TIMEOUT);
  117. }, interval);
  118. logger.info(`XMPP pings will be sent every ${interval} ms`);
  119. }
  120. /**
  121. * Stops current "ping" interval task.
  122. */
  123. stopInterval() {
  124. if (this.intervalId) {
  125. window.clearInterval(this.intervalId);
  126. this.intervalId = null;
  127. this.failedPings = 0;
  128. logger.info('Ping interval cleared');
  129. }
  130. }
  131. /**
  132. * Adds the current time to the array of send ping timestamps.
  133. * @private
  134. */
  135. _addPingExecutionTimestamp() {
  136. this.pingExecIntervals.push(new Date().getTime());
  137. // keep array length to PING_TIMESTAMPS_TO_KEEP
  138. if (this.pingExecIntervals.length > PING_TIMESTAMPS_TO_KEEP) {
  139. this.pingExecIntervals.shift();
  140. }
  141. }
  142. /**
  143. * Returns the maximum time between the recent sent pings, if there is a
  144. * big value it means the computer was inactive for some time(suspended).
  145. * Checks the maximum gap between sending pings, considering and the
  146. * current time. Trying to detect computer inactivity (sleep).
  147. *
  148. * @returns {int} the time ping was suspended, if it was not 0 is returned.
  149. */
  150. getPingSuspendTime() {
  151. const pingIntervals = this.pingExecIntervals.slice();
  152. // we need current time, as if ping was sent now
  153. // if computer sleeps we will get correct interval after next
  154. // scheduled ping, bet we sometimes need that interval before waiting
  155. // for the next ping, on closing the connection on error.
  156. pingIntervals.push(new Date().getTime());
  157. let maxInterval = 0;
  158. let previousTS = pingIntervals[0];
  159. pingIntervals.forEach(e => {
  160. const currentInterval = e - previousTS;
  161. if (currentInterval > maxInterval) {
  162. maxInterval = currentInterval;
  163. }
  164. previousTS = e;
  165. });
  166. // remove the interval between the ping sent
  167. // this way in normal execution there is no suspend and the return
  168. // will be 0 or close to 0.
  169. maxInterval -= PING_INTERVAL;
  170. // make sure we do not return less than 0
  171. return Math.max(maxInterval, 0);
  172. }
  173. }
  174. /**
  175. *
  176. * @param xmpp
  177. */
  178. export default function(xmpp) {
  179. Strophe.addConnectionPlugin('ping', new PingConnectionPlugin(xmpp));
  180. }