@webex/plugin-meetings 3.10.0-next.9 → 3.10.0-webex-services-ready.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 (73) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +11 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/hashTree/constants.js +20 -0
  6. package/dist/hashTree/constants.js.map +1 -0
  7. package/dist/hashTree/hashTree.js +515 -0
  8. package/dist/hashTree/hashTree.js.map +1 -0
  9. package/dist/hashTree/hashTreeParser.js +1266 -0
  10. package/dist/hashTree/hashTreeParser.js.map +1 -0
  11. package/dist/hashTree/types.js +21 -0
  12. package/dist/hashTree/types.js.map +1 -0
  13. package/dist/hashTree/utils.js +48 -0
  14. package/dist/hashTree/utils.js.map +1 -0
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/index.js +511 -48
  18. package/dist/locus-info/index.js.map +1 -1
  19. package/dist/locus-info/types.js +7 -0
  20. package/dist/locus-info/types.js.map +1 -0
  21. package/dist/meeting/index.js +41 -15
  22. package/dist/meeting/index.js.map +1 -1
  23. package/dist/meeting/util.js +1 -0
  24. package/dist/meeting/util.js.map +1 -1
  25. package/dist/meetings/index.js +112 -70
  26. package/dist/meetings/index.js.map +1 -1
  27. package/dist/metrics/constants.js +3 -1
  28. package/dist/metrics/constants.js.map +1 -1
  29. package/dist/reachability/clusterReachability.js +44 -358
  30. package/dist/reachability/clusterReachability.js.map +1 -1
  31. package/dist/reachability/reachability.types.js +14 -1
  32. package/dist/reachability/reachability.types.js.map +1 -1
  33. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  34. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  35. package/dist/types/constants.d.ts +26 -21
  36. package/dist/types/hashTree/constants.d.ts +8 -0
  37. package/dist/types/hashTree/hashTree.d.ts +129 -0
  38. package/dist/types/hashTree/hashTreeParser.d.ts +260 -0
  39. package/dist/types/hashTree/types.d.ts +25 -0
  40. package/dist/types/hashTree/utils.d.ts +9 -0
  41. package/dist/types/locus-info/index.d.ts +91 -42
  42. package/dist/types/locus-info/types.d.ts +46 -0
  43. package/dist/types/meeting/index.d.ts +22 -9
  44. package/dist/types/meetings/index.d.ts +9 -2
  45. package/dist/types/metrics/constants.d.ts +2 -0
  46. package/dist/types/reachability/clusterReachability.d.ts +10 -88
  47. package/dist/types/reachability/reachability.types.d.ts +12 -1
  48. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  49. package/dist/webinar/index.js +1 -1
  50. package/package.json +22 -21
  51. package/src/constants.ts +13 -1
  52. package/src/hashTree/constants.ts +9 -0
  53. package/src/hashTree/hashTree.ts +463 -0
  54. package/src/hashTree/hashTreeParser.ts +1161 -0
  55. package/src/hashTree/types.ts +30 -0
  56. package/src/hashTree/utils.ts +42 -0
  57. package/src/locus-info/index.ts +556 -85
  58. package/src/locus-info/types.ts +48 -0
  59. package/src/meeting/index.ts +58 -26
  60. package/src/meeting/util.ts +1 -0
  61. package/src/meetings/index.ts +104 -51
  62. package/src/metrics/constants.ts +2 -0
  63. package/src/reachability/clusterReachability.ts +50 -347
  64. package/src/reachability/reachability.types.ts +15 -1
  65. package/src/reachability/reachabilityPeerConnection.ts +416 -0
  66. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  67. package/test/unit/spec/hashTree/hashTreeParser.ts +1532 -0
  68. package/test/unit/spec/hashTree/utils.ts +103 -0
  69. package/test/unit/spec/locus-info/index.js +667 -1
  70. package/test/unit/spec/meeting/index.js +91 -20
  71. package/test/unit/spec/meeting/utils.js +77 -0
  72. package/test/unit/spec/meetings/index.js +71 -26
  73. package/test/unit/spec/reachability/clusterReachability.ts +281 -138
@@ -1,12 +1,13 @@
1
- import {Defer} from '@webex/common';
2
-
3
- import LoggerProxy from '../common/logs/logger-proxy';
4
1
  import {ClusterNode} from './request';
5
- import {convertStunUrlToTurn, convertStunUrlToTurnTls} from './util';
6
2
  import EventsScope from '../common/events/events-scope';
