@webex/plugin-meetings 3.10.0-next.27 → 3.10.0-next.29
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 +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +3 -14
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +11 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +2 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meetings/index.js +25 -11
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +11 -7
- package/dist/meetings/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +171 -18
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +3 -1
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachabilityPeerConnection.js +1 -1
- package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +1 -11
- package/dist/types/hashTree/types.d.ts +4 -0
- package/dist/types/hashTree/utils.d.ts +7 -0
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/reachability/clusterReachability.d.ts +30 -3
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/config.ts +1 -0
- package/src/hashTree/hashTreeParser.ts +2 -16
- package/src/hashTree/types.ts +5 -0
- package/src/hashTree/utils.ts +11 -0
- package/src/locus-info/index.ts +2 -3
- package/src/meetings/index.ts +24 -17
- package/src/meetings/util.ts +10 -9
- package/src/reachability/clusterReachability.ts +153 -27
- package/src/reachability/index.ts +6 -1
- package/src/reachability/reachabilityPeerConnection.ts +3 -1
- package/test/unit/spec/hashTree/utils.ts +38 -1
- package/test/unit/spec/meetings/index.js +192 -1
- package/test/unit/spec/meetings/utils.js +51 -1
- package/test/unit/spec/reachability/clusterReachability.ts +125 -1
- package/test/unit/spec/reachability/index.ts +3 -3
|
@@ -23,26 +23,53 @@ export declare const Events: {
|
|
|
23
23
|
export type Events = Enum<typeof Events>;
|
|
24
24
|
/**
|
|
25
25
|
* A class that handles reachability checks for a single cluster.
|
|
26
|
-
* Creates and orchestrates
|
|
26
|
+
* Creates and orchestrates ReachabilityPeerConnection instance(s).
|
|
27
27
|
* Listens to events and emits them to consumers.
|
|
28
|
+
*
|
|
29
|
+
* When enablePerUdpUrlReachability is true:
|
|
30
|
+
* - Creates one ReachabilityPeerConnection for each UDP URL
|
|
31
|
+
* - Creates one ReachabilityPeerConnection for all TCP and TLS URLs together
|
|
32
|
+
* Otherwise:
|
|
33
|
+
* - Creates a single ReachabilityPeerConnection for all URLs
|
|
28
34
|
*/
|
|
29
35
|
export declare class ClusterReachability extends EventsScope {
|
|
30
36
|
private reachabilityPeerConnection;
|
|
37
|
+
private reachabilityPeerConnectionsForUdp;
|
|
31
38
|
readonly isVideoMesh: boolean;
|
|
32
39
|
readonly name: any;
|
|
33
40
|
readonly reachedSubnets: Set<string>;
|
|
41
|
+
private enablePerUdpUrlReachability;
|
|
42
|
+
private udpResultEmitted;
|
|
34
43
|
/**
|
|
35
44
|
* Constructor for ClusterReachability
|
|
36
45
|
* @param {string} name cluster name
|
|
37
46
|
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
47
|
+
* @param {boolean} enablePerUdpUrlReachability whether to create separate peer connections per UDP URL
|
|
38
48
|
*/
|
|
39
|
-
constructor(name: string, clusterInfo: ClusterNode);
|
|
49
|
+
constructor(name: string, clusterInfo: ClusterNode, enablePerUdpUrlReachability?: boolean);
|
|
40
50
|
/**
|
|
41
|
-
*
|
|
51
|
+
* Initializes a single ReachabilityPeerConnection for all protocols
|
|
52
|
+
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
53
|
+
* @returns {void}
|
|
54
|
+
*/
|
|
55
|
+
private initializeSingleReachabilityPeerConnection;
|
|
56
|
+
/**
|
|
57
|
+
* Initializes per-URL UDP reachability checks:
|
|
58
|
+
* - One ReachabilityPeerConnection per UDP URL
|
|
59
|
+
* - One ReachabilityPeerConnection for all TCP and TLS URLs together
|
|
60
|
+
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*/
|
|
63
|
+
private initializePerUdpUrlReachabilityCheck;
|
|
64
|
+
/**
|
|
65
|
+
* Sets up event listeners for a ReachabilityPeerConnection instance
|
|
66
|
+
* @param {ReachabilityPeerConnection} rpc the ReachabilityPeerConnection instance
|
|
67
|
+
* @param {boolean} isUdpPerUrl whether this is a per-URL UDP instance
|
|
42
68
|
* @returns {void}
|
|
43
69
|
*/
|
|
44
70
|
private setupReachabilityPeerConnectionEventListeners;
|
|
45
71
|
/**
|
|
72
|
+
* Gets the aggregated reachability result for this cluster.
|
|
46
73
|
* @returns {ClusterReachabilityResult} reachability result for this cluster
|
|
47
74
|
*/
|
|
48
75
|
getResult(): ClusterReachabilityResult;
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -100,5 +100,6 @@ export default {
|
|
|
100
100
|
logUploadIntervalMultiplicationFactor: 0, // if set to 0 or undefined, logs won't be uploaded periodically, if you want periodic logs, recommended value is 1
|
|
101
101
|
stopIceGatheringAfterFirstRelayCandidate: false,
|
|
102
102
|
enableAudioTwccForMultistream: false,
|
|
103
|
+
enablePerUdpUrlReachability: false, // true: separate peer connection per each UDP URL; false: single peer connection for all URLs
|
|
103
104
|
},
|
|
104
105
|
};
|
|
@@ -3,9 +3,9 @@ import HashTree, {LeafDataItem} from './hashTree';
|
|
|
3
3
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
4
|
import {Enum, HTTP_VERBS} from '../constants';
|
|
5
5
|
import {DataSetNames, EMPTY_HASH} from './constants';
|
|
6
|
-
import {ObjectType, HtMeta} from './types';
|
|
6
|
+
import {ObjectType, HtMeta, HashTreeObject} from './types';
|
|
7
7
|
import {LocusDTO} from '../locus-info/types';
|
|
8
|
-
import {deleteNestedObjectsWithHtMeta} from './utils';
|
|
8
|
+
import {deleteNestedObjectsWithHtMeta, isSelf} from './utils';
|
|
9
9
|
|
|
10
10
|
export interface DataSet {
|
|
11
11
|
url: string;
|
|
@@ -20,11 +20,6 @@ export interface DataSet {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface HashTreeObject {
|
|
24
|
-
htMeta: HtMeta;
|
|
25
|
-
data: Record<string, any>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
export interface RootHashMessage {
|
|
29
24
|
dataSets: Array<DataSet>;
|
|
30
25
|
}
|
|
@@ -60,15 +55,6 @@ export type LocusInfoUpdateCallback = (
|
|
|
60
55
|
*/
|
|
61
56
|
class MeetingEndedError extends Error {}
|
|
62
57
|
|
|
63
|
-
/**
|
|
64
|
-
* Checks if the given hash tree object is of type "self"
|
|
65
|
-
* @param {HashTreeObject} object object to check
|
|
66
|
-
* @returns {boolean} True if the object is of type "self", false otherwise
|
|
67
|
-
*/
|
|
68
|
-
export function isSelf(object: HashTreeObject) {
|
|
69
|
-
return object.htMeta.elementId.type.toLowerCase() === ObjectType.self;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
58
|
/**
|
|
73
59
|
* Parses hash tree eventing locus data
|
|
74
60
|
*/
|
package/src/hashTree/types.ts
CHANGED
package/src/hashTree/utils.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
2
|
|
|
3
|
+
import {ObjectType, HashTreeObject} from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if the given hash tree object is of type "self"
|
|
7
|
+
* @param {HashTreeObject} object object to check
|
|
8
|
+
* @returns {boolean} True if the object is of type "self", false otherwise
|
|
9
|
+
*/
|
|
10
|
+
export function isSelf(object: HashTreeObject) {
|
|
11
|
+
return object.htMeta.elementId.type.toLowerCase() === ObjectType.self;
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
/**
|
|
4
15
|
* Analyzes given part of Locus DTO recursively and delete any nested objects that have their own htMeta
|
|
5
16
|
*
|
package/src/locus-info/index.ts
CHANGED
|
@@ -34,11 +34,10 @@ import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
|
34
34
|
import HashTreeParser, {
|
|
35
35
|
DataSet,
|
|
36
36
|
HashTreeMessage,
|
|
37
|
-
HashTreeObject,
|
|
38
|
-
isSelf,
|
|
39
37
|
LocusInfoUpdateType,
|
|
40
38
|
} from '../hashTree/hashTreeParser';
|
|
41
|
-
import {ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
39
|
+
import {HashTreeObject, ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
40
|
+
import {isSelf} from '../hashTree/utils';
|
|
42
41
|
import {Links, LocusDTO, LocusFullState} from './types';
|
|
43
42
|
|
|
44
43
|
export type LocusLLMEvent = {
|
package/src/meetings/index.ts
CHANGED
|
@@ -67,6 +67,8 @@ import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
|
|
|
67
67
|
import NoMeetingInfoError from '../common/errors/no-meeting-info';
|
|
68
68
|
import JoinForbiddenError from '../common/errors/join-forbidden-error';
|
|
69
69
|
import {HashTreeMessage} from '../hashTree/hashTreeParser';
|
|
70
|
+
import {HashTreeObject} from '../hashTree/types';
|
|
71
|
+
import {isSelf} from '../hashTree/utils';
|
|
70
72
|
|
|
71
73
|
let mediaLogger;
|
|
72
74
|
|
|
@@ -420,31 +422,36 @@ export default class Meetings extends WebexPlugin {
|
|
|
420
422
|
* @memberof Meetings
|
|
421
423
|
*/
|
|
422
424
|
getCorrespondingMeetingByLocus(data: LocusEvent) {
|
|
423
|
-
|
|
424
|
-
data.
|
|
425
|
-
data.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
425
|
+
const locusUrl =
|
|
426
|
+
data.stateElementsMessage?.locusUrl || // hash tree event
|
|
427
|
+
data.locusUrl; // classic event
|
|
428
|
+
|
|
429
|
+
// first try to find by locusUrl - that's the simplest and quickest way
|
|
430
|
+
const existingMeeting = this.meetingCollection.getByKey(MEETING_KEY.LOCUS_URL, locusUrl);
|
|
431
|
+
|
|
432
|
+
if (existingMeeting) {
|
|
433
|
+
return existingMeeting;
|
|
431
434
|
}
|
|
432
435
|
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
//
|
|
436
|
+
// if that didn't work, fallback to other fields like correlationId, sipUri, etc
|
|
437
|
+
|
|
438
|
+
// If the event is a hash tree event, we need to extract "self" object from it
|
|
439
|
+
// We don't care about the version, just need to find the meeting this event is for,
|
|
440
|
+
// so any hash tree object of type "self" will do
|
|
441
|
+
const hashTreeEventSelf = data.stateElementsMessage?.locusStateElements?.find(
|
|
442
|
+
(obj: HashTreeObject) => isSelf(obj)
|
|
443
|
+
)?.data;
|
|
444
|
+
|
|
445
|
+
const self = hashTreeEventSelf || data.locus?.self;
|
|
446
|
+
|
|
436
447
|
return (
|
|
437
|
-
this.meetingCollection.getByKey(MEETING_KEY.LOCUS_URL, data.locusUrl) ||
|
|
438
448
|
// @ts-ignore
|
|
439
449
|
this.meetingCollection.getByKey(
|
|
440
450
|
MEETING_KEY.CORRELATION_ID,
|
|
441
451
|
// @ts-ignore
|
|
442
|
-
MeetingsUtil.
|
|
443
|
-
) ||
|
|
444
|
-
this.meetingCollection.getByKey(
|
|
445
|
-
MEETING_KEY.SIP_URI,
|
|
446
|
-
data.locus?.self?.callbackInfo?.callbackAddress
|
|
452
|
+
MeetingsUtil.getCorrelationIdForDevice(this.webex.internal.device.url, self)
|
|
447
453
|
) ||
|
|
454
|
+
this.meetingCollection.getByKey(MEETING_KEY.SIP_URI, self?.callbackInfo?.callbackAddress) ||
|
|
448
455
|
(data.locus?.info?.isUnifiedSpaceMeeting
|
|
449
456
|
? undefined
|
|
450
457
|
: this.meetingCollection.getByKey(
|
package/src/meetings/util.ts
CHANGED
|
@@ -117,15 +117,16 @@ MeetingsUtil.getMediaServerIp = (sdp) => {
|
|
|
117
117
|
return mediaServerIp;
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Finds correlationId of a device from locus self devices array
|
|
122
|
+
* that matches the given deviceUrl
|
|
123
|
+
* @param {string} deviceUrl
|
|
124
|
+
* @param {object} locusSelf
|
|
125
|
+
* @returns {string|false} correlationId or false if not found
|
|
126
|
+
*/
|
|
127
|
+
MeetingsUtil.getCorrelationIdForDevice = (deviceUrl: string, locusSelf: any) => {
|
|
128
|
+
if (locusSelf?.devices) {
|
|
129
|
+
const foundDevice = locusSelf?.devices.find((device) => device.url === deviceUrl);
|
|
129
130
|
|
|
130
131
|
if (foundDevice && foundDevice.correlationId) {
|
|
131
132
|
return foundDevice.correlationId;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {ClusterNode} from './request';
|
|
2
2
|
import EventsScope from '../common/events/events-scope';
|
|
3
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
3
4
|
|
|
4
5
|
import {Enum} from '../constants';
|
|
5
6
|
import {
|
|
@@ -37,36 +38,117 @@ export type Events = Enum<typeof Events>;
|
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* A class that handles reachability checks for a single cluster.
|
|
40
|
-
* Creates and orchestrates
|
|
41
|
+
* Creates and orchestrates ReachabilityPeerConnection instance(s).
|
|
41
42
|
* Listens to events and emits them to consumers.
|
|
43
|
+
*
|
|
44
|
+
* When enablePerUdpUrlReachability is true:
|
|
45
|
+
* - Creates one ReachabilityPeerConnection for each UDP URL
|
|
46
|
+
* - Creates one ReachabilityPeerConnection for all TCP and TLS URLs together
|
|
47
|
+
* Otherwise:
|
|
48
|
+
* - Creates a single ReachabilityPeerConnection for all URLs
|
|
42
49
|
*/
|
|
43
50
|
export class ClusterReachability extends EventsScope {
|
|
44
|
-
private reachabilityPeerConnection: ReachabilityPeerConnection;
|
|
51
|
+
private reachabilityPeerConnection: ReachabilityPeerConnection | null = null;
|
|
52
|
+
private reachabilityPeerConnectionsForUdp: ReachabilityPeerConnection[] = [];
|
|
53
|
+
|
|
45
54
|
public readonly isVideoMesh: boolean;
|
|
46
55
|
public readonly name;
|
|
47
56
|
public readonly reachedSubnets: Set<string> = new Set();
|
|
48
57
|
|
|
58
|
+
private enablePerUdpUrlReachability: boolean;
|
|
59
|
+
private udpResultEmitted = false;
|
|
60
|
+
|
|
49
61
|
/**
|
|
50
62
|
* Constructor for ClusterReachability
|
|
51
63
|
* @param {string} name cluster name
|
|
52
64
|
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
65
|
+
* @param {boolean} enablePerUdpUrlReachability whether to create separate peer connections per UDP URL
|
|
53
66
|
*/
|
|
54
|
-
constructor(name: string, clusterInfo: ClusterNode) {
|
|
67
|
+
constructor(name: string, clusterInfo: ClusterNode, enablePerUdpUrlReachability = false) {
|
|
55
68
|
super();
|
|
56
69
|
this.name = name;
|
|
57
70
|
this.isVideoMesh = clusterInfo.isVideoMesh;
|
|
71
|
+
this.enablePerUdpUrlReachability = enablePerUdpUrlReachability;
|
|
58
72
|
|
|
59
|
-
this.
|
|
73
|
+
if (this.enablePerUdpUrlReachability) {
|
|
74
|
+
this.initializePerUdpUrlReachabilityCheck(clusterInfo);
|
|
75
|
+
} else {
|
|
76
|
+
this.initializeSingleReachabilityPeerConnection(clusterInfo);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Initializes a single ReachabilityPeerConnection for all protocols
|
|
82
|
+
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
83
|
+
* @returns {void}
|
|
84
|
+
*/
|
|
85
|
+
private initializeSingleReachabilityPeerConnection(clusterInfo: ClusterNode) {
|
|
86
|
+
this.reachabilityPeerConnection = new ReachabilityPeerConnection(this.name, clusterInfo);
|
|
87
|
+
this.setupReachabilityPeerConnectionEventListeners(this.reachabilityPeerConnection);
|
|
62
88
|
}
|
|
63
89
|
|
|
64
90
|
/**
|
|
65
|
-
*
|
|
91
|
+
* Initializes per-URL UDP reachability checks:
|
|
92
|
+
* - One ReachabilityPeerConnection per UDP URL
|
|
93
|
+
* - One ReachabilityPeerConnection for all TCP and TLS URLs together
|
|
94
|
+
* @param {ClusterNode} clusterInfo information about the media cluster
|
|
66
95
|
* @returns {void}
|
|
67
96
|
*/
|
|
68
|
-
private
|
|
69
|
-
|
|
97
|
+
private initializePerUdpUrlReachabilityCheck(clusterInfo: ClusterNode) {
|
|
98
|
+
LoggerProxy.logger.log(
|
|
99
|
+
`ClusterReachability#initializePerUdpUrlReachabilityCheck --> cluster: ${this.name}, performing per-URL UDP reachability for ${clusterInfo.udp.length} URLs`
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Create one ReachabilityPeerConnection for each UDP URL
|
|
103
|
+
clusterInfo.udp.forEach((udpUrl) => {
|
|
104
|
+
const singleUdpClusterInfo: ClusterNode = {
|
|
105
|
+
isVideoMesh: clusterInfo.isVideoMesh,
|
|
106
|
+
udp: [udpUrl],
|
|
107
|
+
tcp: [],
|
|
108
|
+
xtls: [],
|
|
109
|
+
};
|
|
110
|
+
const rpc = new ReachabilityPeerConnection(this.name, singleUdpClusterInfo);
|
|
111
|
+
this.setupReachabilityPeerConnectionEventListeners(rpc, true);
|
|
112
|
+
this.reachabilityPeerConnectionsForUdp.push(rpc);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Create one ReachabilityPeerConnection for all TCP and TLS URLs together
|
|
116
|
+
if (clusterInfo.tcp.length > 0 || clusterInfo.xtls.length > 0) {
|
|
117
|
+
const tcpTlsClusterInfo: ClusterNode = {
|
|
118
|
+
isVideoMesh: clusterInfo.isVideoMesh,
|
|
119
|
+
udp: [],
|
|
120
|
+
tcp: clusterInfo.tcp,
|
|
121
|
+
xtls: clusterInfo.xtls,
|
|
122
|
+
};
|
|
123
|
+
this.reachabilityPeerConnection = new ReachabilityPeerConnection(
|
|
124
|
+
this.name,
|
|
125
|
+
tcpTlsClusterInfo
|
|
126
|
+
);
|
|
127
|
+
this.setupReachabilityPeerConnectionEventListeners(this.reachabilityPeerConnection);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Sets up event listeners for a ReachabilityPeerConnection instance
|
|
133
|
+
* @param {ReachabilityPeerConnection} rpc the ReachabilityPeerConnection instance
|
|
134
|
+
* @param {boolean} isUdpPerUrl whether this is a per-URL UDP instance
|
|
135
|
+
* @returns {void}
|
|
136
|
+
*/
|
|
137
|
+
private setupReachabilityPeerConnectionEventListeners(
|
|
138
|
+
rpc: ReachabilityPeerConnection,
|
|
139
|
+
isUdpPerUrl = false
|
|
140
|
+
) {
|
|
141
|
+
rpc.on(ReachabilityPeerConnectionEvents.resultReady, (data) => {
|
|
142
|
+
// For per-URL UDP checks, only emit the first successful UDP result
|
|
143
|
+
if (isUdpPerUrl && data.protocol === 'udp') {
|
|
144
|
+
if (this.udpResultEmitted) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (data.result === 'reachable') {
|
|
148
|
+
this.udpResultEmitted = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
70
152
|
this.emit(
|
|
71
153
|
{
|
|
72
154
|
file: 'clusterReachability',
|
|
@@ -77,21 +159,18 @@ export class ClusterReachability extends EventsScope {
|
|
|
77
159
|
);
|
|
78
160
|
});
|
|
79
161
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
);
|
|
162
|
+
rpc.on(ReachabilityPeerConnectionEvents.clientMediaIpsUpdated, (data) => {
|
|
163
|
+
this.emit(
|
|
164
|
+
{
|
|
165
|
+
file: 'clusterReachability',
|
|
166
|
+
function: 'setupReachabilityPeerConnectionEventListeners',
|
|
167
|
+
},
|
|
168
|
+
Events.clientMediaIpsUpdated,
|
|
169
|
+
data
|
|
170
|
+
);
|
|
171
|
+
});
|
|
93
172
|
|
|
94
|
-
|
|
173
|
+
rpc.on(ReachabilityPeerConnectionEvents.natTypeUpdated, (data) => {
|
|
95
174
|
this.emit(
|
|
96
175
|
{
|
|
97
176
|
file: 'clusterReachability',
|
|
@@ -102,18 +181,54 @@ export class ClusterReachability extends EventsScope {
|
|
|
102
181
|
);
|
|
103
182
|
});
|
|
104
183
|
|
|
105
|
-
|
|
106
|
-
data.subnets.forEach((subnet) => {
|
|
184
|
+
rpc.on(ReachabilityPeerConnectionEvents.reachedSubnets, (data) => {
|
|
185
|
+
data.subnets.forEach((subnet: string) => {
|
|
107
186
|
this.reachedSubnets.add(subnet);
|
|
108
187
|
});
|
|
109
188
|
});
|
|
110
189
|
}
|
|
111
190
|
|
|
112
191
|
/**
|
|
192
|
+
* Gets the aggregated reachability result for this cluster.
|
|
113
193
|
* @returns {ClusterReachabilityResult} reachability result for this cluster
|
|
114
194
|
*/
|
|
115
195
|
getResult(): ClusterReachabilityResult {
|
|
116
|
-
|
|
196
|
+
if (!this.enablePerUdpUrlReachability) {
|
|
197
|
+
return (
|
|
198
|
+
this.reachabilityPeerConnection?.getResult() ?? {
|
|
199
|
+
udp: {result: 'untested'},
|
|
200
|
+
tcp: {result: 'untested'},
|
|
201
|
+
xtls: {result: 'untested'},
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result: ClusterReachabilityResult = {
|
|
207
|
+
udp: {result: 'untested'},
|
|
208
|
+
tcp: {result: 'untested'},
|
|
209
|
+
xtls: {result: 'untested'},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Get the first reachable UDP result from per-URL instances
|
|
213
|
+
for (const rpc of this.reachabilityPeerConnectionsForUdp) {
|
|
214
|
+
const rpcResult = rpc.getResult();
|
|
215
|
+
if (rpcResult.udp.result === 'reachable') {
|
|
216
|
+
result.udp = rpcResult.udp;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
if (rpcResult.udp.result === 'unreachable' && result.udp.result === 'untested') {
|
|
220
|
+
result.udp = rpcResult.udp;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Get TCP and TLS results from the main peer connection
|
|
225
|
+
if (this.reachabilityPeerConnection) {
|
|
226
|
+
const mainResult = this.reachabilityPeerConnection.getResult();
|
|
227
|
+
result.tcp = mainResult.tcp;
|
|
228
|
+
result.xtls = mainResult.xtls;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
117
232
|
}
|
|
118
233
|
|
|
119
234
|
/**
|
|
@@ -121,7 +236,17 @@ export class ClusterReachability extends EventsScope {
|
|
|
121
236
|
* @returns {Promise<ClusterReachabilityResult>}
|
|
122
237
|
*/
|
|
123
238
|
async start(): Promise<ClusterReachabilityResult> {
|
|
124
|
-
|
|
239
|
+
const startPromises: Promise<ClusterReachabilityResult>[] = [];
|
|
240
|
+
|
|
241
|
+
this.reachabilityPeerConnectionsForUdp.forEach((rpc) => {
|
|
242
|
+
startPromises.push(rpc.start());
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (this.reachabilityPeerConnection) {
|
|
246
|
+
startPromises.push(this.reachabilityPeerConnection.start());
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
await Promise.all(startPromises);
|
|
125
250
|
|
|
126
251
|
return this.getResult();
|
|
127
252
|
}
|
|
@@ -131,6 +256,7 @@ export class ClusterReachability extends EventsScope {
|
|
|
131
256
|
* @returns {void}
|
|
132
257
|
*/
|
|
133
258
|
public abort() {
|
|
134
|
-
this.
|
|
259
|
+
this.reachabilityPeerConnectionsForUdp.forEach((rpc) => rpc.abort());
|
|
260
|
+
this.reachabilityPeerConnection?.abort();
|
|
135
261
|
}
|
|
136
262
|
}
|
|
@@ -961,7 +961,12 @@ export default class Reachability extends EventsScope {
|
|
|
961
961
|
Object.keys(clusterList).forEach((key) => {
|
|
962
962
|
const cluster = clusterList[key];
|
|
963
963
|
|
|
964
|
-
this.clusterReachability[key] = new ClusterReachability(
|
|
964
|
+
this.clusterReachability[key] = new ClusterReachability(
|
|
965
|
+
key,
|
|
966
|
+
cluster,
|
|
967
|
+
// @ts-ignore
|
|
968
|
+
this.webex.config.meetings.enablePerUdpUrlReachability
|
|
969
|
+
);
|
|
965
970
|
this.clusterReachability[key].on(Events.resultReady, async (data: ResultEventData) => {
|
|
966
971
|
const {protocol, result, clientMediaIPs, latencyInMilliseconds} = data;
|
|
967
972
|
|
|
@@ -243,7 +243,9 @@ export class ReachabilityPeerConnection extends EventsScope {
|
|
|
243
243
|
if (result.latencyInMilliseconds === undefined) {
|
|
244
244
|
LoggerProxy.logger.log(
|
|
245
245
|
// @ts-ignore
|
|
246
|
-
`Reachability:ReachabilityPeerConnection#saveResult --> Successfully reached ${
|
|
246
|
+
`Reachability:ReachabilityPeerConnection#saveResult --> Successfully reached ${
|
|
247
|
+
this.clusterName
|
|
248
|
+
} over ${protocol}: ${latency}ms, serverIp=${serverIp || 'unknown'}`
|
|
247
249
|
);
|
|
248
250
|
result.latencyInMilliseconds = latency;
|
|
249
251
|
result.result = 'reachable';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {HashTreeObject, ObjectType} from '../../../../src/hashTree/types';
|
|
2
|
+
import {deleteNestedObjectsWithHtMeta, isSelf} from '../../../../src/hashTree/utils';
|
|
2
3
|
|
|
3
4
|
import {assert} from '@webex/test-helper-chai';
|
|
4
5
|
|
|
@@ -100,4 +101,40 @@ describe('Hash Tree Utils', () => {
|
|
|
100
101
|
});
|
|
101
102
|
});
|
|
102
103
|
});
|
|
104
|
+
|
|
105
|
+
describe('#isSelf', () => {
|
|
106
|
+
['self', 'SELF', 'Self'].forEach((type) => {
|
|
107
|
+
it(`should return true for object with type="${type}"`, () => {
|
|
108
|
+
const selfObject = {
|
|
109
|
+
htMeta: {
|
|
110
|
+
elementId: {
|
|
111
|
+
type,
|
|
112
|
+
id: 1,
|
|
113
|
+
version: 1,
|
|
114
|
+
},
|
|
115
|
+
dataSetNames: [],
|
|
116
|
+
},
|
|
117
|
+
data: {},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
assert.isTrue(isSelf(selfObject as HashTreeObject));
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should return false for non-self object', () => {
|
|
125
|
+
const participantObject = {
|
|
126
|
+
htMeta: {
|
|
127
|
+
elementId: {
|
|
128
|
+
type: ObjectType.participant,
|
|
129
|
+
id: 2,
|
|
130
|
+
version: 1,
|
|
131
|
+
},
|
|
132
|
+
dataSetNames: [],
|
|
133
|
+
},
|
|
134
|
+
data: {},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
assert.isFalse(isSelf(participantObject));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
103
140
|
});
|