Browse Source

Create new config setting `stanza_timeout`

And move STANZA_TIMEOUT off `_converse` and into constants.js
JC Brand 2 years ago
parent
commit
4d2a8e9f8d

+ 2 - 0
CHANGES.md

@@ -9,6 +9,8 @@
 - Generate TypeScript declaration files into `dist/types`
 - Generate TypeScript declaration files into `dist/types`
 - #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode
 - #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode
 
 
+- New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#stanza-timeout)
+
 ## 10.1.2 (2023-02-17)
 ## 10.1.2 (2023-02-17)
 
 
 - #1490: Busy-loop when fetching registration form fails
 - #1490: Busy-loop when fetching registration form fails

+ 7 - 0
docs/source/configuration.rst

@@ -2089,6 +2089,13 @@ themselves).
 In order to support all browsers we need both an MP3 and an Ogg file. Make sure
 In order to support all browsers we need both an MP3 and an Ogg file. Make sure
 to name your files ``msg_received.ogg`` and ``msg_received.mp3``.
 to name your files ``msg_received.ogg`` and ``msg_received.mp3``.
 
 
+stanza_timeout
+--------------
+
+* Default: ``20000`` (20 seconds)
+
+The time to wait, in milliseconds, for a response stanza (for example to an IQ
+request), before a timeout error is thrown and Converse stops waiting.
 
 
 sticky_controlbox
 sticky_controlbox
 -----------------
 -----------------

+ 4 - 3
src/headless/plugins/muc/muc.js

@@ -5,17 +5,17 @@ import p from '../../utils/parse-helpers';
 import pick from 'lodash-es/pick';
 import pick from 'lodash-es/pick';
 import sizzle from 'sizzle';
 import sizzle from 'sizzle';
 import { Model } from '@converse/skeletor/src/model.js';
 import { Model } from '@converse/skeletor/src/model.js';
+import { ROOMSTATUS } from './constants.js';
 import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
 import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
 import { _converse, api, converse } from '../../core.js';
 import { _converse, api, converse } from '../../core.js';
 import { computeAffiliationsDelta, setAffiliations, getAffiliationList }  from './affiliations/utils.js';
 import { computeAffiliationsDelta, setAffiliations, getAffiliationList }  from './affiliations/utils.js';
-import { handleCorrection } from '../../shared/chat/utils.js';
 import { getOpenPromise } from '@converse/openpromise';
 import { getOpenPromise } from '@converse/openpromise';
+import { handleCorrection } from '../../shared/chat/utils.js';
 import { initStorage } from '../../utils/storage.js';
 import { initStorage } from '../../utils/storage.js';
 import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
 import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
 import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
 import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
 import { parseMUCMessage, parseMUCPresence } from './parsers.js';
 import { parseMUCMessage, parseMUCPresence } from './parsers.js';
 import { sendMarker } from '../../shared/actions.js';
 import { sendMarker } from '../../shared/actions.js';
-import { ROOMSTATUS } from './constants.js';
 
 
 const { u } = converse.env;
 const { u } = converse.env;
 
 
@@ -723,7 +723,8 @@ const ChatRoomMixin = {
             el.setAttribute('id', id);
             el.setAttribute('id', id);
         }
         }
         const promise = getOpenPromise();
         const promise = getOpenPromise();
