@webex/plugin-meetings 3.0.0-beta.256 → 3.0.0-beta.258
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/common/errors/webex-errors.js +2 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting-info/utilv2.js +1 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/reachability/index.js +82 -29
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +1 -1
- package/dist/reachability/request.js.map +1 -1
- package/dist/roap/request.js +5 -13
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +4 -4
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/common/errors/webex-errors.d.ts +1 -1
- package/dist/types/meetings/index.d.ts +11 -1
- package/dist/types/reachability/index.d.ts +30 -5
- package/package.json +19 -19
- package/src/common/errors/webex-errors.ts +2 -2
- package/src/meeting-info/utilv2.ts +1 -1
- package/src/reachability/index.ts +101 -25
- package/src/reachability/request.ts +1 -1
- package/src/roap/request.ts +3 -16
- package/src/roap/turnDiscovery.ts +4 -3
- package/test/unit/spec/meeting/index.js +2 -1
- package/test/unit/spec/reachability/index.ts +163 -7
- package/test/unit/spec/roap/index.ts +1 -1
- package/test/unit/spec/roap/request.ts +1 -0
- package/test/unit/spec/roap/turnDiscovery.ts +11 -11
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable class-methods-use-this */
|
|
6
6
|
/* globals window */
|
|
7
|
-
import
|
|
7
|
+
import {uniq, mapValues, pick} from 'lodash';
|
|
8
8
|
|
|
9
9
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
10
10
|
import MeetingUtil from '../meeting/util';
|
|
@@ -16,7 +16,39 @@ import ReachabilityRequest from './request';
|
|
|
16
16
|
const DEFAULT_TIMEOUT = 3000;
|
|
17
17
|
const VIDEO_MESH_TIMEOUT = 1000;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// result for a specific transport protocol (like udp or tcp)
|
|
20
|
+
export type TransportResult = {
|
|
21
|
+
reachable: 'true' | 'false';
|
|
22
|
+
latencyInMilliseconds?: string;
|
|
23
|
+
clientMediaIPs?: string[];
|
|
24
|
+
untested?: 'true';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// reachability result for a specifc media cluster
|
|
28
|
+
type ReachabilityResult = {
|
|
29
|
+
udp: TransportResult;
|
|
30
|
+
tcp: TransportResult;
|
|
31
|
+
xtls: {
|
|
32
|
+
untested: 'true';
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
// this is the type that is required by the backend when we send them reachability results
|
|
36
|
+
export type ReachabilityResults = Record<string, ReachabilityResult>;
|
|
37
|
+
|
|
38
|
+
// this is the type used by Reachability class internally and stored in local storage
|
|
39
|
+
type InternalReachabilityResults = Record<
|
|
40
|
+
string,
|
|
41
|
+
ReachabilityResult & {
|
|
42
|
+
isVideoMesh?: boolean;
|
|
43
|
+
}
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export type ICECandidateResult = {
|
|
47
|
+
clusterId: string;
|
|
48
|
+
isVideoMesh: boolean;
|
|
49
|
+
elapsed?: string | null;
|
|
50
|
+
publicIPs?: string[];
|
|
51
|
+
};
|
|
20
52
|
/**
|
|
21
53
|
* @class Reachability
|
|
22
54
|
* @export
|
|
@@ -61,7 +93,7 @@ export default class Reachability {
|
|
|
61
93
|
* @async
|
|
62
94
|
* @memberof Reachability
|
|
63
95
|
*/
|
|
64
|
-
public async gatherReachability() {
|
|
96
|
+
public async gatherReachability(): Promise<InternalReachabilityResults> {
|
|
65
97
|
this.setup();
|
|
66
98
|
|
|
67
99
|
// Remove stored reachability results to ensure no stale data
|
|
@@ -106,13 +138,45 @@ export default class Reachability {
|
|
|
106
138
|
}
|
|
107
139
|
}
|
|
108
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Reachability results as an object in the format that backend expects
|
|
143
|
+
*
|
|
144
|
+
* @returns {any} reachability results that need to be sent to the backend
|
|
145
|
+
*/
|
|
146
|
+
async getReachabilityResults(): Promise<ReachabilityResults | undefined> {
|
|
147
|
+
let results: ReachabilityResults;
|
|
148
|
+
|
|
149
|
+
// these are the only props that backend needs in the reachability results:
|
|
150
|
+
const reachabilityResultsProps: Array<keyof ReachabilityResult> = ['udp', 'tcp', 'xtls'];
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
const resultsJson = await this.webex.boundedStorage.get(
|
|
155
|
+
REACHABILITY.namespace,
|
|
156
|
+
REACHABILITY.localStorageResult
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const internalResults: InternalReachabilityResults = JSON.parse(resultsJson);
|
|
160
|
+
|
|
161
|
+
results = mapValues(internalResults, (result) => pick(result, reachabilityResultsProps));
|
|
162
|
+
} catch (e) {
|
|
163
|
+
// empty storage, that's ok
|
|
164
|
+
LoggerProxy.logger.warn(
|
|
165
|
+
'Roap:request#attachReachabilityData --> Error parsing reachability data: ',
|
|
166
|
+
e
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return results;
|
|
171
|
+
}
|
|
172
|
+
|
|
109
173
|
/**
|
|
110
174
|
* fetches reachability data and checks for cluster reachability
|
|
111
175
|
* @returns {boolean}
|
|
112
176
|
* @public
|
|
113
177
|
* @memberof Reachability
|
|
114
178
|
*/
|
|
115
|
-
async
|
|
179
|
+
async isAnyPublicClusterReachable() {
|
|
116
180
|
let reachable = false;
|
|
117
181
|
// @ts-ignore
|
|
118
182
|
const reachabilityData = await this.webex.boundedStorage
|
|
@@ -121,10 +185,12 @@ export default class Reachability {
|
|
|
121
185
|
|
|
122
186
|
if (reachabilityData) {
|
|
123
187
|
try {
|
|
124
|
-
const reachabilityResults = JSON.parse(reachabilityData);
|
|
188
|
+
const reachabilityResults: InternalReachabilityResults = JSON.parse(reachabilityData);
|
|
125
189
|
|
|
126
190
|
reachable = Object.values(reachabilityResults).some(
|
|
127
|
-
(result
|
|
191
|
+
(result) =>
|
|
192
|
+
!result.isVideoMesh &&
|
|
193
|
+
(result.udp?.reachable === 'true' || result.tcp?.reachable === 'true')
|
|
128
194
|
);
|
|
129
195
|
} catch (e) {
|
|
130
196
|
LoggerProxy.logger.error(
|
|
@@ -144,7 +210,7 @@ export default class Reachability {
|
|
|
144
210
|
* @memberof Reachability
|
|
145
211
|
*/
|
|
146
212
|
private buildPeerConnectionConfig(cluster: any) {
|
|
147
|
-
const iceServers =
|
|
213
|
+
const iceServers = uniq(cluster.udp).map((url) => ({
|
|
148
214
|
username: '',
|
|
149
215
|
credential: '',
|
|
150
216
|
urls: [url],
|
|
@@ -205,7 +271,7 @@ export default class Reachability {
|
|
|
205
271
|
* @private
|
|
206
272
|
* @memberof Reachability
|
|
207
273
|
*/
|
|
208
|
-
private getLocalSDPForClusters(clusterList: object) {
|
|
274
|
+
private getLocalSDPForClusters(clusterList: object): Promise<InternalReachabilityResults> {
|
|
209
275
|
let clusters: any[] = [...Object.keys(clusterList)];
|
|
210
276
|
|
|
211
277
|
clusters = clusters.map(async (key) => {
|
|
@@ -218,18 +284,17 @@ export default class Reachability {
|
|
|
218
284
|
peerConnection.begin = Date.now();
|
|
219
285
|
peerConnection.setLocalDescription(description);
|
|
220
286
|
|
|
221
|
-
return this.iceGatheringState(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
287
|
+
return this.iceGatheringState(peerConnection, cluster.isVideoMesh).catch(
|
|
288
|
+
(iceGatheringStateError) => {
|
|
289
|
+
LoggerProxy.logger.log(
|
|
290
|
+
`Reachability:index#getLocalSDPForClusters --> Error in getLocalSDP : ${iceGatheringStateError}`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
);
|
|
229
294
|
});
|
|
230
295
|
|
|
231
296
|
return Promise.all(clusters)
|
|
232
|
-
.then(this.
|
|
297
|
+
.then(this.parseIceResultsToInternalReachabilityResults)
|
|
233
298
|
.then((reachabilityLatencyResults) => {
|
|
234
299
|
this.logUnreachableClusters();
|
|
235
300
|
|
|
@@ -318,12 +383,14 @@ export default class Reachability {
|
|
|
318
383
|
* speed.
|
|
319
384
|
* @private
|
|
320
385
|
* @param {RTCPeerConnection} peerConnection
|
|
321
|
-
* @param {
|
|
386
|
+
* @param {boolean} isVideoMesh
|
|
322
387
|
* @returns {Promise}
|
|
323
388
|
*/
|
|
324
|
-
private iceGatheringState(peerConnection: RTCPeerConnection,
|
|
389
|
+
private iceGatheringState(peerConnection: RTCPeerConnection, isVideoMesh: boolean) {
|
|
325
390
|
const ELAPSED = 'elapsed';
|
|
326
391
|
|
|
392
|
+
const timeout = isVideoMesh ? VIDEO_MESH_TIMEOUT : DEFAULT_TIMEOUT;
|
|
393
|
+
|
|
327
394
|
return new Promise<ICECandidateResult>((resolve) => {
|
|
328
395
|
const peerConnectionProxy = new window.Proxy(peerConnection, {
|
|
329
396
|
// eslint-disable-next-line require-jsdoc
|
|
@@ -339,8 +406,14 @@ export default class Reachability {
|
|
|
339
406
|
set: (target, property, value) => {
|
|
340
407
|
// only intercept elapsed property
|
|
341
408
|
if (property === ELAPSED) {
|
|
342
|
-
|
|
343
|
-
|
|
409
|
+
resolve({
|
|
410
|
+
// @ts-ignore
|
|
411
|
+
clusterId: peerConnection.key,
|
|
412
|
+
isVideoMesh,
|
|
413
|
+
// @ts-ignore
|
|
414
|
+
publicIPs: target.publicIPs,
|
|
415
|
+
elapsed: value,
|
|
416
|
+
});
|
|
344
417
|
|
|
345
418
|
return true;
|
|
346
419
|
}
|
|
@@ -392,10 +465,12 @@ export default class Reachability {
|
|
|
392
465
|
* @protected
|
|
393
466
|
* @memberof Reachability
|
|
394
467
|
*/
|
|
395
|
-
protected
|
|
468
|
+
protected parseIceResultsToInternalReachabilityResults(
|
|
469
|
+
iceResults: Array<ICECandidateResult>
|
|
470
|
+
): InternalReachabilityResults {
|
|
396
471
|
const reachabilityMap = {};
|
|
397
472
|
|
|
398
|
-
iceResults.forEach(({clusterId, elapsed, publicIPs}) => {
|
|
473
|
+
iceResults.forEach(({clusterId, isVideoMesh, elapsed, publicIPs}) => {
|
|
399
474
|
const latencyResult = {};
|
|
400
475
|
|
|
401
476
|
if (!elapsed) {
|
|
@@ -417,6 +492,7 @@ export default class Reachability {
|
|
|
417
492
|
udp: latencyResult,
|
|
418
493
|
tcp: {untested: 'true'},
|
|
419
494
|
xtls: {untested: 'true'},
|
|
495
|
+
isVideoMesh,
|
|
420
496
|
};
|
|
421
497
|
});
|
|
422
498
|
|
|
@@ -426,11 +502,11 @@ export default class Reachability {
|
|
|
426
502
|
/**
|
|
427
503
|
* fetches reachability data
|
|
428
504
|
* @param {object} clusterList
|
|
429
|
-
* @returns {Promise<
|
|
505
|
+
* @returns {Promise<InternalReachabilityResults>} reachability check results
|
|
430
506
|
* @private
|
|
431
507
|
* @memberof Reachability
|
|
432
508
|
*/
|
|
433
|
-
private performReachabilityCheck(clusterList: object) {
|
|
509
|
+
private performReachabilityCheck(clusterList: object): Promise<InternalReachabilityResults> {
|
|
434
510
|
if (!clusterList || !Object.keys(clusterList).length) {
|
|
435
511
|
return Promise.resolve({});
|
|
436
512
|
}
|
|
@@ -49,7 +49,7 @@ class ReachabilityRequest {
|
|
|
49
49
|
const {clusters, joinCookie} = res.body;
|
|
50
50
|
|
|
51
51
|
Object.keys(clusters).forEach((key) => {
|
|
52
|
-
clusters[key].isVideoMesh = res.body.clusterClasses?.hybridMedia?.includes(key);
|
|
52
|
+
clusters[key].isVideoMesh = !!res.body.clusterClasses?.hybridMedia?.includes(key);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
LoggerProxy.logger.log(
|
package/src/roap/request.ts
CHANGED
|
@@ -18,23 +18,10 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
18
18
|
let joinCookie;
|
|
19
19
|
|
|
20
20
|
// @ts-ignore
|
|
21
|
-
const
|
|
22
|
-
.get(REACHABILITY.namespace, REACHABILITY.localStorageResult)
|
|
23
|
-
.catch(() => {});
|
|
24
|
-
|
|
25
|
-
if (reachabilityData) {
|
|
26
|
-
try {
|
|
27
|
-
const reachabilityResult = JSON.parse(reachabilityData);
|
|
21
|
+
const reachabilityResult = await this.webex.meetings.reachability.getReachabilityResults();
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
localSdp.reachability = reachabilityResult;
|
|
32
|
-
}
|
|
33
|
-
} catch (e) {
|
|
34
|
-
LoggerProxy.logger.error(
|
|
35
|
-
`Roap:request#attachReachabilityData --> Error in parsing reachability data: ${e}`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
23
|
+
if (reachabilityResult && Object.keys(reachabilityResult).length) {
|
|
24
|
+
localSdp.reachability = reachabilityResult;
|
|
38
25
|
}
|
|
39
26
|
|
|
40
27
|
// @ts-ignore
|
|
@@ -225,10 +225,11 @@ export default class TurnDiscovery {
|
|
|
225
225
|
* @returns {Promise<string>} Promise with empty string if reachability is not skipped or a reason if it is skipped
|
|
226
226
|
*/
|
|
227
227
|
private async getSkipReason(meeting: Meeting): Promise<string> {
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
const isAnyPublicClusterReachable =
|
|
229
|
+
// @ts-ignore - fix type
|
|
230
|
+
await meeting.webex.meetings.reachability.isAnyPublicClusterReachable();
|
|
230
231
|
|
|
231
|
-
if (
|
|
232
|
+
if (isAnyPublicClusterReachable) {
|
|
232
233
|
LoggerProxy.logger.info(
|
|
233
234
|
'Roap:turnDiscovery#getSkipReason --> reachability has not failed, skipping TURN discovery'
|
|
234
235
|
);
|
|
@@ -1722,7 +1722,8 @@ describe('plugin-meetings', () => {
|
|
|
1722
1722
|
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
1723
1723
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
1724
1724
|
meeting.webex.meetings.reachability = {
|
|
1725
|
-
|
|
1725
|
+
isAnyPublicClusterReachable: sinon.stub().resolves(true),
|
|
1726
|
+
getReachabilityResults: sinon.stub().resolves(undefined),
|
|
1726
1727
|
};
|
|
1727
1728
|
meeting.roap.doTurnDiscovery = sinon
|
|
1728
1729
|
.stub()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {assert} from '@webex/test-helper-chai';
|
|
2
2
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
3
3
|
import sinon from 'sinon';
|
|
4
|
-
import Reachability, {ICECandidateResult} from '@webex/plugin-meetings/src/reachability/';
|
|
4
|
+
import Reachability, {ICECandidateResult, ReachabilityResults} from '@webex/plugin-meetings/src/reachability/';
|
|
5
5
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
6
6
|
|
|
7
7
|
import { IP_VERSION } from '@webex/plugin-meetings/src/constants';
|
|
8
8
|
|
|
9
|
-
describe('
|
|
9
|
+
describe('isAnyPublicClusterReachable', () => {
|
|
10
10
|
let webex;
|
|
11
11
|
|
|
12
12
|
beforeEach(() => {
|
|
@@ -29,7 +29,7 @@ describe('isAnyClusterReachable', () => {
|
|
|
29
29
|
}
|
|
30
30
|
const reachability = new Reachability(webex);
|
|
31
31
|
|
|
32
|
-
const result = await reachability.
|
|
32
|
+
const result = await reachability.isAnyPublicClusterReachable();
|
|
33
33
|
|
|
34
34
|
assert.equal(result, expectedValue);
|
|
35
35
|
};
|
|
@@ -57,6 +57,63 @@ describe('isAnyClusterReachable', () => {
|
|
|
57
57
|
it('returns false when reachability.result item is not there', async () => {
|
|
58
58
|
await checkIsClusterReachable(undefined, false);
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
describe('ignores video mesh reachability', () => {
|
|
62
|
+
it('returns false if there are no public cluster results, only video mesh', async () => {
|
|
63
|
+
await checkIsClusterReachable({
|
|
64
|
+
x: {
|
|
65
|
+
udp: {reachable: 'true'},
|
|
66
|
+
tcp: {reachable: 'true'},
|
|
67
|
+
isVideoMesh: true,
|
|
68
|
+
},
|
|
69
|
+
y: {
|
|
70
|
+
udp: {reachable: 'false'},
|
|
71
|
+
tcp: {reachable: 'true'},
|
|
72
|
+
isVideoMesh: true,
|
|
73
|
+
}
|
|
74
|
+
}, false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns false if there public cluster reachability failed, only video mesh succeeded', async () => {
|
|
78
|
+
await checkIsClusterReachable({
|
|
79
|
+
x: {
|
|
80
|
+
udp: {reachable: 'false'},
|
|
81
|
+
tcp: {reachable: 'true'},
|
|
82
|
+
isVideoMesh: true,
|
|
83
|
+
},
|
|
84
|
+
y: {
|
|
85
|
+
udp: {reachable: 'true'},
|
|
86
|
+
tcp: {reachable: 'false'},
|
|
87
|
+
isVideoMesh: true,
|
|
88
|
+
},
|
|
89
|
+
publicOne: {
|
|
90
|
+
udp: {reachable: 'false'},
|
|
91
|
+
tcp: {reachable: 'false'},
|
|
92
|
+
isVideoMesh: false,
|
|
93
|
+
}
|
|
94
|
+
}, false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('returns true if there is at least 1 public cluster result, while video mesh is not reachable', async () => {
|
|
98
|
+
await checkIsClusterReachable({
|
|
99
|
+
x: {
|
|
100
|
+
udp: {reachable: 'true'},
|
|
101
|
+
tcp: {reachable: 'true'},
|
|
102
|
+
isVideoMesh: true,
|
|
103
|
+
},
|
|
104
|
+
y: {
|
|
105
|
+
udp: {reachable: 'false'},
|
|
106
|
+
tcp: {reachable: 'true'},
|
|
107
|
+
isVideoMesh: true,
|
|
108
|
+
},
|
|
109
|
+
publicOne: {
|
|
110
|
+
udp: {reachable: 'false'},
|
|
111
|
+
tcp: {reachable: 'true'},
|
|
112
|
+
isVideoMesh: false,
|
|
113
|
+
}
|
|
114
|
+
}, true);
|
|
115
|
+
});
|
|
116
|
+
})
|
|
60
117
|
});
|
|
61
118
|
|
|
62
119
|
describe('gatherReachability', () => {
|
|
@@ -160,8 +217,8 @@ describe('gatherReachability', () => {
|
|
|
160
217
|
let testingClass: TestReachability;
|
|
161
218
|
|
|
162
219
|
class TestReachability extends Reachability {
|
|
163
|
-
public
|
|
164
|
-
return this.
|
|
220
|
+
public testParseIceResultsToInternalReachabilityResults(iceResults: Array<ICECandidateResult>) {
|
|
221
|
+
return this.parseIceResultsToInternalReachabilityResults(iceResults);
|
|
165
222
|
}
|
|
166
223
|
public testAddPublicIP(peerConnection: RTCPeerConnection, publicIP?: string | null) {
|
|
167
224
|
return this.addPublicIP(peerConnection, publicIP);
|
|
@@ -171,22 +228,25 @@ describe('gatherReachability', () => {
|
|
|
171
228
|
testingClass = new TestReachability({webex});
|
|
172
229
|
});
|
|
173
230
|
|
|
174
|
-
it('calls
|
|
175
|
-
const res = testingClass.
|
|
231
|
+
it('calls parseIceResultsToInternalReachabilityResults correctly', () => {
|
|
232
|
+
const res = testingClass.testParseIceResultsToInternalReachabilityResults([
|
|
176
233
|
{
|
|
177
234
|
clusterId: 'id1',
|
|
178
235
|
elapsed: '12312',
|
|
179
236
|
publicIPs: ['1.1.1.1'],
|
|
237
|
+
isVideoMesh: true,
|
|
180
238
|
},
|
|
181
239
|
{
|
|
182
240
|
clusterId: 'id2',
|
|
183
241
|
elapsed: null,
|
|
184
242
|
publicIPs: ['1.1.1.1'],
|
|
243
|
+
isVideoMesh: false,
|
|
185
244
|
},
|
|
186
245
|
{
|
|
187
246
|
clusterId: 'id2',
|
|
188
247
|
elapsed: '14123',
|
|
189
248
|
publicIPs: undefined,
|
|
249
|
+
isVideoMesh: false,
|
|
190
250
|
},
|
|
191
251
|
]);
|
|
192
252
|
|
|
@@ -203,6 +263,7 @@ describe('gatherReachability', () => {
|
|
|
203
263
|
latencyInMilliseconds: '12312',
|
|
204
264
|
reachable: 'true',
|
|
205
265
|
},
|
|
266
|
+
isVideoMesh: true,
|
|
206
267
|
},
|
|
207
268
|
id2: {
|
|
208
269
|
xtls: {
|
|
@@ -215,6 +276,7 @@ describe('gatherReachability', () => {
|
|
|
215
276
|
latencyInMilliseconds: '14123',
|
|
216
277
|
reachable: 'true',
|
|
217
278
|
},
|
|
279
|
+
isVideoMesh: false,
|
|
218
280
|
},
|
|
219
281
|
});
|
|
220
282
|
});
|
|
@@ -260,3 +322,97 @@ describe('gatherReachability', () => {
|
|
|
260
322
|
});
|
|
261
323
|
});
|
|
262
324
|
});
|
|
325
|
+
|
|
326
|
+
describe('getReachabilityResults', () => {
|
|
327
|
+
let webex;
|
|
328
|
+
|
|
329
|
+
beforeEach(() => {
|
|
330
|
+
webex = new MockWebex();
|
|
331
|
+
|
|
332
|
+
// sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
afterEach(() => {
|
|
336
|
+
sinon.restore();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const runCheck = async (mockStorage: any, expectedResult: ReachabilityResults) => {
|
|
340
|
+
if (mockStorage) {
|
|
341
|
+
await webex.boundedStorage.put(
|
|
342
|
+
'Reachability',
|
|
343
|
+
'reachability.result',
|
|
344
|
+
JSON.stringify(mockStorage)
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const reachability = new Reachability(webex);
|
|
348
|
+
|
|
349
|
+
const result = await reachability.getReachabilityResults();
|
|
350
|
+
|
|
351
|
+
assert.deepEqual(result, expectedResult);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
it('returns undefined if reading from local storage fails', async () => {
|
|
355
|
+
sinon.stub(webex.boundedStorage, 'get').rejects(new Error('fake error'));
|
|
356
|
+
|
|
357
|
+
const reachability = new Reachability(webex);
|
|
358
|
+
|
|
359
|
+
const result = await reachability.getReachabilityResults();
|
|
360
|
+
|
|
361
|
+
assert.isUndefined(result);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('returns results from local storage, stripping any internal data', async () => {
|
|
365
|
+
await runCheck(
|
|
366
|
+
// mock storage:
|
|
367
|
+
{
|
|
368
|
+
cluster1: {
|
|
369
|
+
udp: {reachable: 'true', latencyInMilliseconds: '100'},
|
|
370
|
+
tcp: {reachable: 'false'},
|
|
371
|
+
xtls: {untested: 'true'},
|
|
372
|
+
},
|
|
373
|
+
cluster2: {
|
|
374
|
+
udp: {reachable: 'true', latencyInMilliseconds: '200'},
|
|
375
|
+
tcp: {reachable: 'false'},
|
|
376
|
+
xtls: {untested: 'true'},
|
|
377
|
+
isVideoMesh: true,
|
|
378
|
+
},
|
|
379
|
+
cluster3: {
|
|
380
|
+
udp: {reachable: 'false'},
|
|
381
|
+
tcp: {reachable: 'true', latencyInMilliseconds: '100', clientMediaIPs: ['10.10.10.10']},
|
|
382
|
+
xtls: {untested: 'true'},
|
|
383
|
+
isVideoMesh: true,
|
|
384
|
+
someOtherField: 'any value',
|
|
385
|
+
},
|
|
386
|
+
cluster4: {
|
|
387
|
+
udp: {reachable: 'false', latencyInMilliseconds: '300'},
|
|
388
|
+
tcp: {reachable: 'false', untested: 'true'},
|
|
389
|
+
xtls: {untested: 'true'},
|
|
390
|
+
someOtherField: 'any value',
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
// expected result (same as above, but with isVideoMesh and someOtherField stripped out):
|
|
394
|
+
{
|
|
395
|
+
cluster1: {
|
|
396
|
+
udp: {reachable: 'true', latencyInMilliseconds: '100'},
|
|
397
|
+
tcp: {reachable: 'false'},
|
|
398
|
+
xtls: {untested: 'true'},
|
|
399
|
+
},
|
|
400
|
+
cluster2: {
|
|
401
|
+
udp: {reachable: 'true', latencyInMilliseconds: '200'},
|
|
402
|
+
tcp: {reachable: 'false'},
|
|
403
|
+
xtls: {untested: 'true'},
|
|
404
|
+
},
|
|
405
|
+
cluster3: {
|
|
406
|
+
udp: {reachable: 'false'},
|
|
407
|
+
tcp: {reachable: 'true', latencyInMilliseconds: '100', clientMediaIPs: ['10.10.10.10']},
|
|
408
|
+
xtls: {untested: 'true'},
|
|
409
|
+
},
|
|
410
|
+
cluster4: {
|
|
411
|
+
udp: {reachable: 'false', latencyInMilliseconds: '300'},
|
|
412
|
+
tcp: {reachable: 'false', untested: 'true'},
|
|
413
|
+
xtls: {untested: 'true'},
|
|
414
|
+
},
|
|
415
|
+
}
|
|
416
|
+
);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
@@ -64,7 +64,7 @@ describe('Roap', () => {
|
|
|
64
64
|
setRoapSeq: sinon.stub(),
|
|
65
65
|
config: {experimental: {enableTurnDiscovery: false}},
|
|
66
66
|
locusMediaRequest: {fake: true},
|
|
67
|
-
webex: { meetings: { reachability: {
|
|
67
|
+
webex: { meetings: { reachability: { isAnyPublicClusterReachable: () => true}}},
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
|
|
@@ -54,7 +54,7 @@ describe('TurnDiscovery', () => {
|
|
|
54
54
|
}),
|
|
55
55
|
updateMediaConnections: sinon.stub(),
|
|
56
56
|
webex: {meetings: {reachability: {
|
|
57
|
-
|
|
57
|
+
isAnyPublicClusterReachable: () => Promise.resolve(false),
|
|
58
58
|
}}},
|
|
59
59
|
isMultistream: false,
|
|
60
60
|
locusMediaRequest: { fake: true },
|
|
@@ -257,8 +257,8 @@ describe('TurnDiscovery', () => {
|
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
it('resolves with undefined when cluster is reachable', async () => {
|
|
260
|
-
const prev = testMeeting.webex.meetings.reachability.
|
|
261
|
-
testMeeting.webex.meetings.reachability.
|
|
260
|
+
const prev = testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable;
|
|
261
|
+
testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable = () => Promise.resolve(true);
|
|
262
262
|
const result = await new TurnDiscovery(mockRoapRequest).doTurnDiscovery(testMeeting);
|
|
263
263
|
|
|
264
264
|
const {turnServerInfo, turnDiscoverySkippedReason} = result;
|
|
@@ -267,7 +267,7 @@ describe('TurnDiscovery', () => {
|
|
|
267
267
|
assert.equal(turnDiscoverySkippedReason, 'reachability');
|
|
268
268
|
assert.notCalled(mockRoapRequest.sendRoap);
|
|
269
269
|
assert.notCalled(Metrics.sendBehavioralMetric);
|
|
270
|
-
testMeeting.webex.meetings.reachability.
|
|
270
|
+
testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable = prev;
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
it("resolves with undefined if we don't get a response within 10s", async () => {
|
|
@@ -379,15 +379,15 @@ describe('TurnDiscovery', () => {
|
|
|
379
379
|
|
|
380
380
|
describe('isSkipped', () => {
|
|
381
381
|
[
|
|
382
|
-
{enabledInConfig: true,
|
|
383
|
-
{enabledInConfig: true,
|
|
384
|
-
{enabledInConfig: false,
|
|
385
|
-
{enabledInConfig: false,
|
|
386
|
-
].forEach(({enabledInConfig,
|
|
387
|
-
it(`returns ${expectedIsSkipped} when TURN discovery is ${enabledInConfig ? '' : 'not '} enabled in config and
|
|
382
|
+
{enabledInConfig: true, isAnyPublicClusterReachable: true, expectedIsSkipped: true},
|
|
383
|
+
{enabledInConfig: true, isAnyPublicClusterReachable: false, expectedIsSkipped: false},
|
|
384
|
+
{enabledInConfig: false, isAnyPublicClusterReachable: true, expectedIsSkipped: true},
|
|
385
|
+
{enabledInConfig: false, isAnyPublicClusterReachable: false, expectedIsSkipped: true},
|
|
386
|
+
].forEach(({enabledInConfig, isAnyPublicClusterReachable, expectedIsSkipped}) => {
|
|
387
|
+
it(`returns ${expectedIsSkipped} when TURN discovery is ${enabledInConfig ? '' : 'not '} enabled in config and isAnyPublicClusterReachable() returns ${isAnyPublicClusterReachable ? 'true' : 'false'}`, async () => {
|
|
388
388
|
testMeeting.config.experimental.enableTurnDiscovery = enabledInConfig;
|
|
389
389
|
|
|
390
|
-
sinon.stub(testMeeting.webex.meetings.reachability, '
|
|
390
|
+
sinon.stub(testMeeting.webex.meetings.reachability, 'isAnyPublicClusterReachable').resolves(isAnyPublicClusterReachable);
|
|
391
391
|
|
|
392
392
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
393
393
|
|