@webex/plugin-meetings 3.0.0 → 3.1.0
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.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +5 -4
- package/dist/constants.js +8 -4
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +16 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/mediaSharesUtils.js +15 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/selfUtils.js +5 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.d.ts +61 -0
- package/dist/media/MediaConnectionAwaiter.js +163 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -0
- package/dist/media/index.js +4 -1
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -24
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.d.ts +26 -7
- package/dist/meeting/index.js +893 -677
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.d.ts +2 -8
- package/dist/meeting/muteState.js +37 -25
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.d.ts +3 -0
- package/dist/meeting/request.js +32 -23
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +4 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.d.ts +8 -0
- package/dist/meetings/index.js +20 -0
- package/dist/meetings/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.d.ts +2 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.d.ts +2 -0
- package/dist/multistream/remoteMediaGroup.js +16 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.d.ts +15 -0
- package/dist/multistream/remoteMediaManager.js +179 -65
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.d.ts +9 -1
- package/dist/multistream/sendSlotManager.js +22 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.d.ts +1 -0
- package/dist/reachability/clusterReachability.js +29 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.d.ts +4 -0
- package/dist/reachability/index.js +18 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +12 -10
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.d.ts +7 -0
- package/dist/reachability/util.js +19 -0
- package/dist/reachability/util.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.d.ts +10 -2
- package/dist/roap/index.js +15 -0
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +3 -3
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.d.ts +64 -17
- package/dist/roap/turnDiscovery.js +307 -126
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +53 -30
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/config.ts +1 -0
- package/src/constants.ts +7 -3
- package/src/index.ts +1 -0
- package/src/interpretation/index.ts +18 -1
- package/src/locus-info/mediaSharesUtils.ts +16 -0
- package/src/locus-info/selfUtils.ts +5 -0
- package/src/media/MediaConnectionAwaiter.ts +174 -0
- package/src/media/index.ts +3 -1
- package/src/media/properties.ts +6 -31
- package/src/meeting/index.ts +321 -106
- package/src/meeting/muteState.ts +34 -20
- package/src/meeting/request.ts +18 -2
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/utilv2.ts +2 -1
- package/src/meetings/index.ts +18 -0
- package/src/multistream/mediaRequestManager.ts +4 -1
- package/src/multistream/remoteMediaGroup.ts +19 -0
- package/src/multistream/remoteMediaManager.ts +101 -16
- package/src/multistream/sendSlotManager.ts +28 -0
- package/src/reachability/clusterReachability.ts +20 -5
- package/src/reachability/index.ts +24 -1
- package/src/reachability/request.ts +15 -11
- package/src/reachability/util.ts +21 -0
- package/src/reconnection-manager/index.ts +1 -1
- package/src/roap/index.ts +25 -3
- package/src/roap/request.ts +3 -3
- package/src/roap/turnDiscovery.ts +244 -78
- package/src/statsAnalyzer/index.ts +63 -27
- package/test/integration/spec/journey.js +14 -14
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/interpretation/index.ts +39 -3
- package/test/unit/spec/locus-info/index.js +28 -19
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
- package/test/unit/spec/locus-info/selfUtils.js +42 -12
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
- package/test/unit/spec/media/index.ts +89 -78
- package/test/unit/spec/media/properties.ts +16 -70
- package/test/unit/spec/meeting/index.js +638 -139
- package/test/unit/spec/meeting/muteState.js +219 -67
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +6 -1
- package/test/unit/spec/meeting-info/utilv2.js +6 -0
- package/test/unit/spec/meetings/index.js +40 -20
- package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
- package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
- package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
- package/test/unit/spec/reachability/clusterReachability.ts +86 -22
- package/test/unit/spec/reachability/index.ts +197 -60
- package/test/unit/spec/reachability/request.js +15 -7
- package/test/unit/spec/reachability/util.ts +32 -2
- package/test/unit/spec/reconnection-manager/index.js +28 -0
- package/test/unit/spec/roap/index.ts +61 -6
- package/test/unit/spec/roap/turnDiscovery.ts +298 -16
- package/test/unit/spec/stats-analyzer/index.js +179 -0
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -17
- package/dist/member/member.types.js.map +0 -1
- package/src/member/member.types.ts +0 -13
- /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
|
@@ -22,10 +22,14 @@ export type ReachabilityMetrics = {
|
|
|
22
22
|
reachability_public_udp_failed: number;
|
|
23
23
|
reachability_public_tcp_success: number;
|
|
24
24
|
reachability_public_tcp_failed: number;
|
|
25
|
+
reachability_public_xtls_success: number;
|
|
26
|
+
reachability_public_xtls_failed: number;
|
|
25
27
|
reachability_vmn_udp_success: number;
|
|
26
28
|
reachability_vmn_udp_failed: number;
|
|
27
29
|
reachability_vmn_tcp_success: number;
|
|
28
30
|
reachability_vmn_tcp_failed: number;
|
|
31
|
+
reachability_vmn_xtls_success: number;
|
|
32
|
+
reachability_vmn_xtls_failed: number;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
/**
|
|
@@ -141,10 +145,14 @@ export default class Reachability {
|
|
|
141
145
|
reachability_public_udp_failed: 0,
|
|
142
146
|
reachability_public_tcp_success: 0,
|
|
143
147
|
reachability_public_tcp_failed: 0,
|
|
148
|
+
reachability_public_xtls_success: 0,
|
|
149
|
+
reachability_public_xtls_failed: 0,
|
|
144
150
|
reachability_vmn_udp_success: 0,
|
|
145
151
|
reachability_vmn_udp_failed: 0,
|
|
146
152
|
reachability_vmn_tcp_success: 0,
|
|
147
153
|
reachability_vmn_tcp_failed: 0,
|
|
154
|
+
reachability_vmn_xtls_success: 0,
|
|
155
|
+
reachability_vmn_xtls_failed: 0,
|
|
148
156
|
};
|
|
149
157
|
|
|
150
158
|
const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
|
|
@@ -156,6 +164,10 @@ export default class Reachability {
|
|
|
156
164
|
const outcome = result.tcp.result === 'reachable' ? 'success' : 'failed';
|
|
157
165
|
stats[`reachability_${clusterType}_tcp_${outcome}`] += 1;
|
|
158
166
|
}
|
|
167
|
+
if (result.xtls && result.xtls.result !== 'untested') {
|
|
168
|
+
const outcome = result.xtls.result === 'reachable' ? 'success' : 'failed';
|
|
169
|
+
stats[`reachability_${clusterType}_xtls_${outcome}`] += 1;
|
|
170
|
+
}
|
|
159
171
|
};
|
|
160
172
|
|
|
161
173
|
try {
|
|
@@ -338,7 +350,10 @@ export default class Reachability {
|
|
|
338
350
|
LoggerProxy.logger.log(
|
|
339
351
|
`Reachability:index#performReachabilityChecks --> doing UDP${
|
|
340
352
|
// @ts-ignore
|
|
341
|
-
this.webex.config.meetings.experimental.enableTcpReachability ? '
|
|
353
|
+
this.webex.config.meetings.experimental.enableTcpReachability ? ',TCP' : ''
|
|
354
|
+
}${
|
|
355
|
+
// @ts-ignore
|
|
356
|
+
this.webex.config.meetings.experimental.enableTlsReachability ? ',TLS' : ''
|
|
342
357
|
} reachability checks`
|
|
343
358
|
);
|
|
344
359
|
|
|
@@ -354,6 +369,14 @@ export default class Reachability {
|
|
|
354
369
|
cluster.tcp = [];
|
|
355
370
|
}
|
|
356
371
|
|
|
372
|
+
const includeTlsReachability =
|
|
373
|
+
// @ts-ignore
|
|
374
|
+
this.webex.config.meetings.experimental.enableTlsReachability && !cluster.isVideoMesh;
|
|
375
|
+
|
|
376
|
+
if (!includeTlsReachability) {
|
|
377
|
+
cluster.xtls = [];
|
|
378
|
+
}
|
|
379
|
+
|
|
357
380
|
this.clusterReachability[key] = new ClusterReachability(key, cluster);
|
|
358
381
|
|
|
359
382
|
return this.clusterReachability[key].start().then((result) => {
|
|
@@ -34,17 +34,21 @@ class ReachabilityRequest {
|
|
|
34
34
|
* @returns {Promise}
|
|
35
35
|
*/
|
|
36
36
|
getClusters = (ipVersion?: IP_VERSION): Promise<{clusters: ClusterList; joinCookie: any}> =>
|
|
37
|
-
this.webex
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
this.webex.internal.newMetrics.callDiagnosticLatencies
|
|
38
|
+
.measureLatency(
|
|
39
|
+
() =>
|
|
40
|
+
this.webex.request({
|
|
41
|
+
method: HTTP_VERBS.GET,
|
|
42
|
+
shouldRefreshAccessToken: false,
|
|
43
|
+
api: API.CALLIOPEDISCOVERY,
|
|
44
|
+
resource: RESOURCE.CLUSTERS,
|
|
45
|
+
qs: {
|
|
46
|
+
JCSupport: 1,
|
|
47
|
+
ipver: ipVersion,
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
'internal.get.cluster.time'
|
|
51
|
+
)
|
|
48
52
|
.then((res) => {
|
|
49
53
|
const {clusters, joinCookie} = res.body;
|
|
50
54
|
|
package/src/reachability/util.ts
CHANGED
|
@@ -22,3 +22,24 @@ export function convertStunUrlToTurn(stunUrl: string, protocol: 'udp' | 'tcp') {
|
|
|
22
22
|
|
|
23
23
|
return url.toString();
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Converts a stun url to a turns url
|
|
28
|
+
*
|
|
29
|
+
* @param {string} stunUrl url of a stun server
|
|
30
|
+
* @returns {string} url of a turns server
|
|
31
|
+
*/
|
|
32
|
+
export function convertStunUrlToTurnTls(stunUrl: string) {
|
|
33
|
+
// stunUrl looks like this: "stun:external-media1.public.wjfkm-a-15.prod.infra.webex.com:443"
|
|
34
|
+
// and we need it to be like this: "turns:external-media1.public.wjfkm-a-15.prod.infra.webex.com:443?transport=tcp"
|
|
35
|
+
const url = new URL(stunUrl);
|
|
36
|
+
|
|
37
|
+
if (url.protocol !== 'stun:') {
|
|
38
|
+
throw new Error(`Not a STUN URL: ${stunUrl}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
url.protocol = 'turns:';
|
|
42
|
+
url.searchParams.append('transport', 'tcp');
|
|
43
|
+
|
|
44
|
+
return url.toString();
|
|
45
|
+
}
|
|
@@ -568,7 +568,7 @@ export default class ReconnectionManager {
|
|
|
568
568
|
|
|
569
569
|
const iceServers = [];
|
|
570
570
|
|
|
571
|
-
if (turnServerResult.turnServerInfo) {
|
|
571
|
+
if (turnServerResult.turnServerInfo?.url) {
|
|
572
572
|
iceServers.push({
|
|
573
573
|
urls: turnServerResult.turnServerInfo.url,
|
|
574
574
|
username: turnServerResult.turnServerInfo.username || '',
|
package/src/roap/index.ts
CHANGED
|
@@ -5,12 +5,18 @@ import {ROAP} from '../constants';
|
|
|
5
5
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
6
6
|
|
|
7
7
|
import RoapRequest from './request';
|
|
8
|
-
import TurnDiscovery from './turnDiscovery';
|
|
8
|
+
import TurnDiscovery, {TurnDiscoveryResult} from './turnDiscovery';
|
|
9
9
|
import Meeting from '../meeting';
|
|
10
10
|
import MeetingUtil from '../meeting/util';
|
|
11
11
|
import Metrics from '../metrics';
|
|
12
12
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
13
13
|
|
|
14
|
+
export {
|
|
15
|
+
type TurnDiscoveryResult,
|
|
16
|
+
type TurnServerInfo,
|
|
17
|
+
type TurnDiscoverySkipReason,
|
|
18
|
+
} from './turnDiscovery';
|
|
19
|
+
|
|
14
20
|
/**
|
|
15
21
|
* Roap options
|
|
16
22
|
* @typedef {Object} RoapOptions
|
|
@@ -39,7 +45,7 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
39
45
|
options: any;
|
|
40
46
|
roapHandler: any;
|
|
41
47
|
roapRequest: any;
|
|
42
|
-
turnDiscovery:
|
|
48
|
+
turnDiscovery: TurnDiscovery;
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
*
|
|
@@ -260,7 +266,23 @@ export default class Roap extends StatelessWebexPlugin {
|
|
|
260
266
|
* @param {Boolean} [isForced]
|
|
261
267
|
* @returns {Promise}
|
|
262
268
|
*/
|
|
263
|
-
doTurnDiscovery(
|
|
269
|
+
doTurnDiscovery(
|
|
270
|
+
meeting: Meeting,
|
|
271
|
+
isReconnecting: boolean,
|
|
272
|
+
isForced?: boolean
|
|
273
|
+
): Promise<TurnDiscoveryResult> {
|
|
264
274
|
return this.turnDiscovery.doTurnDiscovery(meeting, isReconnecting, isForced);
|
|
265
275
|
}
|
|
276
|
+
|
|
277
|
+
generateTurnDiscoveryRequestMessage(meeting: Meeting, isForced: boolean) {
|
|
278
|
+
return this.turnDiscovery.generateTurnDiscoveryRequestMessage(meeting, isForced);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
handleTurnDiscoveryHttpResponse(meeting: Meeting, httpResponse: object) {
|
|
282
|
+
return this.turnDiscovery.handleTurnDiscoveryHttpResponse(meeting, httpResponse);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
abortTurnDiscovery() {
|
|
286
|
+
return this.turnDiscovery.abort();
|
|
287
|
+
}
|
|
266
288
|
}
|
package/src/roap/request.ts
CHANGED
|
@@ -123,7 +123,7 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
123
123
|
);
|
|
124
124
|
const {locus} = res.body;
|
|
125
125
|
|
|
126
|
-
locus.roapSeq = roapMessage.seq;
|
|
126
|
+
locus.roapSeq = options.roapMessage.seq;
|
|
127
127
|
|
|
128
128
|
return {
|
|
129
129
|
locus,
|
|
@@ -139,9 +139,9 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
139
139
|
rawError: err,
|
|
140
140
|
},
|
|
141
141
|
});
|
|
142
|
-
LoggerProxy.logger.error(`Roap:request#sendRoap --> Error
|
|
142
|
+
LoggerProxy.logger.error(`Roap:request#sendRoap --> Error:`, err);
|
|
143
143
|
LoggerProxy.logger.error(
|
|
144
|
-
`Roap:request#sendRoapRequest -->
|
|
144
|
+
`Roap:request#sendRoapRequest --> roapMessage that caused error:${JSON.stringify(
|
|
145
145
|
roapMessage,
|
|
146
146
|
null,
|
|
147
147
|
2
|
|
@@ -4,7 +4,7 @@ import {Defer} from '@webex/common';
|
|
|
4
4
|
import Metrics from '../metrics';
|
|
5
5
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
6
6
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
7
|
-
import {ROAP} from '../constants';
|
|
7
|
+
import {ROAP, Enum} from '../constants';
|
|
8
8
|
|
|
9
9
|
import RoapRequest from './request';
|
|
10
10
|
import Meeting from '../meeting';
|
|
@@ -18,6 +18,28 @@ const TURN_DISCOVERY_TIMEOUT = 10; // in seconds
|
|
|
18
18
|
// and do the SDP offer with seq=1
|
|
19
19
|
const TURN_DISCOVERY_SEQ = 0;
|
|
20
20
|
|
|
21
|
+
const TurnDiscoverySkipReason = {
|
|
22
|
+
missingHttpResponse: 'missing http response', // when we asked for the TURN discovery response to be in the http response, but it wasn't there
|
|
23
|
+
reachability: 'reachability', // when udp reachability to public clusters is ok, so we don't need TURN (this doens't apply when joinWithMedia() is used)
|
|
24
|
+
alreadyInProgress: 'already in progress', // when we try to start TURN discovery while it's already in progress
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type TurnDiscoverySkipReason =
|
|
28
|
+
| Enum<typeof TurnDiscoverySkipReason> // this is a kind of FYI, because in practice typescript will infer the type of TurnDiscoverySkipReason as a string
|
|
29
|
+
| string // used in case of errors, contains the error message
|
|
30
|
+
| undefined; // used when TURN discovery is not skipped
|
|
31
|
+
|
|
32
|
+
export type TurnServerInfo = {
|
|
33
|
+
url: string;
|
|
34
|
+
username: string;
|
|
35
|
+
password: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type TurnDiscoveryResult = {
|
|
39
|
+
turnServerInfo?: TurnServerInfo;
|
|
40
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
41
|
+
};
|
|
42
|
+
|
|
21
43
|
/**
|
|
22
44
|
* Handles the process of finding out TURN server information from Linus.
|
|
23
45
|
* This is achieved by sending a TURN_DISCOVERY_REQUEST.
|
|
@@ -27,11 +49,7 @@ export default class TurnDiscovery {
|
|
|
27
49
|
|
|
28
50
|
private defer?: Defer; // used for waiting for the response
|
|
29
51
|
|
|
30
|
-
private turnInfo:
|
|
31
|
-
url: string;
|
|
32
|
-
username: string;
|
|
33
|
-
password: string;
|
|
34
|
-
};
|
|
52
|
+
private turnInfo: TurnServerInfo;
|
|
35
53
|
|
|
36
54
|
private responseTimer?: ReturnType<typeof setTimeout>;
|
|
37
55
|
|
|
@@ -85,7 +103,8 @@ export default class TurnDiscovery {
|
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
/**
|
|
88
|
-
*
|
|
106
|
+
* Handles TURN_DISCOVERY_RESPONSE roap message. Use it if the roap message comes over the websocket,
|
|
107
|
+
* otherwise use handleTurnDiscoveryHttpResponse() if it comes in the http response.
|
|
89
108
|
*
|
|
90
109
|
* @param {Object} roapMessage
|
|
91
110
|
* @param {string} from string to indicate how we got the response (used just for logging)
|
|
@@ -158,18 +177,191 @@ export default class TurnDiscovery {
|
|
|
158
177
|
}
|
|
159
178
|
|
|
160
179
|
/**
|
|
161
|
-
*
|
|
180
|
+
* Generates TURN_DISCOVERY_REQUEST roap message. When this method returns a roapMessage, it means that a TURN discovery process has started.
|
|
181
|
+
* It needs be ended by calling handleTurnDiscoveryHttpResponse() once you get a response from the backend. If you don't get any response
|
|
182
|
+
* or want to abort, you need to call abort().
|
|
162
183
|
*
|
|
163
|
-
* @param {
|
|
164
|
-
* @
|
|
184
|
+
* @param {Meeting} meeting
|
|
185
|
+
* @param {boolean} isForced
|
|
186
|
+
* @returns {Object}
|
|
187
|
+
*/
|
|
188
|
+
public async generateTurnDiscoveryRequestMessage(
|
|
189
|
+
meeting: Meeting,
|
|
190
|
+
isForced: boolean
|
|
191
|
+
): Promise<{roapMessage?: object; turnDiscoverySkippedReason: TurnDiscoverySkipReason}> {
|
|
192
|
+
if (this.defer) {
|
|
193
|
+
LoggerProxy.logger.warn(
|
|
194
|
+
'Roap:turnDiscovery#generateTurnDiscoveryRequestMessage --> TURN discovery already in progress'
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
roapMessage: undefined,
|
|
199
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.alreadyInProgress,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
204
|
+
|
|
205
|
+
if (!isForced) {
|
|
206
|
+
turnDiscoverySkippedReason = await this.getSkipReason(meeting);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (turnDiscoverySkippedReason) {
|
|
210
|
+
return {roapMessage: undefined, turnDiscoverySkippedReason};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.defer = new Defer();
|
|
214
|
+
|
|
215
|
+
const roapMessage = {
|
|
216
|
+
messageType: ROAP.ROAP_TYPES.TURN_DISCOVERY_REQUEST,
|
|
217
|
+
version: ROAP.ROAP_VERSION,
|
|
218
|
+
seq: TURN_DISCOVERY_SEQ,
|
|
219
|
+
headers: ['includeAnswerInHttpResponse', 'noOkInTransaction'],
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
LoggerProxy.logger.info(
|
|
223
|
+
'Roap:turnDiscovery#generateTurnDiscoveryRequestMessage --> generated TURN_DISCOVERY_REQUEST message'
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return {roapMessage, turnDiscoverySkippedReason: undefined};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Handles any errors that occur during TURN discovery without re-throwing them.
|
|
231
|
+
*
|
|
232
|
+
* @param {Meeting} meeting
|
|
233
|
+
* @param {Error} error
|
|
234
|
+
* @returns {TurnDiscoveryResult}
|
|
235
|
+
*/
|
|
236
|
+
private handleTurnDiscoveryFailure(meeting: Meeting, error: Error): TurnDiscoveryResult {
|
|
237
|
+
// we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN
|
|
238
|
+
LoggerProxy.logger.info(
|
|
239
|
+
`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${error}`
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE, {
|
|
243
|
+
correlation_id: meeting.correlationId,
|
|
244
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
245
|
+
reason: error.message,
|
|
246
|
+
stack: error.stack,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {turnServerInfo: undefined, turnDiscoverySkippedReason: `failure: ${error.message}`};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handles TURN_DISCOVERY_RESPONSE roap message that came in http response. If the response is not valid,
|
|
254
|
+
* it returns an object with turnServerInfo set to undefined. In that case you need to call abort()
|
|
255
|
+
* to end the TURN discovery process.
|
|
256
|
+
*
|
|
257
|
+
* @param {Meeting} meeting
|
|
258
|
+
* @param {Object|undefined} httpResponse can be undefined to indicate that we didn't get the response
|
|
259
|
+
* @returns {Promise<TurnDiscoveryResult>}
|
|
165
260
|
* @memberof Roap
|
|
166
261
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
262
|
+
public async handleTurnDiscoveryHttpResponse(
|
|
263
|
+
meeting: Meeting,
|
|
264
|
+
httpResponse?: object
|
|
265
|
+
): Promise<TurnDiscoveryResult> {
|
|
266
|
+
if (!this.defer) {
|
|
267
|
+
LoggerProxy.logger.warn(
|
|
268
|
+
'Roap:turnDiscovery#handleTurnDiscoveryHttpResponse --> unexpected http response, TURN discovery is not in progress'
|
|
269
|
+
);
|
|
171
270
|
|
|
172
|
-
|
|
271
|
+
throw new Error(
|
|
272
|
+
'handleTurnDiscoveryHttpResponse() called before generateTurnDiscoveryRequestMessage()'
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (httpResponse === undefined) {
|
|
277
|
+
return {
|
|
278
|
+
turnServerInfo: undefined,
|
|
279
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.missingHttpResponse,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const roapMessage = this.parseHttpTurnDiscoveryResponse(meeting, httpResponse);
|
|
285
|
+
|
|
286
|
+
if (!roapMessage) {
|
|
287
|
+
return {
|
|
288
|
+
turnServerInfo: undefined,
|
|
289
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.missingHttpResponse,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.handleTurnDiscoveryResponse(roapMessage, 'in http response');
|
|
294
|
+
|
|
295
|
+
const {isOkRequired} = await this.defer.promise;
|
|
296
|
+
|
|
297
|
+
if (isOkRequired) {
|
|
298
|
+
await this.sendRoapOK(meeting);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.defer = undefined;
|
|
302
|
+
|
|
303
|
+
LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');
|
|
304
|
+
|
|
305
|
+
return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.abort();
|
|
308
|
+
|
|
309
|
+
return this.handleTurnDiscoveryFailure(meeting, error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Aborts current TURN discovery. This method needs to be called if you called generateTurnDiscoveryRequestMessage(),
|
|
315
|
+
* but then never got any response from the server.
|
|
316
|
+
* @returns {void}
|
|
317
|
+
*/
|
|
318
|
+
public abort() {
|
|
319
|
+
if (this.defer) {
|
|
320
|
+
this.defer.reject(new Error('TURN discovery aborted'));
|
|
321
|
+
this.defer = undefined;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Parses the TURN_DISCOVERY_RESPONSE roap message out of the http response
|
|
327
|
+
* and returns it.
|
|
328
|
+
*
|
|
329
|
+
* @param {Meeting} meeting
|
|
330
|
+
* @param {any} httpResponse
|
|
331
|
+
* @returns {any}
|
|
332
|
+
*/
|
|
333
|
+
private parseHttpTurnDiscoveryResponse(
|
|
334
|
+
meeting: Meeting,
|
|
335
|
+
httpResponse: {mediaConnections?: Array<{remoteSdp?: string}>}
|
|
336
|
+
) {
|
|
337
|
+
let turnDiscoveryResponse;
|
|
338
|
+
|
|
339
|
+
if (httpResponse.mediaConnections?.[0]?.remoteSdp) {
|
|
340
|
+
const remoteSdp = JSON.parse(httpResponse.mediaConnections[0].remoteSdp);
|
|
341
|
+
|
|
342
|
+
if (remoteSdp.roapMessage) {
|
|
343
|
+
// yes, it's misleading that remoteSdp actually contains a TURN discovery response, but that's how the backend works...
|
|
344
|
+
const {seq, messageType, errorType, errorCause, headers} = remoteSdp.roapMessage;
|
|
345
|
+
|
|
346
|
+
turnDiscoveryResponse = {
|
|
347
|
+
seq,
|
|
348
|
+
messageType,
|
|
349
|
+
errorType,
|
|
350
|
+
errorCause,
|
|
351
|
+
headers,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!turnDiscoveryResponse) {
|
|
357
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING, {
|
|
358
|
+
correlationId: meeting.correlationId,
|
|
359
|
+
messageType: 'TURN_DISCOVERY_RESPONSE',
|
|
360
|
+
isMultistream: meeting.isMultistream,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return turnDiscoveryResponse;
|
|
173
365
|
}
|
|
174
366
|
|
|
175
367
|
/**
|
|
@@ -181,13 +373,19 @@ export default class TurnDiscovery {
|
|
|
181
373
|
* @private
|
|
182
374
|
* @memberof Roap
|
|
183
375
|
*/
|
|
184
|
-
sendRoapTurnDiscoveryRequest(
|
|
376
|
+
private sendRoapTurnDiscoveryRequest(
|
|
377
|
+
meeting: Meeting,
|
|
378
|
+
isReconnecting: boolean
|
|
379
|
+
): Promise<TurnDiscoveryResult> {
|
|
185
380
|
if (this.defer) {
|
|
186
381
|
LoggerProxy.logger.warn(
|
|
187
382
|
'Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> already in progress'
|
|
188
383
|
);
|
|
189
384
|
|
|
190
|
-
return Promise.resolve(
|
|
385
|
+
return Promise.resolve({
|
|
386
|
+
turnServerInfo: undefined,
|
|
387
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason.alreadyInProgress,
|
|
388
|
+
});
|
|
191
389
|
}
|
|
192
390
|
|
|
193
391
|
this.defer = new Defer();
|
|
@@ -215,41 +413,14 @@ export default class TurnDiscovery {
|
|
|
215
413
|
// @ts-ignore - because of meeting.webex
|
|
216
414
|
ipVersion: MeetingUtil.getIpVersion(meeting.webex),
|
|
217
415
|
})
|
|
218
|
-
.then((response) => {
|
|
416
|
+
.then(async (response) => {
|
|
219
417
|
const {mediaConnections} = response;
|
|
220
418
|
|
|
221
|
-
let turnDiscoveryResponse;
|
|
222
|
-
|
|
223
419
|
if (mediaConnections) {
|
|
224
420
|
meeting.updateMediaConnections(mediaConnections);
|
|
225
|
-
|
|
226
|
-
if (mediaConnections[0]?.remoteSdp) {
|
|
227
|
-
const remoteSdp = JSON.parse(mediaConnections[0].remoteSdp);
|
|
228
|
-
|
|
229
|
-
if (remoteSdp.roapMessage) {
|
|
230
|
-
// yes, it's misleading that remoteSdp actually contains a TURN discovery response, but that's how the backend works...
|
|
231
|
-
const {seq, messageType, errorType, errorCause, headers} = remoteSdp.roapMessage;
|
|
232
|
-
|
|
233
|
-
turnDiscoveryResponse = {
|
|
234
|
-
seq,
|
|
235
|
-
messageType,
|
|
236
|
-
errorType,
|
|
237
|
-
errorCause,
|
|
238
|
-
headers,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
421
|
}
|
|
243
422
|
|
|
244
|
-
|
|
245
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING, {
|
|
246
|
-
correlationId: meeting.correlationId,
|
|
247
|
-
messageType: 'TURN_DISCOVERY_RESPONSE',
|
|
248
|
-
isMultistream: meeting.isMultistream,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return turnDiscoveryResponse;
|
|
423
|
+
return this.handleTurnDiscoveryHttpResponse(meeting, response);
|
|
253
424
|
});
|
|
254
425
|
}
|
|
255
426
|
|
|
@@ -261,7 +432,14 @@ export default class TurnDiscovery {
|
|
|
261
432
|
* @returns {Promise}
|
|
262
433
|
*/
|
|
263
434
|
sendRoapOK(meeting: Meeting) {
|
|
264
|
-
LoggerProxy.logger.info(
|
|
435
|
+
LoggerProxy.logger.info(
|
|
436
|
+
'Roap:turnDiscovery#sendRoapOK --> TURN discovery response requires OK, sending it...'
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_REQUIRES_OK, {
|
|
440
|
+
correlation_id: meeting.correlationId,
|
|
441
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
442
|
+
});
|
|
265
443
|
|
|
266
444
|
return this.roapRequest.sendRoap({
|
|
267
445
|
roapMessage: {
|
|
@@ -284,7 +462,7 @@ export default class TurnDiscovery {
|
|
|
284
462
|
* @param {Meeting} meeting
|
|
285
463
|
* @returns {Promise<string>} Promise with empty string if reachability is not skipped or a reason if it is skipped
|
|
286
464
|
*/
|
|
287
|
-
private async getSkipReason(meeting: Meeting): Promise<
|
|
465
|
+
private async getSkipReason(meeting: Meeting): Promise<TurnDiscoverySkipReason> {
|
|
288
466
|
const isAnyPublicClusterReachable =
|
|
289
467
|
// @ts-ignore - fix type
|
|
290
468
|
await meeting.webex.meetings.reachability.isAnyPublicClusterReachable();
|
|
@@ -294,10 +472,10 @@ export default class TurnDiscovery {
|
|
|
294
472
|
'Roap:turnDiscovery#getSkipReason --> reachability has not failed, skipping TURN discovery'
|
|
295
473
|
);
|
|
296
474
|
|
|
297
|
-
return
|
|
475
|
+
return TurnDiscoverySkipReason.reachability;
|
|
298
476
|
}
|
|
299
477
|
|
|
300
|
-
return
|
|
478
|
+
return undefined;
|
|
301
479
|
}
|
|
302
480
|
|
|
303
481
|
/**
|
|
@@ -330,8 +508,12 @@ export default class TurnDiscovery {
|
|
|
330
508
|
* @param {Boolean} [isForced]
|
|
331
509
|
* @returns {Promise}
|
|
332
510
|
*/
|
|
333
|
-
async doTurnDiscovery(
|
|
334
|
-
|
|
511
|
+
async doTurnDiscovery(
|
|
512
|
+
meeting: Meeting,
|
|
513
|
+
isReconnecting?: boolean,
|
|
514
|
+
isForced?: boolean
|
|
515
|
+
): Promise<TurnDiscoveryResult> {
|
|
516
|
+
let turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
335
517
|
|
|
336
518
|
if (!isForced) {
|
|
337
519
|
turnDiscoverySkippedReason = await this.getSkipReason(meeting);
|
|
@@ -345,24 +527,20 @@ export default class TurnDiscovery {
|
|
|
345
527
|
}
|
|
346
528
|
|
|
347
529
|
try {
|
|
348
|
-
const
|
|
530
|
+
const turnDiscoveryResult = await this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting);
|
|
531
|
+
|
|
532
|
+
if (
|
|
533
|
+
turnDiscoveryResult.turnDiscoverySkippedReason !==
|
|
534
|
+
TurnDiscoverySkipReason.missingHttpResponse
|
|
535
|
+
) {
|
|
536
|
+
return turnDiscoveryResult;
|
|
537
|
+
}
|
|
349
538
|
|
|
350
539
|
// if we haven't got the response over http, we need to wait for it to come over the websocket via Mercury
|
|
351
|
-
const {isOkRequired} =
|
|
352
|
-
? await this.handleTurnDiscoveryResponseInHttpResponse(httpResponse)
|
|
353
|
-
: await this.waitForTurnDiscoveryResponse();
|
|
540
|
+
const {isOkRequired} = await this.waitForTurnDiscoveryResponse();
|
|
354
541
|
|
|
355
542
|
if (isOkRequired) {
|
|
356
543
|
await this.sendRoapOK(meeting);
|
|
357
|
-
|
|
358
|
-
LoggerProxy.logger.info(
|
|
359
|
-
'Roap:turnDiscovery#doTurnDiscovery --> TURN discovery response requires OK'
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_REQUIRES_OK, {
|
|
363
|
-
correlation_id: meeting.correlationId,
|
|
364
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
365
|
-
});
|
|
366
544
|
}
|
|
367
545
|
|
|
368
546
|
this.defer = undefined;
|
|
@@ -371,19 +549,7 @@ export default class TurnDiscovery {
|
|
|
371
549
|
|
|
372
550
|
return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};
|
|
373
551
|
} catch (e) {
|
|
374
|
-
|
|
375
|
-
LoggerProxy.logger.info(
|
|
376
|
-
`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${e}`
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE, {
|
|
380
|
-
correlation_id: meeting.correlationId,
|
|
381
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
382
|
-
reason: e.message,
|
|
383
|
-
stack: e.stack,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
return {turnServerInfo: undefined, turnDiscoverySkippedReason: undefined};
|
|
552
|
+
return this.handleTurnDiscoveryFailure(meeting, e);
|
|
387
553
|
}
|
|
388
554
|
}
|
|
389
555
|
}
|