7
3
 
8
- import {CONNECTION_STATE, Enum, ICE_GATHERING_STATE} from '../constants';
9
- import {ClusterReachabilityResult, NatType} from './reachability.types';
4
+ import {Enum} from '../constants';
5
+ import {
6
+ ClusterReachabilityResult,
7
+ NatType,
8
+ ReachabilityPeerConnectionEvents,
9
+ } from './reachability.types';
10
+ import {ReachabilityPeerConnection} from './reachabilityPeerConnection';
10
11
 
11
12
  // data for the Events.resultReady event
12
13
  export type ResultEventData = {
@@ -36,17 +37,11 @@ export type Events = Enum<typeof Events>;
36
37
 
37
38
  /**
38
39
  * A class that handles reachability checks for a single cluster.
39
- * It emits events from Events enum
40
+ * Creates and orchestrates a ReachabilityPeerConnection instance.
41
+ * Listens to events and emits them to consumers.
40
42
  */
41
43
  export class ClusterReachability extends EventsScope {
42
- private numUdpUrls: number;
43
- private numTcpUrls: number;
44
- private numXTlsUrls: number;
45
- private result: ClusterReachabilityResult;
46
- private pc?: RTCPeerConnection;
47
- private defer: Defer; // this defer is resolved once reachability checks for this cluster are completed
48
- private startTimestamp: number;
49
- private srflxIceCandidates: RTCIceCandidate[] = [];
44
+ private reachabilityPeerConnection: ReachabilityPeerConnection;
50
45
  public readonly isVideoMesh: boolean;
51
46
  public readonly name;
52
47
  public readonly reachedSubnets: Set<string> = new Set();
@@ -60,374 +55,82 @@ export class ClusterReachability extends EventsScope {
60
55
  super();
61
56
  this.name = name;
62
57
  this.isVideoMesh = clusterInfo.isVideoMesh;
63
- this.numUdpUrls = clusterInfo.udp.length;
64
- this.numTcpUrls = clusterInfo.tcp.length;
65
- this.numXTlsUrls = clusterInfo.xtls.length;
66
-
67
- this.pc = this.createPeerConnection(clusterInfo);
68
-
69
- this.defer = new Defer();
70
- this.result = {
71
- udp: {
72
- result: 'untested',
73
- },
74
- tcp: {
75
- result: 'untested',
76
- },
77
- xtls: {
78
- result: 'untested',
79
- },
80
- };
81
- }
82
-
83
- /**
84
- * Gets total elapsed time, can be called only after start() is called
85
- * @returns {Number} Milliseconds
86
- */
87
- private getElapsedTime() {
88
- return Math.round(performance.now() - this.startTimestamp);
89
- }
90
-
91
- /**
92
- * Generate peerConnection config settings
93
- * @param {ClusterNode} cluster
94
- * @returns {RTCConfiguration} peerConnectionConfig
95
- */
96
- private buildPeerConnectionConfig(cluster: ClusterNode): RTCConfiguration {
97
- const udpIceServers = cluster.udp.map((url) => ({
98
- username: '',
99
- credential: '',
100
- urls: [url],
101
- }));
102
-
103
- // STUN servers are contacted only using UDP, so in order to test TCP reachability
104
- // we pretend that Linus is a TURN server, because we can explicitly say "transport=tcp" in TURN urls.
105
- // We then check for relay candidates to know if TURN-TCP worked (see registerIceCandidateListener()).
106
- const tcpIceServers = cluster.tcp.map((urlString: string) => {
107
- return {
108
- username: 'webexturnreachuser',
109
- credential: 'webexturnreachpwd',
110
- urls: [convertStunUrlToTurn(urlString, 'tcp')],
111
- };
112
- });
113
58
 
114
- const turnTlsIceServers = cluster.xtls.map((urlString: string) => {
115
- return {
116
- username: 'webexturnreachuser',
117
- credential: 'webexturnreachpwd',
118
- urls: [convertStunUrlToTurnTls(urlString)],
119
- };
120
- });
59
+ this.reachabilityPeerConnection = new ReachabilityPeerConnection(name, clusterInfo);
121
60
 
122
- return {
123
- iceServers: [...udpIceServers, ...tcpIceServers, ...turnTlsIceServers],
124
- iceCandidatePoolSize: 0,
125
- iceTransportPolicy: 'all',
126
- };
61
+ this.setupReachabilityPeerConnectionEventListeners();
127
62
  }
128
63
 
129
64
  /**
130
- * Creates an RTCPeerConnection
131
- * @param {ClusterNode} clusterInfo information about the media cluster
132
- * @returns {RTCPeerConnection} peerConnection
133
- */
134
- private createPeerConnection(clusterInfo: ClusterNode) {
135
- try {
136
- const config = this.buildPeerConnectionConfig(clusterInfo);
137
-
138
- const peerConnection = new RTCPeerConnection(config);
139
-
140
- return peerConnection;
141
- } catch (peerConnectionError) {
142
- LoggerProxy.logger.warn(
143
- `Reachability:index#createPeerConnection --> Error creating peerConnection:`,
144
- peerConnectionError
145
- );
146
-
147
- return undefined;
148
- }
149
- }
150
-
151
- /**
152
- * @returns {ClusterReachabilityResult} reachability result for this cluster
153
- */
154
- getResult() {
155
- return this.result;
156
- }
157
-
158
- /**
159
- * Closes the peerConnection
160
- *
161
- * @returns {void}
162
- */
163
- private closePeerConnection() {
164
- if (this.pc) {
165
- this.pc.onicecandidate = null;
166
- this.pc.onicegatheringstatechange = null;
167
- this.pc.close();
168
- }
169
- }
170
-
171
- /**
172
- * Resolves the defer, indicating that reachability checks for this cluster are completed
173
- *
65
+ * Sets up event listeners for the ReachabilityPeerConnection instance
174
66
  * @returns {void}
175
67
  */
176
- private finishReachabilityCheck() {
177
- this.defer.resolve();
178
- }
179
-
180
- /**
181
- * Aborts the cluster reachability checks by closing the peer connection
182
- *
183
- * @returns {void}
184
- */
185
- public abort() {
186
- const {CLOSED} = CONNECTION_STATE;
187
-
188
- if (this.pc.connectionState !== CLOSED) {
189
- this.closePeerConnection();
190
- this.finishReachabilityCheck();
191
- }
192
- }
193
-
194
- /**
195
- * Adds public IP (client media IPs)
196
- * @param {string} protocol
197
- * @param {string} publicIP
198
- * @returns {void}
199
- */
200
- private addPublicIP(protocol: 'udp' | 'tcp' | 'xtls', publicIP?: string | null) {
201
- const result = this.result[protocol];
202
-
203
- if (publicIP) {
204
- let ipAdded = false;
205
-
206
- if (result.clientMediaIPs) {
207
- if (!result.clientMediaIPs.includes(publicIP)) {
208
- result.clientMediaIPs.push(publicIP);
209
- ipAdded = true;
210
- }
211
- } else {
212
- result.clientMediaIPs = [publicIP];
213
- ipAdded = true;
214
- }
68
+ private setupReachabilityPeerConnectionEventListeners() {
69
+ this.reachabilityPeerConnection.on(ReachabilityPeerConnectionEvents.resultReady, (data) => {
70
+ this.emit(
71
+ {
72
+ file: 'clusterReachability',
73
+ function: 'setupReachabilityPeerConnectionEventListeners',
74
+ },
75
+ Events.resultReady,
76
+ data
77
+ );
78
+ });
215
79
 
216
- if (ipAdded)
80
+ this.reachabilityPeerConnection.on(
81
+ ReachabilityPeerConnectionEvents.clientMediaIpsUpdated,
82
+ (data) => {
217
83
  this.emit(
218
84
  {
219
85
  file: 'clusterReachability',
220
- function: 'addPublicIP',
86
+ function: 'setupReachabilityPeerConnectionEventListeners',
221
87
  },
222
88
  Events.clientMediaIpsUpdated,
223
- {
224
- protocol,
225
- clientMediaIPs: result.clientMediaIPs,
226
- }
89
+ data
227
90
  );
228
- }
229
- }
230
-
231
- /**
232
- * Registers a listener for the iceGatheringStateChange event
233
- *
234
- * @returns {void}
235
- */
236
- private registerIceGatheringStateChangeListener() {
237
- this.pc.onicegatheringstatechange = () => {
238
- if (this.pc.iceGatheringState === ICE_GATHERING_STATE.COMPLETE) {
239
- this.closePeerConnection();
240
- this.finishReachabilityCheck();
241
- }
242
- };
243
- }
244
-
245
- /**
246
- * Saves the latency in the result for the given protocol and marks it as reachable,
247
- * emits the "resultReady" event if this is the first result for that protocol,
248
- * emits the "clientMediaIpsUpdated" event if we already had a result and only found
249
- * a new client IP
250
- *
251
- * @param {string} protocol
252
- * @param {number} latency
253
- * @param {string|null} [publicIp]
254
- * @param {string|null} [serverIp]
255
- * @returns {void}
256
- */
257
- private saveResult(
258
- protocol: 'udp' | 'tcp' | 'xtls',
259
- latency: number,
260
- publicIp?: string | null,
261
- serverIp?: string | null
262
- ) {
263
- const result = this.result[protocol];
264
-
265
- if (result.latencyInMilliseconds === undefined) {
266
- LoggerProxy.logger.log(
267
- // @ts-ignore
268
- `Reachability:index#saveResult --> Successfully reached ${this.name} over ${protocol}: ${latency}ms`
269
- );
270
- result.latencyInMilliseconds = latency;
271
- result.result = 'reachable';
272
- if (publicIp) {
273
- result.clientMediaIPs = [publicIp];
274
91
  }
92
+ );
275
93
 
94
+ this.reachabilityPeerConnection.on(ReachabilityPeerConnectionEvents.natTypeUpdated, (data) => {
276
95
  this.emit(
277
96
  {
278
97
  file: 'clusterReachability',
279
- function: 'saveResult',
98
+ function: 'setupReachabilityPeerConnectionEventListeners',
280
99
  },
281
- Events.resultReady,
282
- {
283
- protocol,
284
- ...result,
285
- }
100
+ Events.natTypeUpdated,
101
+ data
286
102
  );
287
- } else {
288
- this.addPublicIP(protocol, publicIp);
289
- }
290
-
291
- if (serverIp) {
292
- this.reachedSubnets.add(serverIp);
293
- }
294
- }
295
-
296
- /**
297
- * Determines NAT Type.
298
- *
299
- * @param {RTCIceCandidate} candidate
300
- * @returns {void}
301
- */
302
- private determineNatType(candidate: RTCIceCandidate) {
303
- this.srflxIceCandidates.push(candidate);
304
-
305
- if (this.srflxIceCandidates.length > 1) {
306
- const portsFound: Record<string, Set<number>> = {};
307
-
308
- this.srflxIceCandidates.forEach((c) => {
309
- const key = `${c.address}:${c.relatedPort}`;
310
- if (!portsFound[key]) {
311
- portsFound[key] = new Set();
312
- }
313
- portsFound[key].add(c.port);
314
- });
103
+ });
315
104
 
316
- Object.entries(portsFound).forEach(([, ports]) => {
317
- if (ports.size > 1) {
318
- // Found candidates with the same address and relatedPort, but different ports
319
- this.emit(
320
- {
321
- file: 'clusterReachability',
322
- function: 'determineNatType',
323
- },
324
- Events.natTypeUpdated,
325
- {
326
- natType: NatType.SymmetricNat,
327
- }
328
- );
329
- }
105
+ this.reachabilityPeerConnection.on(ReachabilityPeerConnectionEvents.reachedSubnets, (data) => {
106
+ data.subnets.forEach((subnet) => {
107
+ this.reachedSubnets.add(subnet);
330
108
  });
331
- }
109
+ });
332
110
  }
333
111
 
334
112
  /**
335
- * Registers a listener for the icecandidate event
336
- *
337
- * @returns {void}
113
+ * @returns {ClusterReachabilityResult} reachability result for this cluster
338
114
  */
339
- private registerIceCandidateListener() {
340
- this.pc.onicecandidate = (e) => {
341
- const TURN_TLS_PORT = 443;
342
- const CANDIDATE_TYPES = {
343
- SERVER_REFLEXIVE: 'srflx',
344
- RELAY: 'relay',
345
- };
346
-
347
- const latencyInMilliseconds = this.getElapsedTime();
348
-
349
- if (e.candidate) {
350
- if (e.candidate.type === CANDIDATE_TYPES.SERVER_REFLEXIVE) {
351
- let serverIp = null;
352
- if ('url' in e.candidate) {
353
- const stunServerUrlRegex = /stun:([\d.]+):\d+/;
354
-
355
- const match = (e.candidate as any).url.match(stunServerUrlRegex);
356
- if (match) {
357
- // eslint-disable-next-line prefer-destructuring
358
- serverIp = match[1];
359
- }
360
- }
361
-
362
- this.saveResult('udp', latencyInMilliseconds, e.candidate.address, serverIp);
363
-
364
- this.determineNatType(e.candidate);
365
- }
366
-
367
- if (e.candidate.type === CANDIDATE_TYPES.RELAY) {
368
- const protocol = e.candidate.port === TURN_TLS_PORT ? 'xtls' : 'tcp';
369
- this.saveResult(protocol, latencyInMilliseconds, null, e.candidate.address);
370
- }
371
- }
372
- };
115
+ getResult(): ClusterReachabilityResult {
116
+ return this.reachabilityPeerConnection.getResult();
373
117
  }
374
118
 
375
119
  /**
376
- * Starts the process of doing UDP and TCP reachability checks on the media cluster.
377
- * XTLS reachability checking is not supported.
378
- *
379
- * @returns {Promise}
120
+ * Starts the process of doing UDP, TCP, and XTLS reachability checks on the media cluster.
121
+ * @returns {Promise<ClusterReachabilityResult>}
380
122
  */
381
123
  async start(): Promise<ClusterReachabilityResult> {
382
- if (!this.pc) {
383
- LoggerProxy.logger.warn(
384
- `Reachability:ClusterReachability#start --> Error: peerConnection is undefined`
385
- );
386
-
387
- return this.result;
388
- }
389
-
390
- // Initialize this.result as saying that nothing is reachable.
391
- // It will get updated as we go along and successfully gather ICE candidates.
392
- this.result.udp = {
393
- result: this.numUdpUrls > 0 ? 'unreachable' : 'untested',
394
- };
395
- this.result.tcp = {
396
- result: this.numTcpUrls > 0 ? 'unreachable' : 'untested',
397
- };
398
- this.result.xtls = {
399
- result: this.numXTlsUrls > 0 ? 'unreachable' : 'untested',
400
- };
401
-
402
- try {
403
- const offer = await this.pc.createOffer({offerToReceiveAudio: true});
404
-
405
- this.startTimestamp = performance.now();
124
+ await this.reachabilityPeerConnection.start();
406
125
 
407
- // Set up the state change listeners before triggering the ICE gathering
408
- const gatherIceCandidatePromise = this.gatherIceCandidates();
409
-
410
- // not awaiting the next call on purpose, because we're not sending the offer anywhere and there won't be any answer
411
- // we just need to make this call to trigger the ICE gathering process
412
- this.pc.setLocalDescription(offer);
413
-
414
- await gatherIceCandidatePromise;
415
- } catch (error) {
416
- LoggerProxy.logger.warn(`Reachability:ClusterReachability#start --> Error: `, error);
417
- }
418
-
419
- return this.result;
126
+ return this.getResult();
420
127
  }
421
128
 
422
129
  /**
423
- * Starts the process of gathering ICE candidates
424
- *
425
- * @returns {Promise} promise that's resolved once reachability checks for this cluster are completed or timeout is reached
130
+ * Aborts the cluster reachability checks
131
+ * @returns {void}
426
132
  */
427
- private gatherIceCandidates() {
428
- this.registerIceGatheringStateChangeListener();
429
- this.registerIceCandidateListener();
430
-
431
- return this.defer.promise;
133
+ public abort() {
134
+ this.reachabilityPeerConnection.abort();
432
135
  }
433
136
  }
@@ -1,4 +1,18 @@
1
- import {IP_VERSION} from '../constants';
1
+ import {IP_VERSION, Enum} from '../constants';
2
+
3
+ export type Protocol = 'udp' | 'tcp' | 'xtls';
4
+
5
+ /**
6
+ * Events emitted by ReachabilityPeerConnection
7
+ */
8
+ export const ReachabilityPeerConnectionEvents = {
9
+ resultReady: 'resultReady', // emitted when successfully reached over a protocol
10
+ clientMediaIpsUpdated: 'clientMediaIpsUpdated', // emitted when new public IPs are found
11
+ natTypeUpdated: 'natTypeUpdated', // emitted when NAT type is determined
12
+ reachedSubnets: 'reachedSubnets', // emitted when server IP (subnet) is discovered
13
+ } as const;
14
+
15
+ export type ReachabilityPeerConnectionEvents = Enum<typeof ReachabilityPeerConnectionEvents>;
2
16
 
3
17
  // result for a specific transport protocol (like udp or tcp)
4
18
  export type TransportResult = {