-        const timeoutHandler = _converse.connection.addTimedHandler(_converse.STANZA_TIMEOUT, () => {
+        const timeout = api.settings.get('stanza_timeout');
+        const timeoutHandler = _converse.connection.addTimedHandler(timeout, () => {
             _converse.connection.deleteHandler(handler);
             _converse.connection.deleteHandler(handler);
             const err = new _converse.TimeoutError('Timeout Error: No response from server');
             const err = new _converse.TimeoutError('Timeout Error: No response from server');
             promise.resolve(err);
             promise.resolve(err);

+ 3 - 2
src/headless/plugins/roster/utils.js

@@ -1,6 +1,7 @@
 import log from "@converse/headless/log";
 import log from "@converse/headless/log";
 import { Model } from '@converse/skeletor/src/model.js';
 import { Model } from '@converse/skeletor/src/model.js';
 import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
 import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
+import { STATUS_WEIGHTS } from "../../shared/constants";
 import { _converse, api, converse } from "@converse/headless/core";
 import { _converse, api, converse } from "@converse/headless/core";
 import { initStorage } from '@converse/headless/utils/storage.js';
 import { initStorage } from '@converse/headless/utils/storage.js';
 import { shouldClearCache } from '@converse/headless/utils/core.js';
 import { shouldClearCache } from '@converse/headless/utils/core.js';
@@ -198,12 +199,12 @@ export function rejectPresenceSubscription (jid, message) {
 export function contactsComparator (contact1, contact2) {
 export function contactsComparator (contact1, contact2) {
     const status1 = contact1.presence.get('show') || 'offline';
     const status1 = contact1.presence.get('show') || 'offline';
     const status2 = contact2.presence.get('show') || 'offline';
     const status2 = contact2.presence.get('show') || 'offline';
-    if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) {
+    if (STATUS_WEIGHTS[status1] === STATUS_WEIGHTS[status2]) {
         const name1 = (contact1.getDisplayName()).toLowerCase();
         const name1 = (contact1.getDisplayName()).toLowerCase();
         const name2 = (contact2.getDisplayName()).toLowerCase();
         const name2 = (contact2.getDisplayName()).toLowerCase();
         return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
         return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
     } else  {
     } else  {
-        return _converse.STATUS_WEIGHTS[status1] < _converse.STATUS_WEIGHTS[status2] ? -1 : 1;
+        return STATUS_WEIGHTS[status1] < STATUS_WEIGHTS[status2] ? -1 : 1;
     }
     }
 }
 }
 
 

+ 3 - 2
src/headless/plugins/status/api.js

@@ -1,4 +1,5 @@
-import { _converse, api } from '@converse/headless/core';
+import { _converse, api } from '../../core';
+import { STATUS_WEIGHTS } from '../../shared/constants';
 
 
 
 
 export default {
 export default {
@@ -32,7 +33,7 @@ export default {
          */
          */
         async set (value, message) {
         async set (value, message) {
             const data = {'status': value};
             const data = {'status': value};
-            if (!Object.keys(_converse.STATUS_WEIGHTS).includes(value)) {
+            if (!Object.keys(STATUS_WEIGHTS).includes(value)) {
                 throw new Error(
                 throw new Error(
                     'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
                     'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
                 );
                 );

+ 1 - 17
src/headless/shared/_converse.js

@@ -1,7 +1,7 @@
 import i18n from './i18n.js';
 import i18n from './i18n.js';
 import log from '../log.js';
 import log from '../log.js';
 import pluggable from 'pluggable.js/src/pluggable.js';
 import pluggable from 'pluggable.js/src/pluggable.js';
-import { CONNECTION_STATUS, VERSION_NAME } from './constants';
+import { VERSION_NAME } from './constants';
 import { Events } from '@converse/skeletor/src/events.js';
 import { Events } from '@converse/skeletor/src/events.js';
 import { Router } from '@converse/skeletor/src/router.js';
 import { Router } from '@converse/skeletor/src/router.js';
 import { TimeoutError } from './errors.js';
 import { TimeoutError } from './errors.js';
@@ -21,7 +21,6 @@ const _converse = {
     log,
     log,
 
 
     shouldClearCache, // TODO: Should be moved to utils with next major release
     shouldClearCache, // TODO: Should be moved to utils with next major release
-    CONNECTION_STATUS, // TODO: remove in next major release
     VERSION_NAME,
     VERSION_NAME,
 
 
     templates: {},
     templates: {},
@@ -29,15 +28,6 @@ const _converse = {
         'initialized': getOpenPromise()
         'initialized': getOpenPromise()
     },
     },
 
 
-    STATUS_WEIGHTS: {
-        'offline':      6,
-        'unavailable':  5,
-        'xa':           4,
-        'away':         3,
-        'dnd':          2,
-        'chat':         1, // We currently don't differentiate between "chat" and "online"
-        'online':       1
-    },
     ANONYMOUS: 'anonymous',
     ANONYMOUS: 'anonymous',
     CLOSED: 'closed',
     CLOSED: 'closed',
     EXTERNAL: 'external',
     EXTERNAL: 'external',
@@ -46,12 +36,6 @@ const _converse = {
     OPENED: 'opened',
     OPENED: 'opened',
     PREBIND: 'prebind',
     PREBIND: 'prebind',
 
 
-    /**
-     * @constant
-     * @type { number }
-     */
-    STANZA_TIMEOUT: 20000,
-
     SUCCESS: 'success',
     SUCCESS: 'success',
     FAILURE: 'failure',
     FAILURE: 'failure',
 
 

+ 2 - 1
src/headless/shared/api/public.js

@@ -6,7 +6,7 @@ import i18n from '../i18n';
 import log from '../../log.js';
 import log from '../../log.js';
 import sizzle from 'sizzle';
 import sizzle from 'sizzle';
 import u, { setUnloadEvent } from '../../utils/core.js';
 import u, { setUnloadEvent } from '../../utils/core.js';
-import { CHAT_STATES, KEYCODES } from '../constants.js';
+import { CHAT_STATES, KEYCODES, VERSION_NAME } from '../constants.js';
 import { Collection } from "@converse/skeletor/src/collection";
 import { Collection } from "@converse/skeletor/src/collection";
 import { Model } from '@converse/skeletor/src/model.js';
 import { Model } from '@converse/skeletor/src/model.js';
 import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
 import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
@@ -185,6 +185,7 @@ export const converse = Object.assign(window.converse || {}, {
      * @memberOf converse
      * @memberOf converse
      */
      */
     'env': {
     'env': {
+        VERSION_NAME,
         $build,
         $build,
         $iq,
         $iq,
         $msg,
         $msg,

+ 5 - 4
src/headless/shared/api/send.js

@@ -1,4 +1,4 @@
-import _converse from '@converse/headless/shared/_converse.js';
+import _converse from '../../shared/_converse.js';
 import log from '../../log.js';
 import log from '../../log.js';
 import { Strophe } from 'strophe.js/src/strophe';
 import { Strophe } from 'strophe.js/src/strophe';
 import { TimeoutError } from '../errors.js';
 import { TimeoutError } from '../errors.js';
@@ -43,7 +43,8 @@ export default {
      * Send an IQ stanza
      * Send an IQ stanza
      * @method _converse.api.sendIQ
      * @method _converse.api.sendIQ
      * @param { Element } stanza
      * @param { Element } stanza
-     * @param { number } [timeout=_converse.STANZA_TIMEOUT]
+     * @param { number } [timeout] - The default timeout value is taken from
+     *  the `stanza_timeout` configuration setting.
      * @param { Boolean } [reject=true] - Whether an error IQ should cause the promise
      * @param { Boolean } [reject=true] - Whether an error IQ should cause the promise
      *  to be rejected. If `false`, the promise will resolve instead of being rejected.
      *  to be rejected. If `false`, the promise will resolve instead of being rejected.
      * @returns { Promise } A promise which resolves (or potentially rejected) once we
      * @returns { Promise } A promise which resolves (or potentially rejected) once we
@@ -51,13 +52,13 @@ export default {
      *  If the IQ stanza being sent is of type `result` or `error`, there's
      *  If the IQ stanza being sent is of type `result` or `error`, there's
      *  nothing to wait for, so an already resolved promise is returned.
      *  nothing to wait for, so an already resolved promise is returned.
      */
      */
-    sendIQ (stanza, timeout=_converse.STANZA_TIMEOUT, reject=true) {
+    sendIQ (stanza, timeout, reject=true) {
         const { api, connection } = _converse;
         const { api, connection } = _converse;
 
 
         let promise;
         let promise;
         stanza = stanza.tree?.() ?? stanza;
         stanza = stanza.tree?.() ?? stanza;
         if (['get', 'set'].includes(stanza.getAttribute('type'))) {
         if (['get', 'set'].includes(stanza.getAttribute('type'))) {
-            timeout = timeout || _converse.STANZA_TIMEOUT;
+            timeout = timeout || api.settings.get('stanza_timeout');
             if (reject) {
             if (reject) {
                 promise = new Promise((resolve, reject) => connection.sendIQ(stanza, resolve, reject, timeout));
                 promise = new Promise((resolve, reject) => connection.sendIQ(stanza, resolve, reject, timeout));
                 promise.catch((e) => {
                 promise.catch((e) => {

+ 14 - 4
src/headless/shared/constants.js

@@ -1,7 +1,17 @@
 import { Strophe } from 'strophe.js/src/strophe';
 import { Strophe } from 'strophe.js/src/strophe';
 
 
 export const BOSH_WAIT = 59;
 export const BOSH_WAIT = 59;
-export const VERSION_NAME = "v10.1.2";
+export const VERSION_NAME = 'v10.1.2';
+
+export const STATUS_WEIGHTS = {
+    offline: 6,
+    unavailable: 5,
+    xa: 4,
+    away: 3,
+    dnd: 2,
+    chat: 1, // We don't differentiate between "chat" and "online"
+    online: 1,
+};
 
 
 export const CONNECTION_STATUS = {};
 export const CONNECTION_STATUS = {};
 CONNECTION_STATUS[Strophe.Status.ATTACHED] = 'ATTACHED';
 CONNECTION_STATUS[Strophe.Status.ATTACHED] = 'ATTACHED';
@@ -73,7 +83,7 @@ export const CORE_PLUGINS = [
     'converse-roster',
     'converse-roster',
     'converse-smacks',
     'converse-smacks',
     'converse-status',
     'converse-status',
-    'converse-vcard'
+    'converse-vcard',
 ];
 ];
 
 
 export const URL_PARSE_OPTIONS = { 'start': /(\b|_)(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
 export const URL_PARSE_OPTIONS = { 'start': /(\b|_)(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
@@ -94,5 +104,5 @@ export const KEYCODES = {
     FORWARD_SLASH: 47,
     FORWARD_SLASH: 47,
     AT: 50,
     AT: 50,
     META: 91,
     META: 91,
-    META_RIGHT: 93
-}
+    META_RIGHT: 93,
+};

+ 1 - 0
src/headless/shared/settings/constants.js

@@ -101,6 +101,7 @@ export const DEFAULT_SETTINGS = {
     sid: undefined,
     sid: undefined,
     singleton: false,
     singleton: false,
     strict_plugin_dependencies: false,
     strict_plugin_dependencies: false,
+    stanza_timeout: 20000,
     view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile'
     view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile'
     websocket_url: undefined,
     websocket_url: undefined,
     whitelisted_plugins: [],
     whitelisted_plugins: [],

+ 1 - 3
src/plugins/muc-views/tests/retractions.js

@@ -661,9 +661,7 @@ describe("Message Retractions", function () {
         }));
         }));
 
 
         it("can be retracted by its author, causing a timeout error in response",
         it("can be retracted by its author, causing a timeout error in response",
-                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
-
-            _converse.STANZA_TIMEOUT = 1;
+                mock.initConverse(['chatBoxesFetched'], { stanza_timeout: 1 }, async function (_converse) {
 
 
             const muc_jid = 'lounge@montague.lit';
             const muc_jid = 'lounge@montague.lit';
             const features = [...mock.default_muc_features, Strophe.NS.MODERATE];
             const features = [...mock.default_muc_features, Strophe.NS.MODERATE];