@webex/plugin-meetings 2.60.0-next.9 → 2.60.1-next.1

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.
Files changed (55) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.d.ts +1 -0
  4. package/dist/config.js +2 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/index.js +5 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/interceptors/index.d.ts +2 -0
  9. package/dist/interceptors/index.js +15 -0
  10. package/dist/interceptors/index.js.map +1 -0
  11. package/dist/interceptors/locusRetry.d.ts +27 -0
  12. package/dist/interceptors/locusRetry.js +94 -0
  13. package/dist/interceptors/locusRetry.js.map +1 -0
  14. package/dist/interpretation/index.js +1 -1
  15. package/dist/interpretation/siLanguage.js +1 -1
  16. package/dist/meeting/index.js +6 -2
  17. package/dist/meeting/index.js.map +1 -1
  18. package/dist/meetings/index.d.ts +1 -11
  19. package/dist/meetings/index.js +4 -3
  20. package/dist/meetings/index.js.map +1 -1
  21. package/dist/reachability/clusterReachability.d.ts +109 -0
  22. package/dist/reachability/clusterReachability.js +357 -0
  23. package/dist/reachability/clusterReachability.js.map +1 -0
  24. package/dist/reachability/index.d.ts +32 -121
  25. package/dist/reachability/index.js +173 -459
  26. package/dist/reachability/index.js.map +1 -1
  27. package/dist/reachability/util.d.ts +8 -0
  28. package/dist/reachability/util.js +29 -0
  29. package/dist/reachability/util.js.map +1 -0
  30. package/dist/statsAnalyzer/index.d.ts +22 -0
  31. package/dist/statsAnalyzer/index.js +60 -0
  32. package/dist/statsAnalyzer/index.js.map +1 -1
  33. package/dist/statsAnalyzer/mqaUtil.js +4 -0
  34. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  35. package/dist/webinar/index.js +1 -1
  36. package/package.json +25 -22
  37. package/src/config.ts +1 -0
  38. package/src/index.ts +4 -0
  39. package/src/interceptors/index.ts +3 -0
  40. package/src/interceptors/locusRetry.ts +67 -0
  41. package/src/meeting/index.ts +10 -2
  42. package/src/meetings/index.ts +4 -3
  43. package/src/reachability/clusterReachability.ts +320 -0
  44. package/src/reachability/index.ts +124 -421
  45. package/src/reachability/util.ts +24 -0
  46. package/src/statsAnalyzer/index.ts +64 -1
  47. package/src/statsAnalyzer/mqaUtil.ts +4 -0
  48. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  49. package/test/unit/spec/meeting/index.js +8 -1
  50. package/test/unit/spec/meetings/index.js +28 -25
  51. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  52. package/test/unit/spec/reachability/index.ts +159 -226
  53. package/test/unit/spec/reachability/util.ts +40 -0
  54. package/test/unit/spec/roap/request.ts +26 -3
  55. package/test/unit/spec/stats-analyzer/index.js +100 -20
@@ -3,18 +3,19 @@
3
3
  */
4
4
 
5
5
  /* eslint-disable class-methods-use-this */
6
- /* globals window */
7
- import {uniq, mapValues, pick} from 'lodash';
6
+ import {mapValues} from 'lodash';
8
7
 
9
8
  import LoggerProxy from '../common/logs/logger-proxy';
10
9
  import MeetingUtil from '../meeting/util';
11
10
 
12
- import {ICE_GATHERING_STATE, CONNECTION_STATE, REACHABILITY} from '../constants';
11
+ import {REACHABILITY} from '../constants';
13
12
 
14
- import ReachabilityRequest from './request';
15
-
16
- const DEFAULT_TIMEOUT = 3000;
17
- const VIDEO_MESH_TIMEOUT = 1000;
13
+ import ReachabilityRequest, {ClusterList} from './request';
14
+ import {
15
+ ClusterReachability,
16
+ ClusterReachabilityResult,
17
+ TransportResult,
18
+ } from './clusterReachability';
18
19
 
