Browse Source

feat: Skips using disco-info for features. (#1450)

* fix: Drops unused parameter of join and sendPresence.

* feat: Skips using disco-info for features.

Uses presence to publish features added externally.
Recognizes jigasi participants using a specific presence extension used by jigasi.

* squash: Fixes tests.

* squash: Adds e2ee to the features in presence.

* squash: Fix function name and docs.

* squash: Drops detecting jigasi from initiator.

Using the newly added presence features.

* squash: Drops unused var.

* squash: Fix comments.

* squash: Adds a constant and for E2EEE feature.
tags/v0.0.2
Дамян Минков 3 years ago
parent
commit
1fd7256553
No account linked to committer's email address

+ 12
- 4
JitsiConference.js View File

@@ -42,7 +42,11 @@ import RandomUtil from './modules/util/RandomUtil';
42 42
 import ComponentsVersions from './modules/version/ComponentsVersions';
43 43
 import VideoSIPGW from './modules/videosipgw/VideoSIPGW';
44 44
 import * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';
45
-import { JITSI_MEET_MUC_TYPE } from './modules/xmpp/xmpp';
45
+import {
46
+    FEATURE_E2EE,
47
+    FEATURE_JIGASI,
48
+    JITSI_MEET_MUC_TYPE
49
+} from './modules/xmpp/xmpp';
46 50
 import * as MediaType from './service/RTC/MediaType';
47 51
 import VideoType from './service/RTC/VideoType';
48 52
 import {
@@ -1506,9 +1510,11 @@ JitsiConference.prototype.muteParticipant = function(id) {
1506 1510
  * @param status the initial status if any
1507 1511
  * @param identity the member identity, if any
1508 1512
  * @param botType the member botType, if any
1513
+ * @param fullJid the member full jid, if any
1514
+ * @param features the member botType, if any
1509 1515
  */
1510 1516
 JitsiConference.prototype.onMemberJoined = function(
1511
-        jid, nick, role, isHidden, statsID, status, identity, botType) {
1517
+        jid, nick, role, isHidden, statsID, status, identity, botType, fullJid, features) {
1512 1518
     const id = Strophe.getResourceFromJid(jid);
1513 1519
 
1514 1520
     if (id === 'focus' || this.myUserId() === id) {
@@ -1520,6 +1526,8 @@ JitsiConference.prototype.onMemberJoined = function(
1520 1526
 
1521 1527
     participant._role = role;
1522 1528
     participant._botType = botType;
1529
+    participant._features = features || new Set();
1530
+
1523 1531
     this.participants[id] = participant;
1524 1532
     this.eventEmitter.emit(
1525 1533
         JitsiConferenceEvents.USER_JOINED,
@@ -1561,11 +1569,11 @@ JitsiConference.prototype._updateFeatures = function(participant) {
1561 1569
             participant._supportsDTMF = features.has('urn:xmpp:jingle:dtmf:0');
1562 1570
             this.updateDTMFSupport();
1563 1571
 
1564
-            if (features.has('http://jitsi.org/protocol/jigasi')) {
1572
+            if (features.has(FEATURE_JIGASI)) {
1565 1573
                 participant.setProperty('features_jigasi', true);
1566 1574
             }
1567 1575
 
1568
-            if (features.has('https://jitsi.org/meet/e2ee')) {
1576
+            if (features.has(FEATURE_E2EE)) {
1569 1577
                 participant.setProperty('features_e2ee', true);
1570 1578
             }
1571 1579
         })

+ 9
- 15
JitsiConferenceEventManager.js View File

@@ -577,9 +577,9 @@ JitsiConferenceEventManager.prototype.removeXMPPListeners = function() {
577 577
     const conference = this.conference;
578 578
 
579 579
     conference.xmpp.caps.removeListener(
580
-        XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
581
-        this.xmppListeners[XMPPEvents.PARTCIPANT_FEATURES_CHANGED]);
582
-    delete this.xmppListeners[XMPPEvents.PARTCIPANT_FEATURES_CHANGED];
580
+        XMPPEvents.PARTICIPANT_FEATURES_CHANGED,
581
+        this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED]);
582
+    delete this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED];
583 583
 
584 584
     Object.keys(this.xmppListeners).forEach(eventName => {
585 585
         conference.xmpp.removeListener(
@@ -596,23 +596,17 @@ JitsiConferenceEventManager.prototype.removeXMPPListeners = function() {
596 596
 JitsiConferenceEventManager.prototype.setupXMPPListeners = function() {
597 597
     const conference = this.conference;
598 598
 
599
-    const featuresChangedListener = from => {
600
-        const participant
601
-            = conference.getParticipantById(
602
-            Strophe.getResourceFromJid(from));
599
+    const featuresChangedListener = (from, features) => {
600
+        const participant = conference.getParticipantById(Strophe.getResourceFromJid(from));
603 601
 
604 602
         if (participant) {
605
-            conference.eventEmitter.emit(
606
-                JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
607
-                participant);
603
+            participant._features = features;
604
+            conference.eventEmitter.emit(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED, participant);
608 605
         }
609 606
     };
610 607
 
611
-    conference.xmpp.caps.addListener(
612
-        XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
613
-        featuresChangedListener);
614
-    this.xmppListeners[XMPPEvents.PARTCIPANT_FEATURES_CHANGED]
615
-        = featuresChangedListener;
608
+    conference.xmpp.caps.addListener(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, featuresChangedListener);
609
+    this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED] = featuresChangedListener;
616 610
 
617 611
     this._addConferenceXMPPListener(
618 612
         XMPPEvents.CALL_INCOMING,

+ 2
- 2
JitsiConnection.js View File

@@ -150,7 +150,7 @@ JitsiConnection.prototype.getConnectionTimes = function() {
150 150
  * immediately submitted to the others.
151 151
  */
152 152
 JitsiConnection.prototype.addFeature = function(feature, submit = false) {
153
-    return this.xmpp.caps.addFeature(feature, submit);
153
+    this.xmpp.caps.addFeature(feature, submit, true);
154 154
 };
155 155
 
156 156
 /**
@@ -161,7 +161,7 @@ JitsiConnection.prototype.addFeature = function(feature, submit = false) {
161 161
  * immediately submitted to the others.
162 162
  */
163 163
 JitsiConnection.prototype.removeFeature = function(feature, submit = false) {
164
-    return this.xmpp.caps.removeFeature(feature, submit);
164
+    this.xmpp.caps.removeFeature(feature, submit, true);
165 165
 };
166 166
 
167 167
 /**

+ 10
- 1
JitsiParticipant.js View File

@@ -45,6 +45,7 @@ export default class JitsiParticipant {
45 45
         this._connectionStatus = ParticipantConnectionStatus.ACTIVE;
46 46
         this._properties = {};
47 47
         this._identity = identity;
48
+        this._features = new Set();
48 49
     }
49 50
 
50 51
     /* eslint-enable max-params */
@@ -235,12 +236,20 @@ export default class JitsiParticipant {
235 236
         return this._supportsDTMF;
236 237
     }
237 238
 
239
+    /**
240
+     * Returns a set with the features for the participant.
241
+     * @returns {Promise<Set<String>, Error>}
242
+     */
243
+    getFeatures() {
244
+        return Promise.resolve(this._features);
245
+    }
246
+
238 247
     /**
239 248
      * Returns a set with the features for the participant.
240 249
      * @param {int} timeout the timeout in ms for reply from the participant.
241 250
      * @returns {Promise<Set<String>, Error>}
242 251
      */
243
-    getFeatures(timeout = 5000) {
252
+    queryFeatures(timeout = 5000) {
244 253
         if (this._getFeaturesPromise) {
245 254
             return this._getFeaturesPromise;
246 255
         }

+ 47
- 4
modules/xmpp/Caps.js View File

@@ -79,6 +79,10 @@ export default class Caps extends Listenable {
79 79
         this.version = '';
80 80
         this.rooms = new Set();
81 81
 
82
+        // We keep track of features added outside the library and we publish them
83
+        // in the presence of the participant for simplicity, avoiding the disco info request-response.
84
+        this.externalFeatures = new Set();
85
+
82 86
         const emuc = connection.emuc;
83 87
 
84 88
         emuc.addListener(XMPPEvents.EMUC_ROOM_ADDED,
@@ -102,10 +106,19 @@ export default class Caps extends Listenable {
102 106
      * @param {String} feature the name of the feature.
103 107
      * @param {boolean} submit if true - new presence with updated "c" node
104 108
      * will be sent.
109
+     * @param {boolean} external whether this feature was added externally to the library.
110
+     * We put features used directly by the clients (is jibri, remote-control enabled etc.) in the presence
111
+     * to avoid additional disco-info queries by those clients.
105 112
      */
106
-    addFeature(feature, submit = false) {
113
+    addFeature(feature, submit = false, external = false) {
107 114
         this.disco.addFeature(feature);
108 115
         this._generateVersion();
116
+
117
+        if (external && !this.externalFeatures.has(feature)) {
118
+            this.externalFeatures.add(feature);
119
+            this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));
120
+        }
121
+
109 122
         if (submit) {
110 123
             this.submit();
111 124
         }
@@ -117,10 +130,17 @@ export default class Caps extends Listenable {
117 130
      * @param {String} feature the name of the feature.
118 131
      * @param {boolean} submit if true - new presence with updated "c" node
119 132
      * will be sent.
133
+     * @param {boolean} external whether this feature was added externally to the library.
120 134
      */
121
-    removeFeature(feature, submit = false) {
135
+    removeFeature(feature, submit = false, external = false) {
122 136
         this.disco.removeFeature(feature);
123 137
         this._generateVersion();
138
+
139
+        if (external && this.externalFeatures.has(feature)) {
140
+            this.externalFeatures.delete(feature);
141
+            this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));
142
+        }
143
+
124 144
         if (submit) {
125 145
             this.submit();
126 146
         }
@@ -133,6 +153,28 @@ export default class Caps extends Listenable {
133 153
         this.rooms.forEach(room => room.sendPresence());
134 154
     }
135 155
 
156
+    /**
157
+     * Updates the presences in the room based on the current values in externalFeatures.
158
+     * @param {ChatRoom} room the room to update.
159
+     * @private
160
+     */
161
+    _updateRoomWithExternalFeatures(room) {
162
+        if (this.externalFeatures.size === 0) {
163
+            room.removeFromPresence('features');
164
+        } else {
165
+            const children = [];
166
+
167
+            this.externalFeatures.forEach(f => {
168
+                children.push({
169
+                    'tagName': 'feature',
170
+                    attributes: { 'var': f }
171
+                });
172
+            });
173
+
174
+            room.addToPresence('features', { children });
175
+        }
176
+    }
177
+
136 178
     /**
137 179
      * Returns a set with the features for a participant.
138 180
      * @param {String} jid the jid of the participant
@@ -231,6 +273,8 @@ export default class Caps extends Listenable {
231 273
         this.rooms.add(room);
232 274
         room.addListener(XMPPEvents.MUC_MEMBER_LEFT, this._onMucMemberLeft);
233 275
         this._fixChatRoomPresenceMap(room);
276
+
277
+        this._updateRoomWithExternalFeatures(room);
234 278
     }
235 279
 
236 280
     /**
@@ -290,8 +334,7 @@ export default class Caps extends Listenable {
290 334
         this.jidToVersion[from] = { version,
291 335
             node };
292 336
         if (oldVersion && oldVersion.version !== version) {
293
-            this.eventEmitter.emit(XMPPEvents.PARTCIPANT_FEATURES_CHANGED,
294
-                from);
337
+            this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from);
295 338
         }
296 339
 
297 340
         // return true to not remove the handler from Strophe

+ 44
- 33
modules/xmpp/ChatRoom.js View File

@@ -1,6 +1,7 @@
1 1
 /* global $, __filename */
2 2
 
3 3
 import { getLogger } from 'jitsi-meet-logger';
4
+import isEqual from 'lodash.isequal';
4 5
 import { $iq, $msg, $pres, Strophe } from 'strophe.js';
5 6
 
6 7
 import * as JitsiTranscriptionStatus from '../../JitsiTranscriptionStatus';
@@ -181,13 +182,10 @@ export default class ChatRoom extends Listenable {
181 182
     /**
182 183
      * Joins the chat room.
183 184
      * @param {string} password - Password to unlock room on joining.
184
-     * @param {Object} customJoinPresenceExtensions - Key values object to be used
185
-     * for the initial presence, they key will be an xmpp node and its text is the value,
186
-     * and those will be added to the initial <x xmlns='http://jabber.org/protocol/muc'/>
187 185
      * @returns {Promise} - resolved when join completes. At the time of this
188 186
      * writing it's never rejected.
189 187
      */
190
-    join(password, customJoinPresenceExtensions) {
188
+    join(password) {
191 189
         this.password = password;
192 190
 
193 191
         return new Promise(resolve => {
@@ -200,7 +198,7 @@ export default class ChatRoom extends Listenable {
200 198
                     : this.moderator.allocateConferenceFocus();
201 199
 
202 200
             preJoin.then(() => {
203
-                this.sendPresence(true, customJoinPresenceExtensions);
201
+                this.sendPresence(true);
204 202
                 this._removeConnListeners.push(
205 203
                     this.connection.addEventListener(
206 204
                         XmppConnection.Events.CONN_STATUS_CHANGED,
@@ -214,9 +212,8 @@ export default class ChatRoom extends Listenable {
214 212
     /**
215 213
      *
216 214
      * @param fromJoin - Whether this is initial presence to join the room.
217
-     * @param customJoinPresenceExtensions - Object of key values to be added to the initial presence only.
218 215
      */
219
-    sendPresence(fromJoin, customJoinPresenceExtensions) {
216
+    sendPresence(fromJoin) {
220 217
         const to = this.presMap.to;
221 218
 
222 219
         if (!this.connection || !this.connection.connected || !to || (!this.joined && !fromJoin)) {
@@ -237,11 +234,6 @@ export default class ChatRoom extends Listenable {
237 234
             if (this.password) {
238 235
                 pres.c('password').t(this.password).up();
239 236
             }
240
-            if (customJoinPresenceExtensions) {
241
-                Object.keys(customJoinPresenceExtensions).forEach(key => {
242
-                    pres.c(key).t(customJoinPresenceExtensions[key]).up();
243
-                });
244
-            }
245 237
             pres.up();
246 238
         }
247 239
 
@@ -524,6 +516,10 @@ export default class ChatRoom extends Listenable {
524 516
             case 'identity':
525 517
                 member.identity = extractIdentityInformation(node);
526 518
                 break;
519
+            case 'features': {
520
+                member.features = this._extractFeatures(node);
521
+                break;
522
+            }
527 523
             case 'stat': {
528 524
                 const { attributes } = node;
529 525
 
@@ -584,7 +580,7 @@ export default class ChatRoom extends Listenable {
584 580
             hasStatusUpdate = member.status !== undefined;
585 581
             hasVersionUpdate = member.version !== undefined;
586 582
             if (member.isFocus) {
587
-                this._initFocus(from, jid);
583
+                this._initFocus(from, member.features);
588 584
             } else {
589 585
                 // identity is being added to member joined, so external
590 586
                 // services can be notified for that (currently identity is
@@ -599,7 +595,8 @@ export default class ChatRoom extends Listenable {
599 595
                     member.status,
600 596
                     member.identity,
601 597
                     member.botType,
602
-                    member.jid);
598
+                    member.jid,
599
+                    member.features);
603 600
 
604 601
                 // we are reporting the status with the join
605 602
                 // so we do not want a second event about status update
@@ -646,7 +643,7 @@ export default class ChatRoom extends Listenable {
646 643
                 // so this case should not happen, if public jid is turned off we will receive the jid
647 644
                 // when we become moderator in the room
648 645
                 memberOfThis.isFocus = true;
649
-                this._initFocus(from, jid);
646
+                this._initFocus(from, member.features);
650 647
             }
651 648
 
652 649
             // store the new display name
@@ -664,6 +661,11 @@ export default class ChatRoom extends Listenable {
664 661
                 hasVersionUpdate = true;
665 662
                 memberOfThis.version = member.version;
666 663
             }
664
+
665
+            if (!isEqual(memberOfThis.features, member.features)) {
666
+                memberOfThis.features = member.features;
667
+                this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from, member.features);
668
+            }
667 669
         }
668 670
 
669 671
         // after we had fired member or room joined events, lets fire events
@@ -705,6 +707,9 @@ export default class ChatRoom extends Listenable {
705 707
 
706 708
                     this.eventEmitter.emit(
707 709
                         XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, properties);
710
+
711
+                    this.restartByTerminateSupported = properties['support-terminate-restart'] === 'true';
712
+                    logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);
708 713
                 }
709 714
                 break;
710 715
             case 'transcription-status': {
@@ -757,25 +762,33 @@ export default class ChatRoom extends Listenable {
757 762
     }
758 763
 
759 764
     /**
760
-     * Initialize some properties when the focus participant is verified.
761
-     * @param from jid of the focus
762
-     * @param mucJid the jid of the focus in the muc
765
+     * Extracts the features from the presence.
766
+     * @param node the node to process.
767
+     * @return features the Set of features where extracted data is added.
768
+     * @private
763 769
      */
764
-    _initFocus(from, mucJid) {
765
-        // skip if we have queried jicofo already, it will not change
766
-        if (this.focusFeatures) {
767
-            return;
770
+    _extractFeatures(node) {
771
+        const features = new Set();
772
+
773
+        for (let j = 0; j < node.children.length; j++) {
774
+            const { attributes } = node.children[j];
775
+
776
+            if (attributes && attributes.var) {
777
+                features.add(attributes.var);
778
+            }
768 779
         }
769 780
 
770
-        this.focusMucJid = from;
781
+        return features;
782
+    }
771 783
 
772
-        logger.info(`Ignore focus: ${from}, real JID: ${mucJid}`);
773
-        this.xmpp.caps.getFeatures(mucJid, 15000).then(features => {
774
-            this.focusFeatures = features;
775
-            logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);
776
-        }, error => {
777
-            logger.error('Failed to discover Jicofo features', error && error.message);
778
-        });
784
+    /**
785
+     * Initialize some properties when the focus participant is verified.
786
+     * @param from jid of the focus
787
+     * @param features the features reported in jicofo presence
788
+     */
789
+    _initFocus(from, features) {
790
+        this.focusMucJid = from;
791
+        this.focusFeatures = features;
779 792
     }
780 793
 
781 794
     /**
@@ -791,9 +804,7 @@ export default class ChatRoom extends Listenable {
791 804
      * @returns {boolean}
792 805
      */
793 806
     supportsRestartByTerminate() {
794
-        return this.focusFeatures
795
-            ? this.focusFeatures.has('https://jitsi.org/meet/jicofo/terminate-restart')
796
-            : false;
807
+        return this.restartByTerminateSupported;
797 808
     }
798 809
 
799 810
     /**

+ 8
- 4
modules/xmpp/ChatRoom.spec.js View File

@@ -175,7 +175,8 @@ describe('ChatRoom', () => {
175 175
                 'status-text',
176 176
                 undefined,
177 177
                 undefined,
178
-                'fulljid'
178
+                'fulljid',
179
+                undefined // features
179 180
             ]);
180 181
         });
181 182
 
@@ -204,7 +205,8 @@ describe('ChatRoom', () => {
204 205
                 undefined,
205 206
                 undefined,
206 207
                 undefined,
207
-                'jid=attr');
208
+                'jid=attr',
209
+                undefined); // features
208 210
         });
209 211
 
210 212
         it('parses identity correctly', () => {
@@ -250,7 +252,8 @@ describe('ChatRoom', () => {
250 252
                 'status-text',
251 253
                 expectedIdentity,
252 254
                 undefined,
253
-                'fulljid'
255
+                'fulljid',
256
+                undefined // features
254 257
             ]);
255 258
         });
256 259
 
@@ -282,7 +285,8 @@ describe('ChatRoom', () => {
282 285
                 'status-text',
283 286
                 undefined,
284 287
                 expectedBotType,
285
-                'fulljid'
288
+                'fulljid',
289
+                undefined // features
286 290
             ]);
287 291
         });
288 292
 

+ 14
- 1
modules/xmpp/xmpp.js View File

@@ -77,6 +77,19 @@ export const DEFAULT_STUN_SERVERS = [
77 77
  */
78 78
 export const JITSI_MEET_MUC_TYPE = 'type';
79 79
 
80
+/**
81
+ * The feature used by jigasi participants.
82
+ * @type {string}
83
+ */
84
+export const FEATURE_JIGASI = 'http://jitsi.org/protocol/jigasi';
85
+
86
+/**
87
+ * The feature used by the lib to mark support for e2ee. We use the feature by putting it in the presence
88
+ * to avoid additional signaling (disco-info).
89
+ * @type {string}
90
+ */
91
+export const FEATURE_E2EE = 'https://jitsi.org/meet/e2ee';
92
+
80 93
 /**
81 94
  *
82 95
  */
@@ -192,7 +205,7 @@ export default class XMPP extends Listenable {
192 205
         }
193 206
 
194 207
         if (E2EEncryption.isSupported(this.options)) {
195
-            this.caps.addFeature('https://jitsi.org/meet/e2ee');
208
+            this.caps.addFeature(FEATURE_E2EE, false, true);
196 209
         }
197 210
     }
198 211
 

+ 1
- 1
service/xmpp/XMPPEvents.js View File

@@ -173,7 +173,7 @@ const XMPPEvents = {
173 173
     /**
174 174
      * Indicates that the features of the participant has been changed.
175 175
      */
176
-    PARTCIPANT_FEATURES_CHANGED: 'xmpp.partcipant_features_changed',
176
+    PARTICIPANT_FEATURES_CHANGED: 'xmpp.participant_features_changed',
177 177
     PASSWORD_REQUIRED: 'xmpp.password_required',
178 178
 
179 179
     /**

Loading…
Cancel
Save