@webex/plugin-meetings 3.10.0-next.26 → 3.10.0-next.28
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 +2 -7
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +10 -7
- package/dist/locus-info/index.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/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 +1 -5
- package/src/locus-info/index.ts +11 -10
- 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/hashTreeParser.ts +2 -10
- package/test/unit/spec/reachability/clusterReachability.ts +125 -1
- package/test/unit/spec/reachability/index.ts +3 -3
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
|
};
|
|
@@ -1113,11 +1113,7 @@ class HashTreeParser {
|
|
|
1113
1113
|
|
|
1114
1114
|
const url = `${dataSet.url}/sync`;
|
|
1115
1115
|
const body = {
|
|
1116
|
-
|
|
1117
|
-
name: dataSet.name,
|
|
1118
|
-
leafCount: dataSet.leafCount,
|
|
1119
|
-
root: dataSet.hashTree?.getRootHash(),
|
|
1120
|
-
},
|
|
1116
|
+
leafCount: dataSet.leafCount,
|
|
1121
1117
|
leafDataEntries: [],
|
|
1122
1118
|
};
|
|
1123
1119
|
|
package/src/locus-info/index.ts
CHANGED
|
@@ -980,8 +980,8 @@ export default class LocusInfo extends EventsScope {
|
|
|
980
980
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
981
981
|
handleOneOnOneEvent(eventType: string) {
|
|
982
982
|
if (
|
|
983
|
-
this.parsedLocus.fullState
|
|
984
|
-
this.parsedLocus.fullState
|
|
983
|
+
this.parsedLocus.fullState?.type === _CALL_ ||
|
|
984
|
+
this.parsedLocus.fullState?.type === _SIP_BRIDGE_
|
|
985
985
|
) {
|
|
986
986
|
// for 1:1 bob calls alice and alice declines, notify the meeting state
|
|
987
987
|
if (eventType === LOCUSEVENT.PARTICIPANT_DECLINED) {
|
|
@@ -1094,9 +1094,9 @@ export default class LocusInfo extends EventsScope {
|
|
|
1094
1094
|
*/
|
|
1095
1095
|
isMeetingActive() {
|
|
1096
1096
|
if (
|
|
1097
|
-
this.parsedLocus.fullState
|
|
1098
|
-
this.parsedLocus.fullState
|
|
1099
|
-
this.parsedLocus.fullState
|
|
1097
|
+
this.parsedLocus.fullState?.type === _CALL_ ||
|
|
1098
|
+
this.parsedLocus.fullState?.type === _SIP_BRIDGE_ ||
|
|
1099
|
+
this.parsedLocus.fullState?.type === _SPACE_SHARE_
|
|
1100
1100
|
) {
|
|
1101
1101
|
// @ts-ignore
|
|
1102
1102
|
const partner = this.getLocusPartner(this.participants, this.self);
|
|
@@ -1189,7 +1189,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1189
1189
|
}
|
|
1190
1190
|
);
|
|
1191
1191
|
}
|
|
1192
|
-
} else if (this.parsedLocus.fullState
|
|
1192
|
+
} else if (this.parsedLocus.fullState?.type === _MEETING_) {
|
|
1193
1193
|
if (
|
|
1194
1194
|
this.fullState &&
|
|
1195
1195
|
(this.fullState.state === LOCUS.STATE.INACTIVE ||
|
|
@@ -1286,6 +1286,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1286
1286
|
compareSelfAndHost() {
|
|
1287
1287
|
// In some cases the host info is not present but the moderator values changes from null to false so it triggers an update
|
|
1288
1288
|
if (
|
|
1289
|
+
this.parsedLocus.self &&
|
|
1289
1290
|
this.parsedLocus.self.selfIdentity === this.parsedLocus.host?.hostId &&
|
|
1290
1291
|
this.parsedLocus.self.moderator
|
|
1291
1292
|
) {
|
|
@@ -1970,14 +1971,14 @@ export default class LocusInfo extends EventsScope {
|
|
|
1970
1971
|
}
|
|
1971
1972
|
|
|
1972
1973
|
// TODO: check if we need to save the sipUri here as well
|
|
1973
|
-
// this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState
|
|
1974
|
+
// this.emit(LOCUSINFO.EVENTS.MEETING_UPDATE, SelfUtils.getSipUrl(this.getLocusPartner(participants, self), this.parsedLocus.fullState?.type, this.parsedLocus.info?.sipUri));
|
|
1974
1975
|
const result = SelfUtils.getSipUrl(
|
|
1975
1976
|
this.getLocusPartner(this.participants, self),
|
|
1976
|
-
this.parsedLocus.fullState
|
|
1977
|
-
this.parsedLocus.info
|
|
1977
|
+
this.parsedLocus.fullState?.type,
|
|
1978
|
+
this.parsedLocus.info?.sipUri
|
|
1978
1979
|
);
|
|
1979
1980
|
|
|
1980
|
-
if (result
|
|
1981
|
+
if (result?.sipUri) {
|
|
1981
1982
|
this.updateMeeting(result);
|
|
1982
1983
|
}
|
|
1983
1984
|
|
|
@@ -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';
|
|
@@ -1187,11 +1187,7 @@ describe('HashTreeParser', () => {
|
|
|
1187
1187
|
method: 'POST',
|
|
1188
1188
|
uri: `${mainDataSetUrl}/sync`,
|
|
1189
1189
|
body: {
|
|
1190
|
-
|
|
1191
|
-
name: 'main',
|
|
1192
|
-
leafCount: 16,
|
|
1193
|
-
root: '472801612a448c4e0ab74975ed9d7a2e'
|
|
1194
|
-
},
|
|
1190
|
+
leafCount: 16,
|
|
1195
1191
|
leafDataEntries: [
|
|
1196
1192
|
{leafIndex: 0, elementIds: [{type: 'locus', id: 0, version: 201}]},
|
|
1197
1193
|
{leafIndex: 4, elementIds: [{type: 'participant', id: 4, version: 301}]},
|
|
@@ -1244,11 +1240,7 @@ describe('HashTreeParser', () => {
|
|
|
1244
1240
|
method: 'POST',
|
|
1245
1241
|
uri: `${parser.dataSets.self.url}/sync`,
|
|
1246
1242
|
body: {
|
|
1247
|
-
|
|
1248
|
-
name: 'self',
|
|
1249
|
-
leafCount: 1,
|
|
1250
|
-
root: '483ba32a5db954720b4c43ed528d8075'
|
|
1251
|
-
},
|
|
1243
|
+
leafCount: 1,
|
|
1252
1244
|
leafDataEntries: [
|
|
1253
1245
|
{leafIndex: 0, elementIds: [{type: 'self', id: 4, version: 102}]},
|
|
1254
1246
|
],
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
NatTypeUpdatedEventData,
|
|
11
11
|
} from '@webex/plugin-meetings/src/reachability/clusterReachability';
|
|
12
12
|
import {ReachabilityPeerConnection} from '@webex/plugin-meetings/src/reachability/reachabilityPeerConnection';
|
|
13
|
+
import {ReachabilityPeerConnectionEvents} from '@webex/plugin-meetings/src/reachability/reachability.types';
|
|
13
14
|
|
|
14
15
|
describe('ClusterReachability', () => {
|
|
15
16
|
let previousRTCPeerConnection;
|
|
@@ -92,6 +93,22 @@ describe('ClusterReachability', () => {
|
|
|
92
93
|
assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
|
|
93
94
|
});
|
|
94
95
|
|
|
96
|
+
it('should create separate peer connections when enablePerUdpUrlReachability is true', () => {
|
|
97
|
+
const perUdpClusterReachability = new ClusterReachability(
|
|
98
|
+
'testName',
|
|
99
|
+
{
|
|
100
|
+
isVideoMesh: false,
|
|
101
|
+
udp: ['stun:udp1', 'stun:udp2'],
|
|
102
|
+
tcp: ['stun:tcp1.webex.com'],
|
|
103
|
+
xtls: ['stun:xtls1.webex.com'],
|
|
104
|
+
},
|
|
105
|
+
true
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
assert.equal((perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp.length, 2);
|
|
109
|
+
assert.instanceOf((perUdpClusterReachability as any).reachabilityPeerConnection, ReachabilityPeerConnection);
|
|
110
|
+
});
|
|
111
|
+
|
|
95
112
|
describe('#event relaying', () => {
|
|
96
113
|
let clock;
|
|
97
114
|
|
|
@@ -172,6 +189,44 @@ describe('ClusterReachability', () => {
|
|
|
172
189
|
clusterReachability.abort();
|
|
173
190
|
await promise;
|
|
174
191
|
});
|
|
192
|
+
|
|
193
|
+
it('emits only the first successful UDP result when enablePerUdpUrlReachability is true', async () => {
|
|
194
|
+
const perUdpClusterReachability = new ClusterReachability(
|
|
195
|
+
'testName',
|
|
196
|
+
{
|
|
197
|
+
isVideoMesh: false,
|
|
198
|
+
udp: ['stun:udp1', 'stun:udp2'],
|
|
199
|
+
tcp: [],
|
|
200
|
+
xtls: [],
|
|
201
|
+
},
|
|
202
|
+
true
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const udpEvents: ResultEventData[] = [];
|
|
206
|
+
perUdpClusterReachability.on(Events.resultReady, (data: ResultEventData) => {
|
|
207
|
+
udpEvents.push(data);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
|
|
211
|
+
const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
|
|
212
|
+
|
|
213
|
+
udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
|
|
214
|
+
protocol: 'udp',
|
|
215
|
+
result: 'reachable',
|
|
216
|
+
latencyInMilliseconds: 50,
|
|
217
|
+
clientMediaIPs: ['1.1.1.1'],
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
|
|
221
|
+
protocol: 'udp',
|
|
222
|
+
result: 'reachable',
|
|
223
|
+
latencyInMilliseconds: 30,
|
|
224
|
+
clientMediaIPs: ['2.2.2.2'],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
assert.equal(udpEvents.length, 1);
|
|
228
|
+
assert.equal(udpEvents[0].latencyInMilliseconds, 50);
|
|
229
|
+
});
|
|
175
230
|
});
|
|
176
231
|
|
|
177
232
|
describe('#subnet collection', () => {
|
|
@@ -236,6 +291,38 @@ describe('ClusterReachability', () => {
|
|
|
236
291
|
assert.equal(clusterReachability.reachedSubnets.size, 3);
|
|
237
292
|
assert.deepEqual(Array.from(clusterReachability.reachedSubnets), ['192.168.1.1', '10.0.0.1', '172.16.0.1']);
|
|
238
293
|
});
|
|
294
|
+
|
|
295
|
+
it('collects reached subnets from all peer connections when enablePerUdpUrlReachability is true', async () => {
|
|
296
|
+
const perUdpClusterReachability = new ClusterReachability(
|
|
297
|
+
'testName',
|
|
298
|
+
{
|
|
299
|
+
isVideoMesh: false,
|
|
300
|
+
udp: ['stun:udp1', 'stun:udp2'],
|
|
301
|
+
tcp: ['stun:tcp1.webex.com'],
|
|
302
|
+
xtls: [],
|
|
303
|
+
},
|
|
304
|
+
true
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
|
|
308
|
+
const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
|
|
309
|
+
const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
|
|
310
|
+
|
|
311
|
+
udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
|
|
312
|
+
subnets: ['192.168.1.1'],
|
|
313
|
+
});
|
|
314
|
+
udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
|
|
315
|
+
subnets: ['10.0.0.1'],
|
|
316
|
+
});
|
|
317
|
+
tcpTlsRpc.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
|
|
318
|
+
subnets: ['172.16.0.1'],
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
assert.equal(perUdpClusterReachability.reachedSubnets.size, 3);
|
|
322
|
+
assert.isTrue(perUdpClusterReachability.reachedSubnets.has('192.168.1.1'));
|
|
323
|
+
assert.isTrue(perUdpClusterReachability.reachedSubnets.has('10.0.0.1'));
|
|
324
|
+
assert.isTrue(perUdpClusterReachability.reachedSubnets.has('172.16.0.1'));
|
|
325
|
+
});
|
|
239
326
|
});
|
|
240
327
|
|
|
241
328
|
describe('#delegation', () => {
|
|
@@ -277,6 +364,43 @@ describe('ClusterReachability', () => {
|
|
|
277
364
|
assert.calledOnce(rpcGetResultStub);
|
|
278
365
|
assert.deepEqual(result, expectedResult);
|
|
279
366
|
});
|
|
367
|
+
|
|
368
|
+
it('delegates start() and abort() to all peer connections when enablePerUdpUrlReachability is true', async () => {
|
|
369
|
+
const perUdpClusterReachability = new ClusterReachability(
|
|
370
|
+
'testName',
|
|
371
|
+
{
|
|
372
|
+
isVideoMesh: false,
|
|
373
|
+
udp: ['stun:udp1', 'stun:udp2'],
|
|
374
|
+
tcp: ['stun:tcp1.webex.com'],
|
|
375
|
+
xtls: [],
|
|
376
|
+
},
|
|
377
|
+
true
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
|
|
381
|
+
const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
|
|
382
|
+
const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
|
|
383
|
+
|
|
384
|
+
const startStub1 = sinon.stub(udpRpc1, 'start').resolves({udp: {result: 'reachable'}});
|
|
385
|
+
const startStub2 = sinon.stub(udpRpc2, 'start').resolves({udp: {result: 'unreachable'}});
|
|
386
|
+
const startStubTcp = sinon.stub(tcpTlsRpc, 'start').resolves({tcp: {result: 'reachable'}});
|
|
387
|
+
|
|
388
|
+
const abortStub1 = sinon.stub(udpRpc1, 'abort');
|
|
389
|
+
const abortStub2 = sinon.stub(udpRpc2, 'abort');
|
|
390
|
+
const abortStubTcp = sinon.stub(tcpTlsRpc, 'abort');
|
|
391
|
+
|
|
392
|
+
await perUdpClusterReachability.start();
|
|
393
|
+
|
|
394
|
+
assert.calledOnce(startStub1);
|
|
395
|
+
assert.calledOnce(startStub2);
|
|
396
|
+
assert.calledOnce(startStubTcp);
|
|
397
|
+
|
|
398
|
+
perUdpClusterReachability.abort();
|
|
399
|
+
|
|
400
|
+
assert.calledOnce(abortStub1);
|
|
401
|
+
assert.calledOnce(abortStub2);
|
|
402
|
+
assert.calledOnce(abortStubTcp);
|
|
403
|
+
});
|
|
280
404
|
});
|
|
281
405
|
|
|
282
406
|
describe('#WebRTC peer connection setup', () => {
|
|
@@ -616,4 +740,4 @@ describe('ClusterReachability', () => {
|
|
|
616
740
|
});
|
|
617
741
|
});
|
|
618
742
|
});
|
|
619
|
-
});
|
|
743
|
+
});
|
|
@@ -1693,7 +1693,7 @@ describe('gatherReachability', () => {
|
|
|
1693
1693
|
udp: ['testUDP1', 'testUDP2'],
|
|
1694
1694
|
tcp: [], // empty list because TCP is disabled in config
|
|
1695
1695
|
xtls: ['testXTLS1', 'testXTLS2'],
|
|
1696
|
-
});
|
|
1696
|
+
}, undefined);
|
|
1697
1697
|
});
|
|
1698
1698
|
|
|
1699
1699
|
it('does not do TLS reachability if it is disabled in config', async () => {
|
|
@@ -1728,7 +1728,7 @@ describe('gatherReachability', () => {
|
|
|
1728
1728
|
udp: ['testUDP1', 'testUDP2'],
|
|
1729
1729
|
tcp: ['testTCP1', 'testTCP2'],
|
|
1730
1730
|
xtls: [], // empty list because TLS is disabled in config
|
|
1731
|
-
});
|
|
1731
|
+
}, undefined);
|
|
1732
1732
|
});
|
|
1733
1733
|
|
|
1734
1734
|
it('does not do TCP or TLS reachability if it is disabled in config', async () => {
|
|
@@ -1763,7 +1763,7 @@ describe('gatherReachability', () => {
|
|
|
1763
1763
|
udp: ['testUDP1', 'testUDP2'],
|
|
1764
1764
|
tcp: [], // empty list because TCP is disabled in config
|
|
1765
1765
|
xtls: [], // empty list because TLS is disabled in config
|
|
1766
|
-
});
|
|
1766
|
+
}, undefined);
|
|
1767
1767
|
});
|
|
1768
1768
|
|
|
1769
1769
|
it('retry of getClusters is succesfull', async () => {
|