Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ResumeTask.js 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { getLogger } from '@jitsi/logger';
  2. import {
  3. NETWORK_INFO_EVENT,
  4. default as NetworkInfo
  5. } from '../connectivity/NetworkInfo';
  6. import { getJitterDelay } from '../util/Retry';
  7. const logger = getLogger(__filename);
  8. /**
  9. * The class contains the logic for triggering connection resume via XEP-0198 stream management.
  10. * It does two things, the first one is it tracks the internet online/offline status and it makes sure that
  11. * the reconnect is attempted only while online. The seconds thing is that it tracks the retry attempts and extends
  12. * the retry interval using the full jitter pattern.
  13. */
  14. export default class ResumeTask {
  15. /**
  16. * Initializes new {@code RetryTask}.
  17. * @param {Strophe.Connection} stropheConnection - The Strophe connection instance.
  18. */
  19. constructor(stropheConnection) {
  20. this._stropheConn = stropheConnection;
  21. /**
  22. * The counter increased before each resume retry attempt, used to calculate exponential backoff.
  23. * @type {number}
  24. * @private
  25. */
  26. this._resumeRetryN = 0;
  27. this._retryDelay = undefined;
  28. }
  29. /**
  30. * @returns {number|undefined} - How much the app will wait before trying to resume the XMPP connection. When
  31. * 'undefined' it means that no resume task was not scheduled.
  32. */
  33. get retryDelay() {
  34. return this._retryDelay;
  35. }
  36. /**
  37. * Called by {@link XmppConnection} when the connection drops and it's a signal it wants to schedule a reconnect.
  38. *
  39. * @returns {void}
  40. */
  41. schedule() {
  42. this._cancelResume();
  43. this._removeNetworkOnlineListener();
  44. this._resumeRetryN += 1;
  45. this._networkOnlineListener
  46. = NetworkInfo.addCancellableListener(
  47. NETWORK_INFO_EVENT,
  48. ({ isOnline }) => {
  49. if (isOnline) {
  50. this._scheduleResume();
  51. } else {
  52. this._cancelResume();
  53. }
  54. });
  55. NetworkInfo.isOnline() && this._scheduleResume();
  56. }
  57. /**
  58. * Schedules a delayed timeout which will execute the resume action.
  59. * @private
  60. * @returns {void}
  61. */
  62. _scheduleResume() {
  63. if (this._resumeTimeout) {
  64. // NO-OP
  65. return;
  66. }
  67. // The retry delay will be:
  68. // 1st retry: 1.5s - 3s
  69. // 2nd retry: 3s - 9s
  70. // 3rd and next retry: 4.5s - 27s
  71. this._resumeRetryN = Math.min(3, this._resumeRetryN);
  72. this._retryDelay = getJitterDelay(
  73. /* retry */ this._resumeRetryN,
  74. /* minDelay */ this._resumeRetryN * 1500,
  75. 3);
  76. logger.info(`Will try to resume the XMPP connection in ${this.retryDelay}ms`);
  77. this._resumeTimeout = setTimeout(() => this._resumeConnection(), this.retryDelay);
  78. }
  79. /**
  80. * Cancels the delayed resume task.
  81. *
  82. * @private
  83. * @returns {void}
  84. */
  85. _cancelResume() {
  86. if (this._resumeTimeout) {
  87. logger.info('Canceling connection resume task');
  88. clearTimeout(this._resumeTimeout);
  89. this._resumeTimeout = undefined;
  90. this._retryDelay = undefined;
  91. }
  92. }
  93. /**
  94. * Removes network online listener for the NETWORK_INFO_EVENT event.
  95. *
  96. * @private
  97. * @returns {void}
  98. */
  99. _removeNetworkOnlineListener() {
  100. if (this._networkOnlineListener) {
  101. this._networkOnlineListener();
  102. this._networkOnlineListener = null;
  103. }
  104. }
  105. /**
  106. * Resumes the XMPP connection using the stream management plugin.
  107. *
  108. * @private
  109. * @returns {void}
  110. */
  111. _resumeConnection() {
  112. const { streamManagement } = this._stropheConn;
  113. const resumeToken = streamManagement.getResumeToken();
  114. // Things may have changed since when the task was scheduled
  115. if (!resumeToken) {
  116. return;
  117. }
  118. logger.info('Trying to resume the XMPP connection');
  119. const url = new URL(this._stropheConn.service);
  120. let { search } = url;
  121. const pattern = /(previd=)([\w-]+)/;
  122. const oldToken = search.match(pattern);
  123. // Replace previd if the previd value has changed.
  124. if (oldToken && oldToken.indexOf(resumeToken) === -1) {
  125. search = search.replace(pattern, `$1${resumeToken}`);
  126. // Append previd if it doesn't exist.
  127. } else if (!oldToken) {
  128. search += search.indexOf('?') === -1 ? `?previd=${resumeToken}` : `&previd=${resumeToken}`;
  129. }
  130. url.search = search;
  131. this._stropheConn.service = url.toString();
  132. try {
  133. streamManagement.resume();
  134. } catch (e) {
  135. logger.error('Failed to resume XMPP connnection', e);
  136. }
  137. }
  138. /**
  139. * Cancels the retry task. It's called by {@link XmppConnection} when it's no longer interested in reconnecting for
  140. * example when the disconnect method is called.
  141. *
  142. * @returns {void}
  143. */
  144. cancel() {
  145. this._cancelResume();
  146. this._removeNetworkOnlineListener();
  147. this._resumeRetryN = 0;
  148. }
  149. }