@webex/plugin-meetings 3.8.0-next.8 → 3.8.0-next.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +70 -6
- package/dist/breakouts/index.js.map +1 -1
- package/dist/common/errors/webex-errors.js +12 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +20 -123
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +52 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +28 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +62 -12
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +432 -418
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +17 -17
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +94 -6
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +6 -0
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +17 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +570 -302
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +0 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +0 -2
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +13 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +373 -68
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +5 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +136 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +330 -353
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +42 -0
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +38 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +36 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +63 -27
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +112 -47
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/common/errors/webex-errors.d.ts +7 -1
- package/dist/types/config.d.ts +3 -0
- package/dist/types/constants.d.ts +13 -85
- package/dist/types/controls-options-manager/enums.d.ts +3 -1
- package/dist/types/controls-options-manager/types.d.ts +7 -1
- package/dist/types/locus-info/index.d.ts +3 -3
- package/dist/types/locus-info/selfUtils.d.ts +216 -1
- package/dist/types/media/properties.d.ts +15 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
- package/dist/types/meeting/index.d.ts +43 -1
- package/dist/types/meeting/muteState.d.ts +0 -1
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +82 -1
- package/dist/types/meetings/index.d.ts +57 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +159 -1
- package/dist/types/members/index.d.ts +15 -0
- package/dist/types/members/request.d.ts +26 -0
- package/dist/types/members/util.d.ts +27 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +15 -7
- package/dist/types/reachability/index.d.ts +10 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -23
- package/src/breakouts/index.ts +69 -0
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +3 -0
- package/src/constants.ts +20 -90
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +44 -14
- package/src/locus-info/index.ts +56 -13
- package/src/locus-info/selfUtils.ts +496 -442
- package/src/media/index.ts +23 -21
- package/src/media/properties.ts +96 -0
- package/src/meeting/brbState.ts +7 -0
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +382 -93
- package/src/meeting/locusMediaRequest.ts +0 -18
- package/src/meeting/muteState.ts +0 -2
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +11 -2
- package/src/meeting-info/meeting-info-v2.ts +254 -8
- package/src/meeting-info/utilv2.ts +5 -0
- package/src/meetings/index.ts +148 -1
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +13 -2
- package/src/member/util.ts +351 -348
- package/src/members/index.ts +47 -0
- package/src/members/request.ts +44 -0
- package/src/members/util.ts +43 -1
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +73 -26
- package/src/reachability/index.ts +70 -1
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/breakouts/index.ts +167 -95
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +167 -73
- package/test/unit/spec/locus-info/selfUtils.js +98 -24
- package/test/unit/spec/media/index.ts +150 -18
- package/test/unit/spec/media/properties.ts +130 -0
- package/test/unit/spec/meeting/brbState.ts +19 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +557 -35
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- package/test/unit/spec/meeting/muteState.js +0 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +119 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +484 -114
- package/test/unit/spec/meeting-info/utilv2.js +19 -0
- package/test/unit/spec/meetings/index.js +146 -2
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/index.js +140 -26
- package/test/unit/spec/members/request.js +68 -22
- package/test/unit/spec/members/utils.js +75 -0
- package/test/unit/spec/reachability/clusterReachability.ts +88 -56
- package/test/unit/spec/reachability/index.ts +101 -0
- package/test/unit/spec/reachability/request.js +47 -2
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
package/src/members/index.ts
CHANGED
@@ -773,6 +773,28 @@ export default class Members extends StatelessWebexPlugin {
|
|
773
773
|
return this.membersRequest.cancelPhoneInvite(options);
|
774
774
|
}
|
775
775
|
|
776
|
+
/**
|
777
|
+
* Cancels an SIP call to the associated meeting
|
778
|
+
* @param {String} invitee
|
779
|
+
* @returns {Promise}
|
780
|
+
* @memberof Members
|
781
|
+
*/
|
782
|
+
cancelSIPInvite(invitee: any) {
|
783
|
+
if (!this.locusUrl) {
|
784
|
+
return Promise.reject(
|
785
|
+
new ParameterError('The associated locus url for this meeting object must be defined.')
|
786
|
+
);
|
787
|
+
}
|
788
|
+
if (!invitee?.memberId) {
|
789
|
+
return Promise.reject(
|
790
|
+
new ParameterError('The invitee must be defined with a memberId property.')
|
791
|
+
);
|
792
|
+
}
|
793
|
+
const options = MembersUtil.cancelSIPInviteOptions(invitee, this.locusUrl);
|
794
|
+
|
795
|
+
return this.membersRequest.cancelSIPInvite(options);
|
796
|
+
}
|
797
|
+
|
776
798
|
/**
|
777
799
|
* Admits waiting members (invited guests to meeting)
|
778
800
|
* @param {Array} memberIds
|
@@ -886,6 +908,31 @@ export default class Members extends StatelessWebexPlugin {
|
|
886
908
|
});
|
887
909
|
}
|
888
910
|
|
911
|
+
/**
|
912
|
+
* Moves a meeting member into the lobby.
|
913
|
+
* @param {String} memberId -- The ID of the member to move.
|
914
|
+
* @returns {Promise} -- Resolves with the lobby‐move response.
|
915
|
+
* @public
|
916
|
+
* @memberof Members
|
917
|
+
*/
|
918
|
+
public moveToLobby(memberId: string) {
|
919
|
+
if (!this.locusUrl) {
|
920
|
+
return Promise.reject(
|
921
|
+
new ParameterError(
|
922
|
+
'The associated locus url for this meetings members object must be defined.'
|
923
|
+
)
|
924
|
+
);
|
925
|
+
}
|
926
|
+
if (!memberId) {
|
927
|
+
return Promise.reject(
|
928
|
+
new ParameterError('The member id must be defined to move the member to lobby.')
|
929
|
+
);
|
930
|
+
}
|
931
|
+
const body = MembersUtil.getMoveMemberToLobbyRequestBody(memberId);
|
932
|
+
|
933
|
+
return this.membersRequest.moveToLobbyMember({locusUrl: this.locusUrl, memberId}, body);
|
934
|
+
}
|
935
|
+
|
889
936
|
/**
|
890
937
|
* Raise or lower the hand of a member in a meeting
|
891
938
|
* @param {String} memberId
|
package/src/members/request.ts
CHANGED
@@ -129,6 +129,32 @@ export default class MembersRequest extends StatelessWebexPlugin {
|
|
129
129
|
return this.locusDeltaRequest(requestParams);
|
130
130
|
}
|
131
131
|
|
132
|
+
/**
|
133
|
+
* Sends a request to move a meeting member into the lobby.
|
134
|
+
* *
|
135
|
+
* @param {Object} options - Request options.
|
136
|
+
* @param {string} options.locusUrl - The locus URL for the meeting.
|
137
|
+
* @param {string} options.memberId - The ID of the member to move.
|
138
|
+
* @param {Object} body - The request payload.
|
139
|
+
* @param {Object} body.moveToLobby - Container for move‐to‐lobby data.
|
140
|
+
* @param {string[]} body.moveToLobby.participantIds - Array of participant IDs to move.
|
141
|
+
* @returns {Promise} - Resolves with the locus‐delta response.
|
142
|
+
*/
|
143
|
+
moveToLobbyMember(
|
144
|
+
options: {locusUrl: string; memberId: string},
|
145
|
+
body: {moveToLobby: {participantIds: string[]}}
|
146
|
+
) {
|
147
|
+
if (!options || !options.locusUrl || !options.memberId) {
|
148
|
+
throw new ParameterError(
|
149
|
+
'memberId must be defined, and the associated locus url for this meeting object must be defined.'
|
150
|
+
);
|
151
|
+
}
|
152
|
+
|
153
|
+
const requestParams = MembersUtil.getMoveMemberToLobbyRequestParams(options, body);
|
154
|
+
|
155
|
+
return this.locusDeltaRequest(requestParams);
|
156
|
+
}
|
157
|
+
|
132
158
|
/**
|
133
159
|
* Sends a request to raise or lower a member's hand
|
134
160
|
* @param {Object} options
|
@@ -252,4 +278,22 @@ export default class MembersRequest extends StatelessWebexPlugin {
|
|
252
278
|
|
253
279
|
return this.locusDeltaRequest(requestParams);
|
254
280
|
}
|
281
|
+
|
282
|
+
/**
|
283
|
+
* @param {Object} options with format of {invitee: object, locusUrl: string}
|
284
|
+
* @returns {Promise}
|
285
|
+
* @throws {Error} if the options are not valid and complete, must have invitee with memberId AND locusUrl
|
286
|
+
* @memberof MembersRequest
|
287
|
+
*/
|
288
|
+
cancelSIPInvite(options: any) {
|
289
|
+
if (!options?.invitee?.memberId || !options?.locusUrl) {
|
290
|
+
throw new ParameterError(
|
291
|
+
'invitee must be passed and the associated locus url for this meeting object must be defined.'
|
292
|
+
);
|
293
|
+
}
|
294
|
+
|
295
|
+
const requestParams = MembersUtil.generateCancelSIPInviteRequestParams(options);
|
296
|
+
|
297
|
+
return this.locusDeltaRequest(requestParams);
|
298
|
+
}
|
255
299
|
}
|
package/src/members/util.ts
CHANGED
@@ -110,7 +110,10 @@ const MembersUtil = {
|
|
110
110
|
return !DIALER_REGEX.E164_FORMAT.test(invitee.phoneNumber);
|
111
111
|
}
|
112
112
|
|
113
|
-
return !
|
113
|
+
return !(
|
114
|
+
VALID_EMAIL_ADDRESS.test(invitee.email || invitee.emailAddress) ||
|
115
|
+
DIALER_REGEX.SIP_ADDRESS.test(invitee.email || invitee.emailAddress)
|
116
|
+
);
|
114
117
|
},
|
115
118
|
|
116
119
|
getRemoveMemberRequestParams: (options) => {
|
@@ -203,6 +206,22 @@ const MembersUtil = {
|
|
203
206
|
};
|
204
207
|
},
|
205
208
|
|
209
|
+
getMoveMemberToLobbyRequestBody: (memberId: string) => ({
|
210
|
+
moveToLobby: {
|
211
|
+
participantIds: [memberId],
|
212
|
+
},
|
213
|
+
}),
|
214
|
+
|
215
|
+
getMoveMemberToLobbyRequestParams: (options: {memberId: string; locusUrl: string}, body) => {
|
216
|
+
const uri = `${options.locusUrl}/${PARTICIPANT}/${options.memberId}/${CONTROLS}`;
|
217
|
+
|
218
|
+
return {
|
219
|
+
method: HTTP_VERBS.PATCH,
|
220
|
+
uri,
|
221
|
+
body,
|
222
|
+
};
|
223
|
+
},
|
224
|
+
|
206
225
|
/**
|
207
226
|
* @param {ServerRoleShape} role
|
208
227
|
* @returns {ServerRoleShape} the role shape to be added to the body
|
@@ -351,6 +370,29 @@ const MembersUtil = {
|
|
351
370
|
|
352
371
|
return requestParams;
|
353
372
|
},
|
373
|
+
|
374
|
+
cancelSIPInviteOptions: (invitee, locusUrl) => ({
|
375
|
+
invitee,
|
376
|
+
locusUrl,
|
377
|
+
}),
|
378
|
+
|
379
|
+
generateCancelSIPInviteRequestParams: (options) => {
|
380
|
+
const body = {
|
381
|
+
actionType: _REMOVE_,
|
382
|
+
invitees: [
|
383
|
+
{
|
384
|
+
address: options.invitee.memberId,
|
385
|
+
},
|
386
|
+
],
|
387
|
+
};
|
388
|
+
const requestParams = {
|
389
|
+
method: HTTP_VERBS.PUT,
|
390
|
+
uri: options.locusUrl,
|
391
|
+
body,
|
392
|
+
};
|
393
|
+
|
394
|
+
return requestParams;
|
395
|
+
},
|
354
396
|
};
|
355
397
|
|
356
398
|
export default MembersUtil;
|
package/src/metrics/constants.ts
CHANGED
@@ -48,10 +48,19 @@ const BEHAVIORAL_METRICS = {
|
|
48
48
|
UPLOAD_LOGS_FAILURE: 'js_sdk_upload_logs_failure',
|
49
49
|
UPLOAD_LOGS_SUCCESS: 'js_sdk_upload_logs_success',
|
50
50
|
RECEIVE_TRANSCRIPTION_FAILURE: 'js_sdk_receive_transcription_failure',
|
51
|
+
MEETING_IS_IN_PROGRESS_ERROR: 'js_sdk_meeting_is_in_progress_error',
|
52
|
+
STATIC_MEETING_LINK_ALREADY_EXISTS_ERROR: 'js_sdk_static_meeting_link_already_exists_error',
|
51
53
|
FETCH_MEETING_INFO_V1_SUCCESS: 'js_sdk_fetch_meeting_info_v1_success',
|
52
54
|
FETCH_MEETING_INFO_V1_FAILURE: 'js_sdk_fetch_meeting_info_v1_failure',
|
55
|
+
ENABLE_STATIC_METTING_LINK_SUCCESS: 'js_sdk_enable_static_meeting_link_success',
|
56
|
+
ENABLE_STATIC_METTING_LINK_FAILURE: 'js_sdk_enable_static_meeting_link_failure',
|
57
|
+
DISABLE_STATIC_MEETING_LINK_SUCCESS: 'js_sdk_disable_static_meeting_link_success',
|
58
|
+
DISABLE_STATIC_MEETING_LINK_FAILURE: 'js_sdk_disable_static_meeting_link_failure',
|
53
59
|
ADHOC_MEETING_SUCCESS: 'js_sdk_adhoc_meeting_success',
|
54
60
|
ADHOC_MEETING_FAILURE: 'js_sdk_adhoc_meeting_failure',
|
61
|
+
FETCH_STATIC_MEETING_LINK_SUCCESS: 'js_sdk_fetch_static_meeting_link_success',
|
62
|
+
FETCH_STATIC_MEETING_LINK_FAILURE: 'js_sdk_fetch_static_meeting_link_failure',
|
63
|
+
MEETING_LINK_DOES_NOT_EXIST_ERROR: 'js_sdk_meeting_link_does_not_exist_error',
|
55
64
|
VERIFY_PASSWORD_SUCCESS: 'js_sdk_verify_password_success',
|
56
65
|
VERIFY_PASSWORD_ERROR: 'js_sdk_verify_password_error',
|
57
66
|
VERIFY_CAPTCHA_ERROR: 'js_sdk_verify_captcha_error',
|
@@ -6,7 +6,7 @@ import {convertStunUrlToTurn, convertStunUrlToTurnTls} from './util';
|
|
6
6
|
import EventsScope from '../common/events/events-scope';
|
7
7
|
|
8
8
|
import {CONNECTION_STATE, Enum, ICE_GATHERING_STATE} from '../constants';
|
9
|
-
import {ClusterReachabilityResult} from './reachability.types';
|
9
|
+
import {ClusterReachabilityResult, NatType} from './reachability.types';
|
10
10
|
|
11
11
|
// data for the Events.resultReady event
|
12
12
|
export type ResultEventData = {
|
@@ -22,9 +22,14 @@ export type ClientMediaIpsUpdatedEventData = {
|
|
22
22
|
clientMediaIPs: string[];
|
23
23
|
};
|
24
24
|
|
25
|
+
export type NatTypeUpdatedEventData = {
|
26
|
+
natType: NatType;
|
27
|
+
};
|
28
|
+
|
25
29
|
export const Events = {
|
26
30
|
resultReady: 'resultReady', // emitted when a cluster is reached successfully using specific protocol
|
27
31
|
clientMediaIpsUpdated: 'clientMediaIpsUpdated', // emitted when more public IPs are found after resultReady was already sent for a given protocol
|
32
|
+
natTypeUpdated: 'natTypeUpdated', // emitted when NAT type is determined
|
28
33
|
} as const;
|
29
34
|
|
30
35
|
export type Events = Enum<typeof Events>;
|
@@ -41,8 +46,10 @@ export class ClusterReachability extends EventsScope {
|
|
41
46
|
private pc?: RTCPeerConnection;
|
42
47
|
private defer: Defer; // this defer is resolved once reachability checks for this cluster are completed
|
43
48
|
private startTimestamp: number;
|
49
|
+
private srflxIceCandidates: RTCIceCandidate[] = [];
|
44
50
|
public readonly isVideoMesh: boolean;
|
45
51
|
public readonly name;
|
52
|
+
public readonly reachedSubnets: Set<string> = new Set();
|
46
53
|
|
47
54
|
/**
|
48
55
|
* Constructor for ClusterReachability
|
@@ -228,27 +235,13 @@ export class ClusterReachability extends EventsScope {
|
|
228
235
|
*/
|
229
236
|
private registerIceGatheringStateChangeListener() {
|
230
237
|
this.pc.onicegatheringstatechange = () => {
|
231
|
-
|
232
|
-
|
233
|
-
if (this.pc.iceConnectionState === COMPLETE) {
|
238
|
+
if (this.pc.iceGatheringState === ICE_GATHERING_STATE.COMPLETE) {
|
234
239
|
this.closePeerConnection();
|
235
240
|
this.finishReachabilityCheck();
|
236
241
|
}
|
237
242
|
};
|
238
243
|
}
|
239
244
|
|
240
|
-
/**
|
241
|
-
* Checks if we have the results for all the protocols (UDP and TCP)
|
242
|
-
*
|
243
|
-
* @returns {boolean} true if we have all results, false otherwise
|
244
|
-
*/
|
245
|
-
private haveWeGotAllResults(): boolean {
|
246
|
-
return ['udp', 'tcp', 'xtls'].every(
|
247
|
-
(protocol) =>
|
248
|
-
this.result[protocol].result === 'reachable' || this.result[protocol].result === 'untested'
|
249
|
-
);
|
250
|
-
}
|
251
|
-
|
252
245
|
/**
|
253
246
|
* Saves the latency in the result for the given protocol and marks it as reachable,
|
254
247
|
* emits the "resultReady" event if this is the first result for that protocol,
|
@@ -258,9 +251,15 @@ export class ClusterReachability extends EventsScope {
|
|
258
251
|
* @param {string} protocol
|
259
252
|
* @param {number} latency
|
260
253
|
* @param {string|null} [publicIp]
|
254
|
+
* @param {string|null} [serverIp]
|
261
255
|
* @returns {void}
|
262
256
|
*/
|
263
|
-
private saveResult(
|
257
|
+
private saveResult(
|
258
|
+
protocol: 'udp' | 'tcp' | 'xtls',
|
259
|
+
latency: number,
|
260
|
+
publicIp?: string | null,
|
261
|
+
serverIp?: string | null
|
262
|
+
) {
|
264
263
|
const result = this.result[protocol];
|
265
264
|
|
266
265
|
if (result.latencyInMilliseconds === undefined) {
|
@@ -288,6 +287,48 @@ export class ClusterReachability extends EventsScope {
|
|
288
287
|
} else {
|
289
288
|
this.addPublicIP(protocol, publicIp);
|
290
289
|
}
|
290
|
+
|
291
|
+
if (serverIp) {
|
292
|
+
this.reachedSubnets.add(serverIp);
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
/**
|
297
|
+
* Determines NAT Type.
|
298
|
+
*
|
299
|
+
* @param {RTCIceCandidate} candidate
|
300
|
+
* @returns {void}
|
301
|
+
*/
|
302
|
+
private determineNatType(candidate: RTCIceCandidate) {
|
303
|
+
this.srflxIceCandidates.push(candidate);
|
304
|
+
|
305
|
+
if (this.srflxIceCandidates.length > 1) {
|
306
|
+
const portsFound: Record<string, Set<number>> = {};
|
307
|
+
|
308
|
+
this.srflxIceCandidates.forEach((c) => {
|
309
|
+
const key = `${c.address}:${c.relatedPort}`;
|
310
|
+
if (!portsFound[key]) {
|
311
|
+
portsFound[key] = new Set();
|
312
|
+
}
|
313
|
+
portsFound[key].add(c.port);
|
314
|
+
});
|
315
|
+
|
316
|
+
Object.entries(portsFound).forEach(([, ports]) => {
|
317
|
+
if (ports.size > 1) {
|
318
|
+
// Found candidates with the same address and relatedPort, but different ports
|
319
|
+
this.emit(
|
320
|
+
{
|
321
|
+
file: 'clusterReachability',
|
322
|
+
function: 'determineNatType',
|
323
|
+
},
|
324
|
+
Events.natTypeUpdated,
|
325
|
+
{
|
326
|
+
natType: NatType.SymmetricNat,
|
327
|
+
}
|
328
|
+
);
|
329
|
+
}
|
330
|
+
});
|
331
|
+
}
|
291
332
|
}
|
292
333
|
|
293
334
|
/**
|
@@ -307,19 +348,25 @@ export class ClusterReachability extends EventsScope {
|
|
307
348
|
|
308
349
|
if (e.candidate) {
|
309
350
|
if (e.candidate.type === CANDIDATE_TYPES.SERVER_REFLEXIVE) {
|
310
|
-
|
351
|
+
let serverIp = null;
|
352
|
+
if ('url' in e.candidate) {
|
353
|
+
const stunServerUrlRegex = /stun:([\d.]+):\d+/;
|
354
|
+
|
355
|
+
const match = (e.candidate as any).url.match(stunServerUrlRegex);
|
356
|
+
if (match) {
|
357
|
+
// eslint-disable-next-line prefer-destructuring
|
358
|
+
serverIp = match[1];
|
359
|
+
}
|
360
|
+
}
|
361
|
+
|
362
|
+
this.saveResult('udp', latencyInMilliseconds, e.candidate.address, serverIp);
|
363
|
+
|
364
|
+
this.determineNatType(e.candidate);
|
311
365
|
}
|
312
366
|
|
313
367
|
if (e.candidate.type === CANDIDATE_TYPES.RELAY) {
|
314
368
|
const protocol = e.candidate.port === TURN_TLS_PORT ? 'xtls' : 'tcp';
|
315
|
-
this.saveResult(protocol, latencyInMilliseconds);
|
316
|
-
// we don't add public IP for TCP, because in the case of relay candidates
|
317
|
-
// e.candidate.address is the TURN server address, not the client's public IP
|
318
|
-
}
|
319
|
-
|
320
|
-
if (this.haveWeGotAllResults()) {
|
321
|
-
this.closePeerConnection();
|
322
|
-
this.finishReachabilityCheck();
|
369
|
+
this.saveResult(protocol, latencyInMilliseconds, null, e.candidate.address);
|
323
370
|
}
|
324
371
|
}
|
325
372
|
};
|
@@ -23,11 +23,13 @@ import {
|
|
23
23
|
ReachabilityResultsForBackend,
|
24
24
|
TransportResultForBackend,
|
25
25
|
GetClustersTrigger,
|
26
|
+
NatType,
|
26
27
|
} from './reachability.types';
|
27
28
|
import {
|
28
29
|
ClientMediaIpsUpdatedEventData,
|
29
30
|
ClusterReachability,
|
30
31
|
Events,
|
32
|
+
NatTypeUpdatedEventData,
|
31
33
|
ResultEventData,
|
32
34
|
} from './clusterReachability';
|
33
35
|
import EventsScope from '../common/events/events-scope';
|
@@ -64,6 +66,7 @@ export default class Reachability extends EventsScope {
|
|
64
66
|
resultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
65
67
|
startTime = undefined;
|
66
68
|
totalDuration = undefined;
|
69
|
+
natType = NatType.Unknown;
|
67
70
|
|
68
71
|
protected lastTrigger?: string;
|
69
72
|
|
@@ -135,6 +138,60 @@ export default class Reachability extends EventsScope {
|
|
135
138
|
}
|
136
139
|
}
|
137
140
|
|
141
|
+
/**
|
142
|
+
* Checks if the given subnet is reachable
|
143
|
+
* @param {string} mediaServerIp - media server ip
|
144
|
+
* @returns {boolean | null} true if reachable, false if not reachable, null if mediaServerIp is not provided
|
145
|
+
* @public
|
146
|
+
* @memberof Reachability
|
147
|
+
*/
|
148
|
+
public isSubnetReachable(mediaServerIp?: string): boolean | null {
|
149
|
+
if (!mediaServerIp) {
|
150
|
+
LoggerProxy.logger.error(`Reachability:index#isSubnetReachable --> mediaServerIp is null`);
|
151
|
+
|
152
|
+
return null;
|
153
|
+
}
|
154
|
+
|
155
|
+
const subnetFirstOctet = mediaServerIp.split('.')[0];
|
156
|
+
|
157
|
+
LoggerProxy.logger.info(
|
158
|
+
`Reachability:index#isSubnetReachable --> Looking for subnet: ${subnetFirstOctet}.X.X.X`
|
159
|
+
);
|
160
|
+
|
161
|
+
const matchingReachedClusters = Object.values(this.clusterReachability).reduce(
|
162
|
+
(acc, cluster) => {
|
163
|
+
const reachedSubnetsArray = Array.from(cluster.reachedSubnets);
|
164
|
+
|
165
|
+
let logMessage = `Reachability:index#isSubnetReachable --> Cluster ${cluster.name} reached [`;
|
166
|
+
for (let i = 0; i < reachedSubnetsArray.length; i += 1) {
|
167
|
+
const subnet = reachedSubnetsArray[i];
|
168
|
+
const reachedSubnetFirstOctet = subnet.split('.')[0];
|
169
|
+
|
170
|
+
if (subnetFirstOctet === reachedSubnetFirstOctet) {
|
171
|
+
acc.add(cluster.name);
|
172
|
+
}
|
173
|
+
|
174
|
+
logMessage += `${subnet}`;
|
175
|
+
if (i < reachedSubnetsArray.length - 1) {
|
176
|
+
logMessage += ',';
|
177
|
+
}
|
178
|
+
}
|
179
|
+
logMessage += `]`;
|
180
|
+
|
181
|
+
LoggerProxy.logger.info(logMessage);
|
182
|
+
|
183
|
+
return acc;
|
184
|
+
},
|
185
|
+
new Set<string>()
|
186
|
+
);
|
187
|
+
|
188
|
+
LoggerProxy.logger.info(
|
189
|
+
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${subnetFirstOctet}.X.X.X`
|
190
|
+
);
|
191
|
+
|
192
|
+
return matchingReachedClusters.size > 0;
|
193
|
+
}
|
194
|
+
|
138
195
|
/**
|
139
196
|
* Gets a list of media clusters from the backend and performs reachability checks on all the clusters
|
140
197
|
* @param {string} trigger - explains the reason for starting reachability
|
@@ -143,6 +200,10 @@ export default class Reachability extends EventsScope {
|
|
143
200
|
* @memberof Reachability
|
144
201
|
*/
|
145
202
|
public async gatherReachability(trigger: string): Promise<ReachabilityResults> {
|
203
|
+
// @ts-ignore
|
204
|
+
if (!this.webex.config.meetings.enableReachabilityChecks) {
|
205
|
+
throw new Error('enableReachabilityChecks is disabled in config');
|
206
|
+
}
|
146
207
|
// Fetch clusters and measure latency
|
147
208
|
try {
|
148
209
|
this.lastTrigger = trigger;
|
@@ -281,7 +342,7 @@ export default class Reachability extends EventsScope {
|
|
281
342
|
{}
|
282
343
|
);
|
283
344
|
this.sendMetric(true);
|
284
|
-
this.resolveReachabilityPromise();
|
345
|
+
this.resolveReachabilityPromise(false);
|
285
346
|
}
|
286
347
|
}
|
287
348
|
|
@@ -305,6 +366,7 @@ export default class Reachability extends EventsScope {
|
|
305
366
|
reachability_vmn_tcp_failed: 0,
|
306
367
|
reachability_vmn_xtls_success: 0,
|
307
368
|
reachability_vmn_xtls_failed: 0,
|
369
|
+
natType: this.natType,
|
308
370
|
};
|
309
371
|
|
310
372
|
const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
|
@@ -963,6 +1025,13 @@ export default class Reachability extends EventsScope {
|
|
963
1025
|
}
|
964
1026
|
);
|
965
1027
|
|
1028
|
+
this.clusterReachability[key].on(
|
1029
|
+
Events.natTypeUpdated,
|
1030
|
+
async (data: NatTypeUpdatedEventData) => {
|
1031
|
+
this.natType = data.natType;
|
1032
|
+
}
|
1033
|
+
);
|
1034
|
+
|
966
1035
|
this.clusterReachability[key].start(); // not awaiting on purpose
|
967
1036
|
});
|
968
1037
|
}
|
@@ -7,6 +7,11 @@ export type TransportResult = {
|
|
7
7
|
clientMediaIPs?: string[];
|
8
8
|
};
|
9
9
|
|
10
|
+
export enum NatType {
|
11
|
+
Unknown = 'unknown',
|
12
|
+
SymmetricNat = 'symmetric-nat',
|
13
|
+
}
|
14
|
+
|
10
15
|
// reachability result for a specific media cluster
|
11
16
|
export type ClusterReachabilityResult = {
|
12
17
|
udp: TransportResult;
|
@@ -27,6 +32,7 @@ export type ReachabilityMetrics = {
|
|
27
32
|
reachability_vmn_tcp_failed: number;
|
28
33
|
reachability_vmn_xtls_success: number;
|
29
34
|
reachability_vmn_xtls_failed: number;
|
35
|
+
natType: NatType;
|
30
36
|
};
|
31
37
|
|
32
38
|
/**
|
@@ -45,6 +45,9 @@ class ReachabilityRequest {
|
|
45
45
|
joinCookie: any;
|
46
46
|
discoveryOptions?: Record<string, any>;
|
47
47
|
}> => {
|
48
|
+
const appType = this.webex?.config?.support?.appType;
|
49
|
+
const appVersion = this.webex?.config?.support?.appVersion;
|
50
|
+
|
48
51
|
// we only measure latency for the initial startup call, not for other triggers
|
49
52
|
const callWrapper =
|
50
53
|
trigger === 'startup'
|
@@ -67,6 +70,10 @@ class ReachabilityRequest {
|
|
67
70
|
'early-call-min-clusters': true,
|
68
71
|
},
|
69
72
|
'previous-report': previousReport,
|
73
|
+
...(appType &&
|
74
|
+
appVersion && {
|
75
|
+
'client-environment': {components: {[appType]: appVersion}},
|
76
|
+
}),
|
70
77
|
trigger,
|
71
78
|
},
|
72
79
|
timeout: this.webex.config.meetings.reachabilityGetClusterTimeout,
|
@@ -591,9 +591,9 @@ export default class ReconnectionManager {
|
|
591
591
|
|
592
592
|
const iceServers = [];
|
593
593
|
|
594
|
-
if (turnServerResult.turnServerInfo?.
|
594
|
+
if (turnServerResult.turnServerInfo?.urls.length > 0) {
|
595
595
|
iceServers.push({
|
596
|
-
urls: turnServerResult.turnServerInfo.
|
596
|
+
urls: turnServerResult.turnServerInfo.urls,
|
597
597
|
username: turnServerResult.turnServerInfo.username || '',
|
598
598
|
credential: turnServerResult.turnServerInfo.password || '',
|
599
599
|
});
|
package/src/roap/index.ts
CHANGED
@@ -5,17 +5,13 @@ import {ROAP} from '../constants';
|
|
5
5
|
import LoggerProxy from '../common/logs/logger-proxy';
|
6
6
|
|
7
7
|
import RoapRequest from './request';
|
8
|
-
import TurnDiscovery
|
8
|
+
import TurnDiscovery from './turnDiscovery';
|
9
|
+
import {TurnDiscoveryResult} from './types';
|
9
10
|
import Meeting from '../meeting';
|
10
|
-
import MeetingUtil from '../meeting/util';
|
11
11
|
import Metrics from '../metrics';
|
12
12
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
13
13
|
|
14
|
-
export {
|
15
|
-
type TurnDiscoveryResult,
|
16
|
-
type TurnServerInfo,
|
17
|
-
type TurnDiscoverySkipReason,
|
18
|
-
} from './turnDiscovery';
|
14
|
+
export {type TurnDiscoveryResult, type TurnServerInfo, type TurnDiscoverySkipReason} from './types';
|
19
15
|
|
20
16
|
/**
|
21
17
|
* Roap options
|