19
20
  export type ReachabilityMetrics = {
20
21
  reachability_public_udp_success: number;
@@ -27,39 +28,34 @@ export type ReachabilityMetrics = {
27
28
  reachability_vmn_tcp_failed: number;
28
29
  };
29
30
 
30
- // result for a specific transport protocol (like udp or tcp)
31
- export type TransportResult = {
31
+ /**
32
+ * This is the type that matches what backend expects us to send to them. It is a bit weird, because
33
+ * it uses strings instead of booleans and numbers, but that's what they require.
34
+ */
35
+ export type TransportResultForBackend = {
32
36
  reachable?: 'true' | 'false';
33
37
  latencyInMilliseconds?: string;
34
38
  clientMediaIPs?: string[];
35
39
  untested?: 'true';
36
40
  };
37
41
 
38
- // reachability result for a specifc media cluster
39
- type ReachabilityResult = {
40
- udp: TransportResult;
41
- tcp: TransportResult;
42
- xtls: {
43
- untested: 'true';
44
- };
42
+ export type ReachabilityResultForBackend = {
43
+ udp: TransportResultForBackend;
44
+ tcp: TransportResultForBackend;
45
+ xtls: TransportResultForBackend;
45
46
  };
47
+
46
48
  // this is the type that is required by the backend when we send them reachability results
47
- export type ReachabilityResults = Record<string, ReachabilityResult>;
49
+ export type ReachabilityResultsForBackend = Record<string, ReachabilityResultForBackend>;
48
50
 
49
51
  // this is the type used by Reachability class internally and stored in local storage
50
- type InternalReachabilityResults = Record<
52
+ export type ReachabilityResults = Record<
51
53
  string,
52
- ReachabilityResult & {
54
+ ClusterReachabilityResult & {
53
55
  isVideoMesh?: boolean;
54
56
  }
55
57
  >;
56
58
 
57
- export type ICECandidateResult = {
58
- clusterId: string;
59
- isVideoMesh: boolean;
60
- elapsed?: string | null;
61
- publicIPs?: string[];
62
- };
63
59
  /**
64
60
  * @class Reachability
65
61
  * @export
@@ -67,8 +63,10 @@ export type ICECandidateResult = {
67
63
  export default class Reachability {
68
64
  namespace = REACHABILITY.namespace;
69
65
  webex: object;
70
- reachabilityRequest: any;
71
- clusterLatencyResults: any;
66
+ reachabilityRequest: ReachabilityRequest;
67
+ clusterReachability: {
68
+ [key: string]: ClusterReachability;
69
+ };
72
70
 
73
71
  /**
74
72
  * Creates an instance of Reachability.
@@ -87,26 +85,16 @@ export default class Reachability {
87
85
  */
88
86
  this.reachabilityRequest = new ReachabilityRequest(this.webex);
89
87
 
90
- /**
91
- * internal object of clusters latency results
92
- * @instance
93
- * @type {object}
94
- * @private
95
- * @memberof Reachability
96
- */
97
- this.clusterLatencyResults = {};
88
+ this.clusterReachability = {};
98
89
  }
99
90
 
100
91
  /**
101
- * fetches reachability data
102
- * @returns {Object} reachability data
92
+ * Gets a list of media clusters from the backend and performs reachability checks on all the clusters
93
+ * @returns {Promise<ReachabilityResults>} reachability results
103
94
  * @public
104
- * @async
105
95
  * @memberof Reachability
106
96
  */
107
- public async gatherReachability(): Promise<InternalReachabilityResults> {
108
- this.setup();
109
-
97
+ public async gatherReachability(): Promise<ReachabilityResults> {
110
98
  // Remove stored reachability results to ensure no stale data
111
99
  // @ts-ignore
112
100
  await this.webex.boundedStorage.del(this.namespace, REACHABILITY.localStorageResult);
@@ -120,7 +108,7 @@ export default class Reachability {
120
108
  );
121
109
 
122
110
  // Perform Reachability Check
123
- const results = await this.performReachabilityCheck(clusters);
111
+ const results = await this.performReachabilityChecks(clusters);
124
112
 
125
113
  // @ts-ignore
126
114
  await this.webex.boundedStorage.put(
@@ -140,10 +128,8 @@ export default class Reachability {
140
128
  );
141
129
 
142
130
  return results;
143
- } catch (getClusterError) {
144
- LoggerProxy.logger.error(
145
- `Reachability:index#gatherReachability --> Error in calling getClusters(): ${getClusterError}`
146
- );
131
+ } catch (error) {
132
+ LoggerProxy.logger.error(`Reachability:index#gatherReachability --> Error:`, error);
147
133
 
148
134
  return {};
149
135
  }
@@ -167,13 +153,13 @@ export default class Reachability {
167
153
  reachability_vmn_tcp_failed: 0,
168
154
  };
169
155
 
170
- const updateStats = (clusterType: 'public' | 'vmn', result: ReachabilityResult) => {
171
- if (result.udp?.reachable) {
172
- const outcome = result.udp.reachable === 'true' ? 'success' : 'failed';
156
+ const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
157
+ if (result.udp && result.udp.result !== 'untested') {
158
+ const outcome = result.udp.result === 'reachable' ? 'success' : 'failed';
173
159
  stats[`reachability_${clusterType}_udp_${outcome}`] += 1;
174
160
  }
175
- if (result.tcp?.reachable) {
176
- const outcome = result.tcp.reachable === 'true' ? 'success' : 'failed';
161
+ if (result.tcp && result.tcp.result !== 'untested') {
162
+ const outcome = result.tcp.result === 'reachable' ? 'success' : 'failed';
177
163
  stats[`reachability_${clusterType}_tcp_${outcome}`] += 1;
178
164
  }
179
165
  };
@@ -185,9 +171,9 @@ export default class Reachability {
185
171
  REACHABILITY.localStorageResult
186
172
  );
187
173
 
188
- const internalResults: InternalReachabilityResults = JSON.parse(resultsJson);
174
+ const results: ReachabilityResults = JSON.parse(resultsJson);
189
175
 
190
- Object.values(internalResults).forEach((result) => {
176
+ Object.values(results).forEach((result) => {
191
177
  updateStats(result.isVideoMesh ? 'vmn' : 'public', result);
192
178
  });
193
179
  } catch (e) {
@@ -201,16 +187,49 @@ export default class Reachability {
201
187
  return stats;
202
188
  }
203
189
 
190
+ /**
191
+ * Maps our internal transport result to the format that backend expects
192
+ * @param {TransportResult} transportResult
193
+ * @returns {TransportResultForBackend}
194
+ */
195
+ private mapTransportResultToBackendDataFormat(
196
+ transportResult: TransportResult
197
+ ): TransportResultForBackend {
198
+ const output: TransportResultForBackend = {};
199
+
200
+ for (const [key, value] of Object.entries(transportResult)) {
201
+ switch (key) {
202
+ case 'result':
203
+ switch (value) {
204
+ case 'reachable':
205
+ output.reachable = 'true';
206
+ break;
207
+ case 'unreachable':
208
+ output.reachable = 'false';
209
+ break;
210
+ case 'untested':
211
+ output.untested = 'true';
212
+ break;
213
+ }
214
+ break;
215
+ case 'latencyInMilliseconds':
216
+ output.latencyInMilliseconds = value.toString();
217
+ break;
218
+ default:
219
+ output[key] = value;
220
+ }
221
+ }
222
+
223
+ return output;
224
+ }
225
+
204
226
  /**
205
227
  * Reachability results as an object in the format that backend expects
206
228
  *
207
229
  * @returns {any} reachability results that need to be sent to the backend
208
230
  */
209
- async getReachabilityResults(): Promise<ReachabilityResults | undefined> {
210
- let results: ReachabilityResults;
211
-
212
- // these are the only props that backend needs in the reachability results:
213
- const reachabilityResultsProps: Array<keyof ReachabilityResult> = ['udp', 'tcp', 'xtls'];
231
+ async getReachabilityResults(): Promise<ReachabilityResultsForBackend | undefined> {
232
+ let results: ReachabilityResultsForBackend;
214
233
 
215
234
  try {
216
235
  // @ts-ignore
@@ -219,9 +238,15 @@ export default class Reachability {
219
238
  REACHABILITY.localStorageResult
220
239
  );
221
240
 
222
- const internalResults: InternalReachabilityResults = JSON.parse(resultsJson);
241
+ const allClusterResults: ReachabilityResults = JSON.parse(resultsJson);
223
242
 
224
- results = mapValues(internalResults, (result) => pick(result, reachabilityResultsProps));
243
+ results = mapValues(allClusterResults, (clusterResult) => ({
244
+ udp: this.mapTransportResultToBackendDataFormat(clusterResult.udp || {result: 'untested'}),
245
+ tcp: this.mapTransportResultToBackendDataFormat(clusterResult.tcp || {result: 'untested'}),
246
+ xtls: this.mapTransportResultToBackendDataFormat(
247
+ clusterResult.xtls || {result: 'untested'}
248
+ ),
249
+ }));
225
250
  } catch (e) {
226
251
  // empty storage, that's ok
227
252
  LoggerProxy.logger.warn(
@@ -248,12 +273,12 @@ export default class Reachability {
248
273
 
249
274
  if (reachabilityData) {
250
275
  try {
251
- const reachabilityResults: InternalReachabilityResults = JSON.parse(reachabilityData);
276
+ const reachabilityResults: ReachabilityResults = JSON.parse(reachabilityData);
252
277
 
253
278
  reachable = Object.values(reachabilityResults).some(
254
279
  (result) =>
255
280
  !result.isVideoMesh &&
256
- (result.udp?.reachable === 'true' || result.tcp?.reachable === 'true')
281
+ (result.udp?.result === 'reachable' || result.tcp?.result === 'reachable')
257
282
  );
258
283
  } catch (e) {
259
284
  LoggerProxy.logger.error(
@@ -265,246 +290,29 @@ export default class Reachability {
265
290
  return reachable;
266
291
  }
267
292
 
268
- /**
269
- * Generate peerConnection config settings
270
- * @param {object} cluster
271
- * @returns {object} peerConnectionConfig
272
- * @private
273
- * @memberof Reachability
274
- */
275
- private buildPeerConnectionConfig(cluster: any) {
276
- const iceServers = uniq(cluster.udp).map((url) => ({
277
- username: '',
278
- credential: '',
279
- urls: [url],
280
- }));
281
-
282
- return {
283
- iceServers: [...iceServers],
284
- iceCandidatePoolSize: '0',
285
- iceTransportPolicy: 'all',
286
- };
287
- }
288
-
289
- /**
290
- * Creates an RTCPeerConnection
291
- * @param {object} cluster
292
- * @returns {RTCPeerConnection} peerConnection
293
- * @private
294
- * @memberof Reachability
295
- */
296
- private createPeerConnection(cluster: any) {
297
- const {key, config} = cluster;
298
-
299
- try {
300
- const peerConnection = new window.RTCPeerConnection(config);
301
-
302
- // @ts-ignore
303
- peerConnection.key = key;
304
-
305
- return peerConnection;
306
- } catch (peerConnectionError) {
307
- LoggerProxy.logger.log(
308
- `Reachability:index#createPeerConnection --> Error creating peerConnection: ${peerConnectionError}`
309
- );
310
-
311
- return null;
312
- }
313
- }
314
-
315
- /**
316
- * Gets total elapsed time
317
- * @param {RTCPeerConnection} peerConnection
318
- * @returns {Number} Milliseconds
319
- * @private
320
- * @memberof Reachability
321
- */
322
- private getElapsedTime(peerConnection: any) {
323
- const startTime = peerConnection.begin;
324
-
325
- delete peerConnection.begin;
326
-
327
- return Date.now() - startTime;
328
- }
329
-
330
- /**
331
- * creates offer and generates localSDP
332
- * @param {object} clusterList cluster List
333
- * @returns {Promise} Reachability latency results
334
- * @private
335
- * @memberof Reachability
336
- */
337
- private getLocalSDPForClusters(clusterList: object): Promise<InternalReachabilityResults> {
338
- let clusters: any[] = [...Object.keys(clusterList)];
339
-
340
- clusters = clusters.map(async (key) => {
341
- const cluster = clusterList[key];
342
- const config = this.buildPeerConnectionConfig(cluster);
343
- const peerConnection = this.createPeerConnection({key, config});
344
- const description = await peerConnection.createOffer({offerToReceiveAudio: true});
345
-
346
- // @ts-ignore
347
- peerConnection.begin = Date.now();
348
- peerConnection.setLocalDescription(description);
349
-
350
- return this.iceGatheringState(peerConnection, cluster.isVideoMesh).catch(
351
- (iceGatheringStateError) => {
352
- LoggerProxy.logger.log(
353
- `Reachability:index#getLocalSDPForClusters --> Error in getLocalSDP : ${iceGatheringStateError}`
354
- );
355
- }
356
- );
357
- });
358
-
359
- return Promise.all(clusters)
360
- .then(this.parseIceResultsToInternalReachabilityResults)
361
- .then((reachabilityLatencyResults) => {
362
- this.logUnreachableClusters();
363
-
364
- // return results
365
- return reachabilityLatencyResults;
366
- });
367
- }
368
-
369
293
  /**
370
294
  * Get list of all unreachable clusters
371
295
  * @returns {array} Unreachable clusters
372
296
  * @private
373
297
  * @memberof Reachability
374
298
  */
375
- private getUnreachablClusters() {
299
+ private getUnreachableClusters(): Array<{name: string; protocol: string}> {
376
300
  const unreachableList = [];
377
- const clusters = this.clusterLatencyResults;
378
301
 
379
- Object.keys(clusters).forEach((key) => {
380
- const cluster = clusters[key];
302
+ Object.entries(this.clusterReachability).forEach(([key, clusterReachability]) => {
303
+ const result = clusterReachability.getResult();
381
304
 
382
- if (cluster.unreachable && !cluster.reachable) {
383
- unreachableList.push(key);
305
+ if (result.udp.result === 'unreachable') {
306
+ unreachableList.push({name: key, protocol: 'udp'});
307
+ }
308
+ if (result.tcp.result === 'unreachable') {
309
+ unreachableList.push({name: key, protocol: 'tcp'});
384
310
  }
385
311
  });
386
312
 
387
313
  return unreachableList;
388
314
  }
389
315
 
390
- /**
391
- * Attach an event handler for the icegatheringstatechange
392
- * event and measure latency.
393
- * @param {RTCPeerConnection} peerConnection
394
- * @returns {undefined}
395
- * @private
396
- * @memberof Reachability
397
- */
398
- private handleIceGatheringStateChange(peerConnection: RTCPeerConnection) {
399
- peerConnection.onicegatheringstatechange = () => {
400
- const {COMPLETE} = ICE_GATHERING_STATE;
401
-
402
- if (peerConnection.iceConnectionState === COMPLETE) {
403
- const elapsed = this.getElapsedTime(peerConnection);
404
-
405
- // @ts-ignore
406
- LoggerProxy.logger.log(
407
- // @ts-ignore
408
- `Reachability:index#onIceGatheringStateChange --> Successfully pinged ${peerConnection.key}:`,
409
- elapsed
410
- );
411
- this.setLatencyAndClose(peerConnection, elapsed);
412
- }
413
- };
414
- }
415
-
416
- /**
417
- * Attach an event handler for the icecandidate
418
- * event and measure latency.
419
- * @param {RTCPeerConnection} peerConnection
420
- * @returns {undefined}
421
- * @private
422
- * @memberof Reachability
423
- */
424
- private handleOnIceCandidate(peerConnection: RTCPeerConnection) {
425
- peerConnection.onicecandidate = (e) => {
426
- const SERVER_REFLEXIVE = 'srflx';
427
-
428
- if (e.candidate && String(e.candidate.type).toLowerCase() === SERVER_REFLEXIVE) {
429
- const elapsed = this.getElapsedTime(peerConnection);
430
-
431
- LoggerProxy.logger.log(
432
- // @ts-ignore
433
- `Reachability:index#onIceCandidate --> Successfully pinged ${peerConnection.key}:`,
434
- elapsed
435
- );
436
- // order is important
437
- this.addPublicIP(peerConnection, e.candidate.address);
438
- this.setLatencyAndClose(peerConnection, elapsed);
439
- }
440
- };
441
- }
442
-
443
- /**
444
- * An event handler on an RTCPeerConnection when the state of the ICE
445
- * candidate gathering process changes. Used to measure connection
446
- * speed.
447
- * @private
448
- * @param {RTCPeerConnection} peerConnection
449
- * @param {boolean} isVideoMesh
450
- * @returns {Promise}
451
- */
452
- private iceGatheringState(peerConnection: RTCPeerConnection, isVideoMesh: boolean) {
453
- const ELAPSED = 'elapsed';
454
-
455
- const timeout = isVideoMesh ? VIDEO_MESH_TIMEOUT : DEFAULT_TIMEOUT;
456
-
457
- return new Promise<ICECandidateResult>((resolve) => {
458
- const peerConnectionProxy = new window.Proxy(peerConnection, {
459
- // eslint-disable-next-line require-jsdoc
460
- get(target, property) {
461
- const targetMember = target[property];
462
-
463
- if (typeof targetMember === 'function') {
464
- return targetMember.bind(target);
465
- }
466
-
467
- return targetMember;
468
- },
469
- set: (target, property, value) => {
470
- // only intercept elapsed property
471
- if (property === ELAPSED) {
472
- resolve({
473
- // @ts-ignore
474
- clusterId: peerConnection.key,
475
- isVideoMesh,
476
- // @ts-ignore
477
- publicIPs: target.publicIPs,
478
- elapsed: value,
479
- });
480
-
481
- return true;
482
- }
483
-
484
- // pass thru
485
- return window.Reflect.set(target, property, value);
486
- },
487
- });
488
-
489
- // Using peerConnection proxy so handle functions below
490
- // won't be coupled to our promise implementation
491
- this.handleIceGatheringStateChange(peerConnectionProxy);
492
- this.handleOnIceCandidate(peerConnectionProxy);
493
-
494
- // Set maximum timeout
495
- window.setTimeout(() => {
496
- const {CLOSED} = CONNECTION_STATE;
497
-
498
- // Close any open peerConnections
499
- if (peerConnectionProxy.connectionState !== CLOSED) {
500
- // order is important
501
- this.addPublicIP(peerConnectionProxy, null);
502
- this.setLatencyAndClose(peerConnectionProxy, null);
503
- }
504
- }, timeout);
505
- });
506
- }
507
-
508
316
  /**
509
317
  * Make a log of unreachable clusters.
510
318
  * @returns {undefined}
@@ -512,160 +320,55 @@ export default class Reachability {
512
320
  * @memberof Reachability
513
321
  */
514
322
  private logUnreachableClusters() {
515
- const list = this.getUnreachablClusters();
323
+ const list = this.getUnreachableClusters();
516
324
 
517
- list.forEach((cluster) => {
325
+ list.forEach(({name, protocol}) => {
518
326
  LoggerProxy.logger.log(
519
- `Reachability:index#logUnreachableClusters --> No ice candidate for ${cluster}.`
327
+ `Reachability:index#logUnreachableClusters --> failed to reach ${name} over ${protocol}`
520
328
  );
521
329
  });
522
330
  }
523
331
 
524
332
  /**
525
- * Calculates time to establish connection
526
- * @param {Array<ICECandidateResult>} iceResults iceResults
527
- * @returns {object} reachabilityMap
528
- * @protected
529
- * @memberof Reachability
333
+ * Performs reachability checks for all clusters
334
+ * @param {ClusterList} clusterList
335
+ * @returns {Promise<ReachabilityResults>} reachability check results
530
336
  */
531
- protected parseIceResultsToInternalReachabilityResults(
532
- iceResults: Array<ICECandidateResult>
533
- ): InternalReachabilityResults {
534
- const reachabilityMap = {};
535
-
536
- iceResults.forEach(({clusterId, isVideoMesh, elapsed, publicIPs}) => {
537
- const latencyResult = {};
538
-
539
- if (!elapsed) {
540
- Object.assign(latencyResult, {reachable: 'false'});
541
- } else {
542
- Object.assign(latencyResult, {
543
- reachable: 'true',
544
- latencyInMilliseconds: elapsed.toString(),
545
- });
546
- }
337
+ private async performReachabilityChecks(clusterList: ClusterList): Promise<ReachabilityResults> {
338
+ const results: ReachabilityResults = {};
547
339
 
548
- if (publicIPs) {
549
- Object.assign(latencyResult, {
550
- clientMediaIPs: publicIPs,
551
- });
552
- }
553
-
554
- reachabilityMap[clusterId] = {
555
- udp: latencyResult,
556
- tcp: {untested: 'true'},
557
- xtls: {untested: 'true'},
558
- isVideoMesh,
559
- };
560
- });
561
-
562
- return reachabilityMap;
563
- }
564
-
565
- /**
566
- * fetches reachability data
567
- * @param {object} clusterList
568
- * @returns {Promise<InternalReachabilityResults>} reachability check results
569
- * @private
570
- * @memberof Reachability
571
- */
572
- private performReachabilityCheck(clusterList: object): Promise<InternalReachabilityResults> {
573
340
  if (!clusterList || !Object.keys(clusterList).length) {
574
- return Promise.resolve({});
341
+ return Promise.resolve(results);
575
342
  }
576
343
 
577
- return new Promise((resolve) => {
578
- this.getLocalSDPForClusters(clusterList)
579
- .then((localSDPData) => {
580
- if (!localSDPData || !Object.keys(localSDPData).length) {
581
- // TODO: handle the error condition properly and try retry
582
- LoggerProxy.logger.log(
583
- 'Reachability:index#performReachabilityCheck --> Local SDP is empty or has missing elements..returning'
584
- );
585
- resolve({});
586
- } else {
587
- resolve(localSDPData);
588
- }
589
- })
590
- .catch((error) => {
591
- LoggerProxy.logger.error(
592
- `Reachability:index#performReachabilityCheck --> Error in getLocalSDPForClusters: ${error}`
593
- );
594
- resolve({});
595
- });
596
- });
597
- }
344
+ // @ts-ignore
345
+ const includeTcpReachability = this.webex.config.meetings.experimental.enableTcpReachability;
598
346
 
599
- /**
600
- * Adds public IP (client media IPs)
601
- * @param {RTCPeerConnection} peerConnection
602
- * @param {string} publicIP
603
- * @returns {void}
604
- */
605
- protected addPublicIP(peerConnection: RTCPeerConnection, publicIP?: string | null) {
606
- const modifiedPeerConnection: RTCPeerConnection & {publicIPs?: string[]} = peerConnection;
607
- const {CLOSED} = CONNECTION_STATE;
347
+ LoggerProxy.logger.log(
348
+ `Reachability:index#performReachabilityChecks --> doing UDP${
349
+ includeTcpReachability ? ' and TCP' : ''
350
+ } reachability checks`
351
+ );
608
352
 
609
- if (modifiedPeerConnection.connectionState === CLOSED) {
610
- LoggerProxy.logger.log(
611
- `Reachability:index#addPublicIP --> Attempting to set publicIP of ${publicIP} on closed peerConnection.`
612
- );
613
- }
353
+ const clusterReachabilityChecks = Object.keys(clusterList).map((key) => {
354
+ const cluster = clusterList[key];
614
355
 
615
- if (publicIP) {
616
- if (modifiedPeerConnection.publicIPs) {
617
- modifiedPeerConnection.publicIPs.push(publicIP);
618
- } else {
619
- modifiedPeerConnection.publicIPs = [publicIP];
356
+ if (!includeTcpReachability) {
357
+ cluster.tcp = [];
620
358
  }
621
- } else {
622
- modifiedPeerConnection.publicIPs = null;
623
- }
624
- }
625
359
 
626
- /**
627
- * Records latency and closes the peerConnection
628
- * @param {RTCPeerConnection} peerConnection
629
- * @param {number} elapsed Latency in milliseconds
630
- * @returns {undefined}
631
- * @private
632
- * @memberof Reachability
633
- */
634
- private setLatencyAndClose(peerConnection: RTCPeerConnection, elapsed: number) {
635
- const REACHABLE = 'reachable';
636
- const UNREACHABLE = 'unreachable';
637
- const {CLOSED} = CONNECTION_STATE;
638
- // @ts-ignore
639
- const {key} = peerConnection;
640
- const resultKey = elapsed === null ? UNREACHABLE : REACHABLE;
641
- const intialState = {[REACHABLE]: 0, [UNREACHABLE]: 0};
360
+ this.clusterReachability[key] = new ClusterReachability(key, cluster);
642
361
 
643
- if (peerConnection.connectionState === CLOSED) {
644
- LoggerProxy.logger.log(
645
- `Reachability:index#setLatencyAndClose --> Attempting to set latency of ${elapsed} on closed peerConnection.`
646
- );
647
-
648
- return;
649
- }
362
+ return this.clusterReachability[key].start().then((result) => {
363
+ results[key] = result;
364
+ results[key].isVideoMesh = cluster.isVideoMesh;
365
+ });
366
+ });
650
367
 
651
- this.clusterLatencyResults[key] = this.clusterLatencyResults[key] || intialState;
652
- this.clusterLatencyResults[key][resultKey] += 1;
368
+ await Promise.all(clusterReachabilityChecks);
653
369
 
654
- // Set to null in case this fired from
655
- // an event other than onIceCandidate
656
- peerConnection.onicecandidate = null;
657
- peerConnection.close();
658
- // @ts-ignore
659
- peerConnection.elapsed = elapsed;
660
- }
370
+ this.logUnreachableClusters();
661
371
 
662
- /**
663
- * utility function
664
- * @returns {undefined}
665
- * @private
666
- * @memberof Reachability
667
- */
668
- private setup() {
669
- this.clusterLatencyResults = {};
372
+ return results;
670
373
  }
671
374
  }