Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

ResumeTask.ts 5.6KB

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