@webex/plugin-meetings 2.29.1 → 2.29.3
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/meeting/index.js +68 -0
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +16 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +2 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/reachability/index.js +17 -13
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +16 -7
- package/dist/reachability/request.js.map +1 -1
- package/package.json +5 -5
- package/src/meeting/index.js +68 -0
- package/src/meeting/request.js +15 -0
- package/src/meeting/util.js +2 -1
- package/src/reachability/index.js +12 -8
- package/src/reachability/{request.js → request.ts} +23 -6
- package/test/unit/spec/meeting/index.js +155 -0
- package/test/unit/spec/meeting/request.js +14 -0
- package/test/unit/spec/meeting/utils.js +2 -0
- package/test/unit/spec/reachability/request.ts +84 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Reachability","webex","reachabilityRequest","ReachabilityRequest","clusterLatencyResults","setup","window","localStorage","removeItem","REACHABILITY","LoggerProxy","logger","error","getClusters","clusters","performReachabilityCheck","results","setItem","cluster","iceServers","udp","tcp","map","url","username","credential","urls","iceCandidatePoolSize","iceTransportPolicy","key","config","peerConnection","RTCPeerConnection","peerConnectionError","log","startTime","begin","clusterList","buildPeerConnectionConfig","createPeerConnection","createOffer","offerToReceiveAudio","description","setLocalDescription","iceGatheringState","catch","iceGatheringStateError","all","then","parseIceResultsToReachabilityResults","reachabilityLatencyResults","logUnreachableClusters","unreachableList","forEach","unreachable","reachable","push","onicegatheringstatechange","COMPLETE","ICE_GATHERING_STATE","iceConnectionState","elapsed","getElapsedTime","setLatencyAndClose","onicecandidate","e","SERVER_REFLEXIVE","candidate","String","type","toLowerCase","ELAPSED","waitTime","resolve","peerConnectionProxy","Proxy","get","target","property","targetMember","bind","set","value","clusterId","Reflect","handleIceGatheringStateChange","handleOnIceCandidate","setTimeout","CLOSED","CONNECTION_STATE","connectionState","list","getUnreachablClusters","iceResults","reachabilityMap","latencyResult","latencyInMilliseconds","toString","length","getLocalSDPForClusters","localSDPData","REACHABLE","UNREACHABLE","resultKey","intialState","close"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-disable class-methods-use-this */\n/* globals window */\nimport _ from 'lodash';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {\n ICE_GATHERING_STATE,\n CONNECTION_STATE,\n REACHABILITY\n} from '../constants';\n\nimport ReachabilityRequest from './request';\n\n/**\n * @class Reachability\n * @export\n */\nexport default class Reachability {\n /**\n * Creates an instance of Reachability.\n * @param {object} webex\n * @memberof Reachability\n */\n constructor(webex) {\n this.webex = webex;\n\n /**\n * internal request object for the server\n * @instance\n * @type {Array}\n * @private\n * @memberof Reachability\n */\n this.reachabilityRequest = new ReachabilityRequest(this.webex);\n\n /**\n * internal object of clusters latency results\n * @instance\n * @type {object}\n * @private\n * @memberof Reachability\n */\n this.clusterLatencyResults = {};\n }\n\n\n /**\n * fetches reachability data\n * @returns {Object} reachability data\n * @public\n * @async\n * @memberof Reachability\n */\n async gatherReachability() {\n this.setup();\n\n // Remove stored reachability results to ensure no stale data\n if (window?.localStorage?.removeItem) {\n window.localStorage.removeItem(REACHABILITY.localStorage);\n }\n else {\n LoggerProxy.logger.error('Reachability:index#gatherReachability --> Error in accessing LocalStorage.');\n\n return {};\n }\n\n // Fetch clusters and measure latency\n try {\n const clusters = await this.reachabilityRequest.getClusters();\n\n // Perform Reachability Check\n const results = await this.performReachabilityCheck(clusters);\n\n window.localStorage.setItem(REACHABILITY.localStorage, JSON.stringify(results));\n\n return results;\n }\n catch (getClusterError) {\n LoggerProxy.logger.error(`Reachability:index#gatherReachability --> Error in calling getClusters(): ${getClusterError}`);\n\n return {};\n }\n }\n\n\n /**\n * Generate peerConnection config settings\n * @param {object} cluster\n * @returns {object} peerConnectionConfig\n * @private\n * @memberof Reachability\n */\n buildPeerConnectionConfig(cluster) {\n const iceServers = _.uniq([\n ...cluster.udp,\n ...cluster.tcp\n ]).map((url) => ({\n username: '',\n credential: '',\n urls: [url]\n }));\n\n return {\n iceServers: [\n ...iceServers\n ],\n iceCandidatePoolSize: '0',\n iceTransportPolicy: 'all'\n };\n }\n\n\n /**\n * Creates an RTCPeerConnection\n * @param {object} cluster\n * @returns {RTCPeerConnection} peerConnection\n * @private\n * @memberof Reachability\n */\n createPeerConnection(cluster) {\n const {key, config} = cluster;\n\n try {\n const peerConnection = new window.RTCPeerConnection(config);\n\n peerConnection.key = key;\n\n return peerConnection;\n }\n catch (peerConnectionError) {\n LoggerProxy.logger.log(`Reachability:index#getLocalSDPForClusters --> Error creating peerConnection: ${peerConnectionError}`);\n\n return null;\n }\n }\n\n\n /**\n * Gets total elapsed time\n * @param {RTCPeerConnection} peerConnection\n * @returns {Number} Milliseconds\n * @private\n * @memberof Reachability\n */\n getElapsedTime(peerConnection) {\n const startTime = peerConnection.begin;\n\n delete peerConnection.begin;\n\n return Date.now() - startTime;\n }\n\n\n /**\n * creates offer and generates localSDP\n * @param {object} clusterList cluster List\n * @returns {Promise} Reachability latency results\n * @private\n * @memberof Reachability\n */\n getLocalSDPForClusters(clusterList) {\n let clusters = [...Object.keys(clusterList)];\n\n clusters = clusters.map(async (key) => {\n const cluster = clusterList[key];\n const config = this.buildPeerConnectionConfig(cluster);\n const peerConnection = this.createPeerConnection({key, config});\n const description = await peerConnection.createOffer({offerToReceiveAudio: true});\n\n peerConnection.begin = Date.now();\n peerConnection.setLocalDescription(description);\n\n return this.iceGatheringState(peerConnection)\n .catch((iceGatheringStateError) => {\n LoggerProxy.logger.log(`Reachability:index#getLocalSDPForClusters --> Error in getLocalSDP : ${iceGatheringStateError}`);\n });\n });\n\n return Promise.all(clusters)\n .then(this.parseIceResultsToReachabilityResults)\n .then((reachabilityLatencyResults) => {\n this.logUnreachableClusters();\n\n // return results\n return reachabilityLatencyResults;\n });\n }\n\n\n /**\n * Get list of all unreachable clusters\n * @returns {array} Unreachable clusters\n * @private\n * @memberof Reachability\n */\n getUnreachablClusters() {\n const unreachableList = [];\n const clusters = this.clusterLatencyResults;\n\n Object.keys(clusters).forEach((key) => {\n const cluster = clusters[key];\n\n if (cluster.unreachable && !cluster.reachable) {\n unreachableList.push(key);\n }\n });\n\n return unreachableList;\n }\n\n\n /**\n * Attach an event handler for the icegatheringstatechange\n * event and measure latency.\n * @param {RTCPeerConnection} peerConnection\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n handleIceGatheringStateChange(peerConnection) {\n peerConnection.onicegatheringstatechange = () => {\n const {COMPLETE} = ICE_GATHERING_STATE;\n\n if (peerConnection.iceConnectionState === COMPLETE) {\n const elapsed = this.getElapsedTime(peerConnection);\n\n LoggerProxy.logger.log(`Reachability:index#onIceGatheringStateChange --> Successfully pinged ${peerConnection.key}:`, elapsed);\n this.setLatencyAndClose(peerConnection, elapsed);\n }\n };\n }\n\n\n /**\n * Attach an event handler for the icecandidate\n * event and measure latency.\n * @param {RTCPeerConnection} peerConnection\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n handleOnIceCandidate(peerConnection) {\n peerConnection.onicecandidate = (e) => {\n const SERVER_REFLEXIVE = 'srflx';\n\n if (e.candidate && String(e.candidate.type).toLowerCase() === SERVER_REFLEXIVE) {\n const elapsed = this.getElapsedTime(peerConnection);\n\n LoggerProxy.logger.log(`Reachability:index#onIceCandidate --> Successfully pinged ${peerConnection.key}:`, elapsed);\n this.setLatencyAndClose(peerConnection, elapsed);\n }\n };\n }\n\n\n /**\n * An event handler on an RTCPeerConnection when the state of the ICE\n * candidate gathering process changes. Used to measure connection\n * speed.\n * @private\n * @param {RTCPeerConnection} peerConnection\n * @returns {Promise}\n */\n iceGatheringState(peerConnection) {\n const ELAPSED = 'elapsed';\n const waitTime = 5e3;\n\n return new Promise((resolve) => {\n const peerConnectionProxy = new window.Proxy(peerConnection, {\n get(target, property) {\n const targetMember = target[property];\n\n if (typeof (targetMember) === 'function') {\n return targetMember.bind(target);\n }\n\n return targetMember;\n },\n set: (target, property, value) => {\n // only intercept elapsed property\n if (property === ELAPSED) {\n resolve({clusterId: peerConnection.key, elapsed: value});\n\n return true;\n }\n\n // pass thru\n return window.Reflect.set(target, property, value);\n }\n });\n\n // Using peerConnection proxy so handle functions below\n // won't be coupled to our promise implementation\n this.handleIceGatheringStateChange(peerConnectionProxy);\n this.handleOnIceCandidate(peerConnectionProxy);\n\n // Set maximum timeout\n window.setTimeout(() => {\n const CLOSED = {CONNECTION_STATE};\n\n // Close any open peerConnections\n if (peerConnectionProxy.connectionState !== CLOSED) {\n this.setLatencyAndClose(peerConnectionProxy, null);\n }\n }, waitTime);\n });\n }\n\n\n /**\n * Make a log of unreachable clusters.\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n logUnreachableClusters() {\n const list = this.getUnreachablClusters();\n\n list.forEach((cluster) => {\n LoggerProxy.logger.log(`Reachability:index#getLocalSDPForClusters --> No ice candidate for ${cluster}.`);\n });\n }\n\n\n /**\n * Calculates time to establish connection\n * @param {array} iceResults iceResults\n * @returns {object} reachabilityMap\n * @private\n * @memberof Reachability\n */\n parseIceResultsToReachabilityResults(iceResults) {\n const reachabilityMap = {};\n\n iceResults.forEach(({clusterId, elapsed}) => {\n let latencyResult;\n\n if (elapsed === null) {\n latencyResult = {reachable: 'false'};\n }\n else {\n latencyResult = {\n reachable: 'true',\n latencyInMilliseconds: (elapsed).toString()\n };\n }\n\n reachabilityMap[clusterId] = {\n udp: latencyResult,\n tcp: latencyResult\n };\n });\n\n return reachabilityMap;\n }\n\n\n /**\n * fetches reachability data\n * @param {object} clusterList\n * @returns {Promise<localSDPData>} reachability check results\n * @private\n * @memberof Reachability\n */\n performReachabilityCheck(clusterList) {\n if (!clusterList || !Object.keys(clusterList).length) {\n return Promise.resolve({});\n }\n\n return new Promise((resolve) => {\n this.getLocalSDPForClusters(clusterList)\n .then((localSDPData) => {\n if (!localSDPData || !Object.keys(localSDPData).length) {\n // TODO: handle the error condition properly and try retry\n LoggerProxy.logger.log('Reachability:index#performReachabilityCheck --> Local SDP is empty or has missing elements..returning');\n resolve({});\n }\n else {\n resolve(localSDPData);\n }\n })\n .catch((error) => {\n LoggerProxy.logger.error(`Reachability:index#performReachabilityCheck --> Error in getLocalSDPForClusters: ${error}`);\n resolve({});\n });\n });\n }\n\n\n /**\n * Records latency and closes the peerConnection\n * @param {RTCPeerConnection} peerConnection\n * @param {number} elapsed Latency in milliseconds\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n setLatencyAndClose(peerConnection, elapsed) {\n const REACHABLE = 'reachable';\n const UNREACHABLE = 'unreachable';\n const {CLOSED} = CONNECTION_STATE;\n const {key} = peerConnection;\n const resultKey = elapsed === null ? UNREACHABLE : REACHABLE;\n const intialState = {[REACHABLE]: 0, [UNREACHABLE]: 0};\n\n if (peerConnection.connectionState === CLOSED) {\n LoggerProxy.logger.log(`Reachability:index#setLatencyAndClose --> Attempting to set latency of ${elapsed} on closed peerConnection.`);\n\n return;\n }\n\n this.clusterLatencyResults[key] = this.clusterLatencyResults[key] || intialState;\n this.clusterLatencyResults[key][resultKey] += 1;\n\n // Set to null in case this fired from\n // an event other than onIceCandidate\n peerConnection.onicecandidate = null;\n peerConnection.close();\n peerConnection.elapsed = elapsed;\n }\n\n\n /**\n * utility function\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n setup() {\n this.clusterLatencyResults = {};\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["DEFAULT_TIMEOUT","VIDEO_MESH_TIMEOUT","Reachability","webex","reachabilityRequest","ReachabilityRequest","clusterLatencyResults","setup","window","localStorage","removeItem","REACHABILITY","LoggerProxy","logger","error","getClusters","clusters","performReachabilityCheck","results","setItem","log","cluster","iceServers","udp","tcp","map","url","username","credential","urls","iceCandidatePoolSize","iceTransportPolicy","key","config","peerConnection","RTCPeerConnection","peerConnectionError","startTime","begin","clusterList","buildPeerConnectionConfig","createPeerConnection","createOffer","offerToReceiveAudio","description","setLocalDescription","iceGatheringState","isVideoMesh","catch","iceGatheringStateError","all","then","parseIceResultsToReachabilityResults","reachabilityLatencyResults","logUnreachableClusters","unreachableList","forEach","unreachable","reachable","push","onicegatheringstatechange","COMPLETE","ICE_GATHERING_STATE","iceConnectionState","elapsed","getElapsedTime","setLatencyAndClose","onicecandidate","e","SERVER_REFLEXIVE","candidate","String","type","toLowerCase","timeout","ELAPSED","resolve","peerConnectionProxy","Proxy","get","target","property","targetMember","bind","set","value","clusterId","Reflect","handleIceGatheringStateChange","handleOnIceCandidate","setTimeout","CLOSED","CONNECTION_STATE","connectionState","list","getUnreachablClusters","iceResults","reachabilityMap","latencyResult","latencyInMilliseconds","toString","length","getLocalSDPForClusters","localSDPData","REACHABLE","UNREACHABLE","resultKey","intialState","close"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-disable class-methods-use-this */\n/* globals window */\nimport _ from 'lodash';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {\n ICE_GATHERING_STATE,\n CONNECTION_STATE,\n REACHABILITY\n} from '../constants';\n\nimport ReachabilityRequest from './request';\n\nconst DEFAULT_TIMEOUT = 3000;\nconst VIDEO_MESH_TIMEOUT = 1000;\n\n/**\n * @class Reachability\n * @export\n */\nexport default class Reachability {\n /**\n * Creates an instance of Reachability.\n * @param {object} webex\n * @memberof Reachability\n */\n constructor(webex) {\n this.webex = webex;\n\n /**\n * internal request object for the server\n * @instance\n * @type {Array}\n * @private\n * @memberof Reachability\n */\n this.reachabilityRequest = new ReachabilityRequest(this.webex);\n\n /**\n * internal object of clusters latency results\n * @instance\n * @type {object}\n * @private\n * @memberof Reachability\n */\n this.clusterLatencyResults = {};\n }\n\n\n /**\n * fetches reachability data\n * @returns {Object} reachability data\n * @public\n * @async\n * @memberof Reachability\n */\n async gatherReachability() {\n this.setup();\n\n // Remove stored reachability results to ensure no stale data\n if (window?.localStorage?.removeItem) {\n window.localStorage.removeItem(REACHABILITY.localStorage);\n }\n else {\n LoggerProxy.logger.error('Reachability:index#gatherReachability --> Error in accessing LocalStorage.');\n\n return {};\n }\n\n // Fetch clusters and measure latency\n try {\n const clusters = await this.reachabilityRequest.getClusters();\n\n // Perform Reachability Check\n const results = await this.performReachabilityCheck(clusters);\n\n window.localStorage.setItem(REACHABILITY.localStorage, JSON.stringify(results));\n\n LoggerProxy.logger.log('Reachability:index#gatherReachability --> Reachability checks completed');\n\n return results;\n }\n catch (getClusterError) {\n LoggerProxy.logger.error(`Reachability:index#gatherReachability --> Error in calling getClusters(): ${getClusterError}`);\n\n return {};\n }\n }\n\n\n /**\n * Generate peerConnection config settings\n * @param {object} cluster\n * @returns {object} peerConnectionConfig\n * @private\n * @memberof Reachability\n */\n buildPeerConnectionConfig(cluster) {\n const iceServers = _.uniq([\n ...cluster.udp,\n ...cluster.tcp\n ]).map((url) => ({\n username: '',\n credential: '',\n urls: [url]\n }));\n\n return {\n iceServers: [\n ...iceServers\n ],\n iceCandidatePoolSize: '0',\n iceTransportPolicy: 'all'\n };\n }\n\n\n /**\n * Creates an RTCPeerConnection\n * @param {object} cluster\n * @returns {RTCPeerConnection} peerConnection\n * @private\n * @memberof Reachability\n */\n createPeerConnection(cluster) {\n const {key, config} = cluster;\n\n try {\n const peerConnection = new window.RTCPeerConnection(config);\n\n peerConnection.key = key;\n\n return peerConnection;\n }\n catch (peerConnectionError) {\n LoggerProxy.logger.log(`Reachability:index#createPeerConnection --> Error creating peerConnection: ${peerConnectionError}`);\n\n return null;\n }\n }\n\n\n /**\n * Gets total elapsed time\n * @param {RTCPeerConnection} peerConnection\n * @returns {Number} Milliseconds\n * @private\n * @memberof Reachability\n */\n getElapsedTime(peerConnection) {\n const startTime = peerConnection.begin;\n\n delete peerConnection.begin;\n\n return Date.now() - startTime;\n }\n\n\n /**\n * creates offer and generates localSDP\n * @param {object} clusterList cluster List\n * @returns {Promise} Reachability latency results\n * @private\n * @memberof Reachability\n */\n getLocalSDPForClusters(clusterList) {\n let clusters = [...Object.keys(clusterList)];\n\n clusters = clusters.map(async (key) => {\n const cluster = clusterList[key];\n const config = this.buildPeerConnectionConfig(cluster);\n const peerConnection = this.createPeerConnection({key, config});\n const description = await peerConnection.createOffer({offerToReceiveAudio: true});\n\n peerConnection.begin = Date.now();\n peerConnection.setLocalDescription(description);\n\n return this.iceGatheringState(peerConnection, cluster.isVideoMesh ? VIDEO_MESH_TIMEOUT : DEFAULT_TIMEOUT)\n .catch((iceGatheringStateError) => {\n LoggerProxy.logger.log(`Reachability:index#getLocalSDPForClusters --> Error in getLocalSDP : ${iceGatheringStateError}`);\n });\n });\n\n return Promise.all(clusters)\n .then(this.parseIceResultsToReachabilityResults)\n .then((reachabilityLatencyResults) => {\n this.logUnreachableClusters();\n\n // return results\n return reachabilityLatencyResults;\n });\n }\n\n\n /**\n * Get list of all unreachable clusters\n * @returns {array} Unreachable clusters\n * @private\n * @memberof Reachability\n */\n getUnreachablClusters() {\n const unreachableList = [];\n const clusters = this.clusterLatencyResults;\n\n Object.keys(clusters).forEach((key) => {\n const cluster = clusters[key];\n\n if (cluster.unreachable && !cluster.reachable) {\n unreachableList.push(key);\n }\n });\n\n return unreachableList;\n }\n\n\n /**\n * Attach an event handler for the icegatheringstatechange\n * event and measure latency.\n * @param {RTCPeerConnection} peerConnection\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n handleIceGatheringStateChange(peerConnection) {\n peerConnection.onicegatheringstatechange = () => {\n const {COMPLETE} = ICE_GATHERING_STATE;\n\n if (peerConnection.iceConnectionState === COMPLETE) {\n const elapsed = this.getElapsedTime(peerConnection);\n\n LoggerProxy.logger.log(`Reachability:index#onIceGatheringStateChange --> Successfully pinged ${peerConnection.key}:`, elapsed);\n this.setLatencyAndClose(peerConnection, elapsed);\n }\n };\n }\n\n\n /**\n * Attach an event handler for the icecandidate\n * event and measure latency.\n * @param {RTCPeerConnection} peerConnection\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n handleOnIceCandidate(peerConnection) {\n peerConnection.onicecandidate = (e) => {\n const SERVER_REFLEXIVE = 'srflx';\n\n if (e.candidate && String(e.candidate.type).toLowerCase() === SERVER_REFLEXIVE) {\n const elapsed = this.getElapsedTime(peerConnection);\n\n LoggerProxy.logger.log(`Reachability:index#onIceCandidate --> Successfully pinged ${peerConnection.key}:`, elapsed);\n this.setLatencyAndClose(peerConnection, elapsed);\n }\n };\n }\n\n /**\n * An event handler on an RTCPeerConnection when the state of the ICE\n * candidate gathering process changes. Used to measure connection\n * speed.\n * @private\n * @param {RTCPeerConnection} peerConnection\n * @param {number} timeout\n * @returns {Promise}\n */\n iceGatheringState(peerConnection, timeout) {\n const ELAPSED = 'elapsed';\n\n return new Promise((resolve) => {\n const peerConnectionProxy = new window.Proxy(peerConnection, {\n get(target, property) {\n const targetMember = target[property];\n\n if (typeof (targetMember) === 'function') {\n return targetMember.bind(target);\n }\n\n return targetMember;\n },\n set: (target, property, value) => {\n // only intercept elapsed property\n if (property === ELAPSED) {\n resolve({clusterId: peerConnection.key, elapsed: value});\n\n return true;\n }\n\n // pass thru\n return window.Reflect.set(target, property, value);\n }\n });\n\n // Using peerConnection proxy so handle functions below\n // won't be coupled to our promise implementation\n this.handleIceGatheringStateChange(peerConnectionProxy);\n this.handleOnIceCandidate(peerConnectionProxy);\n\n // Set maximum timeout\n window.setTimeout(() => {\n const {CLOSED} = CONNECTION_STATE;\n\n // Close any open peerConnections\n if (peerConnectionProxy.connectionState !== CLOSED) {\n this.setLatencyAndClose(peerConnectionProxy, null);\n }\n }, timeout);\n });\n }\n\n\n /**\n * Make a log of unreachable clusters.\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n logUnreachableClusters() {\n const list = this.getUnreachablClusters();\n\n list.forEach((cluster) => {\n LoggerProxy.logger.log(`Reachability:index#logUnreachableClusters --> No ice candidate for ${cluster}.`);\n });\n }\n\n\n /**\n * Calculates time to establish connection\n * @param {array} iceResults iceResults\n * @returns {object} reachabilityMap\n * @private\n * @memberof Reachability\n */\n parseIceResultsToReachabilityResults(iceResults) {\n const reachabilityMap = {};\n\n iceResults.forEach(({clusterId, elapsed}) => {\n let latencyResult;\n\n if (elapsed === null) {\n latencyResult = {reachable: 'false'};\n }\n else {\n latencyResult = {\n reachable: 'true',\n latencyInMilliseconds: (elapsed).toString()\n };\n }\n\n reachabilityMap[clusterId] = {\n udp: latencyResult,\n tcp: latencyResult\n };\n });\n\n return reachabilityMap;\n }\n\n\n /**\n * fetches reachability data\n * @param {object} clusterList\n * @returns {Promise<localSDPData>} reachability check results\n * @private\n * @memberof Reachability\n */\n performReachabilityCheck(clusterList) {\n if (!clusterList || !Object.keys(clusterList).length) {\n return Promise.resolve({});\n }\n\n return new Promise((resolve) => {\n this.getLocalSDPForClusters(clusterList)\n .then((localSDPData) => {\n if (!localSDPData || !Object.keys(localSDPData).length) {\n // TODO: handle the error condition properly and try retry\n LoggerProxy.logger.log('Reachability:index#performReachabilityCheck --> Local SDP is empty or has missing elements..returning');\n resolve({});\n }\n else {\n resolve(localSDPData);\n }\n })\n .catch((error) => {\n LoggerProxy.logger.error(`Reachability:index#performReachabilityCheck --> Error in getLocalSDPForClusters: ${error}`);\n resolve({});\n });\n });\n }\n\n\n /**\n * Records latency and closes the peerConnection\n * @param {RTCPeerConnection} peerConnection\n * @param {number} elapsed Latency in milliseconds\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n setLatencyAndClose(peerConnection, elapsed) {\n const REACHABLE = 'reachable';\n const UNREACHABLE = 'unreachable';\n const {CLOSED} = CONNECTION_STATE;\n const {key} = peerConnection;\n const resultKey = elapsed === null ? UNREACHABLE : REACHABLE;\n const intialState = {[REACHABLE]: 0, [UNREACHABLE]: 0};\n\n if (peerConnection.connectionState === CLOSED) {\n LoggerProxy.logger.log(`Reachability:index#setLatencyAndClose --> Attempting to set latency of ${elapsed} on closed peerConnection.`);\n\n return;\n }\n\n this.clusterLatencyResults[key] = this.clusterLatencyResults[key] || intialState;\n this.clusterLatencyResults[key][resultKey] += 1;\n\n // Set to null in case this fired from\n // an event other than onIceCandidate\n peerConnection.onicecandidate = null;\n peerConnection.close();\n peerConnection.elapsed = elapsed;\n }\n\n\n /**\n * utility function\n * @returns {undefined}\n * @private\n * @memberof Reachability\n */\n setup() {\n this.clusterLatencyResults = {};\n }\n}\n"],"mappings":"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"}
|
|
@@ -10,6 +10,8 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
10
10
|
|
|
11
11
|
exports.default = void 0;
|
|
12
12
|
|
|
13
|
+
var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
|
|
14
|
+
|
|
13
15
|
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
14
16
|
|
|
15
17
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
|
|
@@ -23,18 +25,18 @@ var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy")
|
|
|
23
25
|
var _constants = require("../constants");
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
|
-
* @class
|
|
28
|
+
* @class ReachabilityRequest
|
|
27
29
|
*/
|
|
28
|
-
var
|
|
30
|
+
var ReachabilityRequest = /*#__PURE__*/(0, _createClass2.default)(
|
|
29
31
|
/**
|
|
30
|
-
* Creates an instance of
|
|
32
|
+
* Creates an instance of ReachabilityRequest.
|
|
31
33
|
* @param {object} webex
|
|
32
|
-
* @memberof
|
|
34
|
+
* @memberof ReachabilityRequest
|
|
33
35
|
*/
|
|
34
|
-
function
|
|
36
|
+
function ReachabilityRequest(webex) {
|
|
35
37
|
var _this = this;
|
|
36
38
|
|
|
37
|
-
(0, _classCallCheck2.default)(this,
|
|
39
|
+
(0, _classCallCheck2.default)(this, ReachabilityRequest);
|
|
38
40
|
(0, _defineProperty2.default)(this, "getClusters", function () {
|
|
39
41
|
return _this.webex.request({
|
|
40
42
|
method: _constants.HTTP_VERBS.GET,
|
|
@@ -43,6 +45,11 @@ function RechabilityRequest(webex) {
|
|
|
43
45
|
resource: _constants.RESOURCE.CLUSTERS
|
|
44
46
|
}).then(function (res) {
|
|
45
47
|
var clusters = res.body.clusters;
|
|
48
|
+
(0, _keys.default)(clusters).forEach(function (key) {
|
|
49
|
+
var _res$body$clusterClas, _res$body$clusterClas2;
|
|
50
|
+
|
|
51
|
+
clusters[key].isVideoMesh = (_res$body$clusterClas = res.body.clusterClasses) === null || _res$body$clusterClas === void 0 ? void 0 : (_res$body$clusterClas2 = _res$body$clusterClas.hybridMedia) === null || _res$body$clusterClas2 === void 0 ? void 0 : _res$body$clusterClas2.includes(key);
|
|
52
|
+
});
|
|
46
53
|
|
|
47
54
|
_loggerProxy.default.logger.log("Reachability:request#getClusters --> get clusters successful:".concat((0, _stringify.default)(clusters)));
|
|
48
55
|
|
|
@@ -68,9 +75,11 @@ function RechabilityRequest(webex) {
|
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
70
77
|
* gets the cluster information
|
|
78
|
+
*
|
|
79
|
+
* @param {boolean} includeVideoMesh whether to include the video mesh clusters in the result or not
|
|
71
80
|
* @returns {Promise}
|
|
72
81
|
*/
|
|
73
82
|
);
|
|
74
|
-
var _default =
|
|
83
|
+
var _default = ReachabilityRequest;
|
|
75
84
|
exports.default = _default;
|
|
76
85
|
//# sourceMappingURL=request.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["ReachabilityRequest","webex","request","method","HTTP_VERBS","GET","shouldRefreshAccessToken","api","API","CALLIOPEDISCOVERY","resource","RESOURCE","CLUSTERS","then","res","clusters","body","forEach","key","isVideoMesh","clusterClasses","hybridMedia","includes","LoggerProxy","logger","log","localSDPList","POST","REACHABILITY","offers"],"sources":["request.ts"],"sourcesContent":["import LoggerProxy from '../common/logs/logger-proxy';\nimport {\n HTTP_VERBS,\n RESOURCE,\n API\n} from '../constants';\n\nexport interface ClusterNode {\n isVideoMesh: boolean;\n udp: Array<string>;\n tcp: Array<string>;\n xtls: Array<string>;\n}\n\nexport type ClusterList = {\n [key:string]: ClusterNode;\n}\n\n/**\n * @class ReachabilityRequest\n */\nclass ReachabilityRequest {\n /**\n * Creates an instance of ReachabilityRequest.\n * @param {object} webex\n * @memberof ReachabilityRequest\n */\n constructor(webex) {\n this.webex = webex;\n }\n\n /**\n * gets the cluster information\n *\n * @param {boolean} includeVideoMesh whether to include the video mesh clusters in the result or not\n * @returns {Promise}\n */\n getClusters = (): Promise<ClusterList> => this.webex.request({\n method: HTTP_VERBS.GET,\n shouldRefreshAccessToken: false,\n api: API.CALLIOPEDISCOVERY,\n resource: RESOURCE.CLUSTERS\n })\n .then((res) => {\n const {clusters} = res.body;\n\n Object.keys(clusters).forEach((key) => {\n clusters[key].isVideoMesh = res.body.clusterClasses?.hybridMedia?.includes(key);\n });\n\n LoggerProxy.logger.log(`Reachability:request#getClusters --> get clusters successful:${JSON.stringify(clusters)}`);\n\n return clusters;\n });\n\n /**\n * gets remote SDP For Clusters\n * @param {Object} localSDPList localSDPs for the cluster\n * @returns {Object}\n */\n remoteSDPForClusters = (localSDPList) => this.webex.request({\n method: HTTP_VERBS.POST,\n shouldRefreshAccessToken: false,\n api: API.CALLIOPEDISCOVERY,\n resource: RESOURCE.REACHABILITY,\n body: {offers: localSDPList}\n })\n .then((res) => {\n LoggerProxy.logger.log('Reachability:request#remoteSDPForClusters --> Remote SDPs got succcessfully');\n\n return res.body;\n });\n}\n\nexport default ReachabilityRequest;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;;AACA;;AAiBA;AACA;AACA;IACMA,mB;AACJ;AACF;AACA;AACA;AACA;AACE,6BAAYC,KAAZ,EAAmB;EAAA;;EAAA;EAAA,mDAUL;IAAA,OAA4B,KAAI,CAACA,KAAL,CAAWC,OAAX,CAAmB;MAC3DC,MAAM,EAAEC,qBAAA,CAAWC,GADwC;MAE3DC,wBAAwB,EAAE,KAFiC;MAG3DC,GAAG,EAAEC,cAAA,CAAIC,iBAHkD;MAI3DC,QAAQ,EAAEC,mBAAA,CAASC;IAJwC,CAAnB,EAMvCC,IANuC,CAMlC,UAACC,GAAD,EAAS;MACb,IAAOC,QAAP,GAAmBD,GAAG,CAACE,IAAvB,CAAOD,QAAP;MAEA,mBAAYA,QAAZ,EAAsBE,OAAtB,CAA8B,UAACC,GAAD,EAAS;QAAA;;QACrCH,QAAQ,CAACG,GAAD,CAAR,CAAcC,WAAd,4BAA4BL,GAAG,CAACE,IAAJ,CAASI,cAArC,oFAA4B,sBAAyBC,WAArD,2DAA4B,uBAAsCC,QAAtC,CAA+CJ,GAA/C,CAA5B;MACD,CAFD;;MAIAK,oBAAA,CAAYC,MAAZ,CAAmBC,GAAnB,wEAAuF,wBAAeV,QAAf,CAAvF;;MAEA,OAAOA,QAAP;IACD,CAhBuC,CAA5B;EAAA,CAVK;EAAA,4DAiCI,UAACW,YAAD;IAAA,OAAkB,KAAI,CAACzB,KAAL,CAAWC,OAAX,CAAmB;MAC1DC,MAAM,EAAEC,qBAAA,CAAWuB,IADuC;MAE1DrB,wBAAwB,EAAE,KAFgC;MAG1DC,GAAG,EAAEC,cAAA,CAAIC,iBAHiD;MAI1DC,QAAQ,EAAEC,mBAAA,CAASiB,YAJuC;MAK1DZ,IAAI,EAAE;QAACa,MAAM,EAAEH;MAAT;IALoD,CAAnB,EAOtCb,IAPsC,CAOjC,UAACC,GAAD,EAAS;MACbS,oBAAA,CAAYC,MAAZ,CAAmBC,GAAnB,CAAuB,6EAAvB;;MAEA,OAAOX,GAAG,CAACE,IAAX;IACD,CAXsC,CAAlB;EAAA,CAjCJ;EACjB,KAAKf,KAAL,GAAaA,KAAb;AACD;AAED;AACF;AACA;AACA;AACA;AACA;;eAsCeD,mB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "2.29.
|
|
3
|
+
"version": "2.29.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"contributors": [
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime-corejs2": "^7.14.8",
|
|
27
|
-
"@webex/webex-core": "2.29.
|
|
28
|
-
"@webex/internal-plugin-mercury": "2.29.
|
|
29
|
-
"@webex/internal-plugin-conversation": "2.29.
|
|
27
|
+
"@webex/webex-core": "2.29.3",
|
|
28
|
+
"@webex/internal-plugin-mercury": "2.29.3",
|
|
29
|
+
"@webex/internal-plugin-conversation": "2.29.3",
|
|
30
30
|
"webrtc-adapter": "^7.7.0",
|
|
31
31
|
"lodash": "^4.17.21",
|
|
32
32
|
"uuid": "^3.3.2",
|
|
33
33
|
"global": "^4.4.0",
|
|
34
34
|
"ip-anonymize": "^0.1.0",
|
|
35
|
-
"@webex/common": "2.29.
|
|
35
|
+
"@webex/common": "2.29.3",
|
|
36
36
|
"bowser": "^2.11.0",
|
|
37
37
|
"sdp-transform": "^2.12.0",
|
|
38
38
|
"btoa": "^1.2.1",
|
package/src/meeting/index.js
CHANGED
|
@@ -888,6 +888,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
888
888
|
*/
|
|
889
889
|
this.meetingInfoFailureReason = undefined;
|
|
890
890
|
|
|
891
|
+
/**
|
|
892
|
+
* Repeating timer used to send keepAlives when in lobby
|
|
893
|
+
* @instance
|
|
894
|
+
* @type {String}
|
|
895
|
+
* @private
|
|
896
|
+
* @memberof Meeting
|
|
897
|
+
*/
|
|
898
|
+
this.keepAliveTimerId = null;
|
|
899
|
+
|
|
891
900
|
this.setUpLocusInfoListeners();
|
|
892
901
|
this.locusInfo.init(attrs.locus ? attrs.locus : {});
|
|
893
902
|
this.hasJoinedOnce = false;
|
|
@@ -1989,6 +1998,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1989
1998
|
});
|
|
1990
1999
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_UNADMITTED_GUEST, (payload) => {
|
|
1991
2000
|
if (payload) {
|
|
2001
|
+
this.startKeepAlive();
|
|
2002
|
+
|
|
1992
2003
|
Trigger.trigger(
|
|
1993
2004
|
this,
|
|
1994
2005
|
{
|
|
@@ -2008,6 +2019,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2008
2019
|
}
|
|
2009
2020
|
});
|
|
2010
2021
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, (payload) => {
|
|
2022
|
+
this.stopKeepAlive();
|
|
2023
|
+
|
|
2011
2024
|
if (payload) {
|
|
2012
2025
|
Trigger.trigger(
|
|
2013
2026
|
this,
|
|
@@ -5902,4 +5915,59 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5902
5915
|
failure: `${LOG_HEADER} disable bnr failure, `
|
|
5903
5916
|
});
|
|
5904
5917
|
}
|
|
5918
|
+
|
|
5919
|
+
/**
|
|
5920
|
+
* starts keepAlives being sent
|
|
5921
|
+
* @returns {void}
|
|
5922
|
+
* @private
|
|
5923
|
+
* @memberof Meeting
|
|
5924
|
+
*/
|
|
5925
|
+
startKeepAlive = () => {
|
|
5926
|
+
if (this.keepAliveTimerId) {
|
|
5927
|
+
LoggerProxy.logger.warn('Meeting:index#startKeepAlive --> keepAlive not started: keepAliveTimerId already exists');
|
|
5928
|
+
|
|
5929
|
+
return;
|
|
5930
|
+
}
|
|
5931
|
+
if (!this.joinedWith?.keepAliveUrl) {
|
|
5932
|
+
LoggerProxy.logger.warn('Meeting:index#startKeepAlive --> keepAlive not started: no keepAliveUrl');
|
|
5933
|
+
|
|
5934
|
+
return;
|
|
5935
|
+
}
|
|
5936
|
+
if (!this.joinedWith?.keepAliveSecs) {
|
|
5937
|
+
LoggerProxy.logger.warn('Meeting:index#startKeepAlive --> keepAlive not started: no keepAliveSecs');
|
|
5938
|
+
|
|
5939
|
+
return;
|
|
5940
|
+
}
|
|
5941
|
+
if (this.joinedWith.keepAliveSecs <= 1) {
|
|
5942
|
+
LoggerProxy.logger.warn('Meeting:index#startKeepAlive --> keepAlive not started: keepAliveSecs <= 1');
|
|
5943
|
+
|
|
5944
|
+
return;
|
|
5945
|
+
}
|
|
5946
|
+
const {keepAliveUrl} = this.joinedWith;
|
|
5947
|
+
const keepAliveInterval = (this.joinedWith.keepAliveSecs - 1) * 750; // taken from UCF
|
|
5948
|
+
|
|
5949
|
+
this.keepAliveTimerId = setInterval(() => {
|
|
5950
|
+
this.meetingRequest.keepAlive({keepAliveUrl})
|
|
5951
|
+
.catch((error) => {
|
|
5952
|
+
LoggerProxy.logger.warn(
|
|
5953
|
+
`Meeting:index#startKeepAlive --> Stopping sending keepAlives to ${keepAliveUrl} after error ${error}`
|
|
5954
|
+
);
|
|
5955
|
+
this.stopKeepAlive();
|
|
5956
|
+
});
|
|
5957
|
+
}, keepAliveInterval);
|
|
5958
|
+
};
|
|
5959
|
+
|
|
5960
|
+
/**
|
|
5961
|
+
* stops keepAlives being sent
|
|
5962
|
+
* @returns {void}
|
|
5963
|
+
* @private
|
|
5964
|
+
* @memberof Meeting
|
|
5965
|
+
*/
|
|
5966
|
+
stopKeepAlive = () => {
|
|
5967
|
+
if (!this.keepAliveTimerId) {
|
|
5968
|
+
return;
|
|
5969
|
+
}
|
|
5970
|
+
clearInterval(this.keepAliveTimerId);
|
|
5971
|
+
this.keepAliveTimerId = null;
|
|
5972
|
+
};
|
|
5905
5973
|
}
|
package/src/meeting/request.js
CHANGED
|
@@ -665,4 +665,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
665
665
|
uri
|
|
666
666
|
});
|
|
667
667
|
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Send a locus keepAlive (used in lobby)
|
|
671
|
+
* @param {Object} options
|
|
672
|
+
* @param {Url} options.keepAliveUrl
|
|
673
|
+
* @returns {Promise}
|
|
674
|
+
*/
|
|
675
|
+
keepAlive({
|
|
676
|
+
keepAliveUrl,
|
|
677
|
+
}) {
|
|
678
|
+
return this.request({
|
|
679
|
+
method: HTTP_VERBS.GET,
|
|
680
|
+
uri: keepAliveUrl
|
|
681
|
+
});
|
|
682
|
+
}
|
|
668
683
|
}
|
package/src/meeting/util.js
CHANGED
|
@@ -130,7 +130,8 @@ MeetingUtil.cleanUp = (meeting) => {
|
|
|
130
130
|
meeting.unsetPeerConnections();
|
|
131
131
|
meeting.reconnectionManager.cleanUp();
|
|
132
132
|
})
|
|
133
|
-
.then(() => meeting.roap.stop(meeting.correlationId, meeting.roapSeq))
|
|
133
|
+
.then(() => meeting.roap.stop(meeting.correlationId, meeting.roapSeq))
|
|
134
|
+
.then(() => meeting.stopKeepAlive());
|
|
134
135
|
};
|
|
135
136
|
|
|
136
137
|
MeetingUtil.disconnectPhoneAudio = (meeting, phoneUrl) => {
|
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
|
|
16
16
|
import ReachabilityRequest from './request';
|
|
17
17
|
|
|
18
|
+
const DEFAULT_TIMEOUT = 3000;
|
|
19
|
+
const VIDEO_MESH_TIMEOUT = 1000;
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* @class Reachability
|
|
20
23
|
* @export
|
|
@@ -77,6 +80,8 @@ export default class Reachability {
|
|
|
77
80
|
|
|
78
81
|
window.localStorage.setItem(REACHABILITY.localStorage, JSON.stringify(results));
|
|
79
82
|
|
|
83
|
+
LoggerProxy.logger.log('Reachability:index#gatherReachability --> Reachability checks completed');
|
|
84
|
+
|
|
80
85
|
return results;
|
|
81
86
|
}
|
|
82
87
|
catch (getClusterError) {
|
|
@@ -132,7 +137,7 @@ export default class Reachability {
|
|
|
132
137
|
return peerConnection;
|
|
133
138
|
}
|
|
134
139
|
catch (peerConnectionError) {
|
|
135
|
-
LoggerProxy.logger.log(`Reachability:index#
|
|
140
|
+
LoggerProxy.logger.log(`Reachability:index#createPeerConnection --> Error creating peerConnection: ${peerConnectionError}`);
|
|
136
141
|
|
|
137
142
|
return null;
|
|
138
143
|
}
|
|
@@ -174,7 +179,7 @@ export default class Reachability {
|
|
|
174
179
|
peerConnection.begin = Date.now();
|
|
175
180
|
peerConnection.setLocalDescription(description);
|
|
176
181
|
|
|
177
|
-
return this.iceGatheringState(peerConnection)
|
|
182
|
+
return this.iceGatheringState(peerConnection, cluster.isVideoMesh ? VIDEO_MESH_TIMEOUT : DEFAULT_TIMEOUT)
|
|
178
183
|
.catch((iceGatheringStateError) => {
|
|
179
184
|
LoggerProxy.logger.log(`Reachability:index#getLocalSDPForClusters --> Error in getLocalSDP : ${iceGatheringStateError}`);
|
|
180
185
|
});
|
|
@@ -256,18 +261,17 @@ export default class Reachability {
|
|
|
256
261
|
};
|
|
257
262
|
}
|
|
258
263
|
|
|
259
|
-
|
|
260
264
|
/**
|
|
261
265
|
* An event handler on an RTCPeerConnection when the state of the ICE
|
|
262
266
|
* candidate gathering process changes. Used to measure connection
|
|
263
267
|
* speed.
|
|
264
268
|
* @private
|
|
265
269
|
* @param {RTCPeerConnection} peerConnection
|
|
270
|
+
* @param {number} timeout
|
|
266
271
|
* @returns {Promise}
|
|
267
272
|
*/
|
|
268
|
-
iceGatheringState(peerConnection) {
|
|
273
|
+
iceGatheringState(peerConnection, timeout) {
|
|
269
274
|
const ELAPSED = 'elapsed';
|
|
270
|
-
const waitTime = 5e3;
|
|
271
275
|
|
|
272
276
|
return new Promise((resolve) => {
|
|
273
277
|
const peerConnectionProxy = new window.Proxy(peerConnection, {
|
|
@@ -300,13 +304,13 @@ export default class Reachability {
|
|
|
300
304
|
|
|
301
305
|
// Set maximum timeout
|
|
302
306
|
window.setTimeout(() => {
|
|
303
|
-
const CLOSED =
|
|
307
|
+
const {CLOSED} = CONNECTION_STATE;
|
|
304
308
|
|
|
305
309
|
// Close any open peerConnections
|
|
306
310
|
if (peerConnectionProxy.connectionState !== CLOSED) {
|
|
307
311
|
this.setLatencyAndClose(peerConnectionProxy, null);
|
|
308
312
|
}
|
|
309
|
-
},
|
|
313
|
+
}, timeout);
|
|
310
314
|
});
|
|
311
315
|
}
|
|
312
316
|
|
|
@@ -321,7 +325,7 @@ export default class Reachability {
|
|
|
321
325
|
const list = this.getUnreachablClusters();
|
|
322
326
|
|
|
323
327
|
list.forEach((cluster) => {
|
|
324
|
-
LoggerProxy.logger.log(`Reachability:index#
|
|
328
|
+
LoggerProxy.logger.log(`Reachability:index#logUnreachableClusters --> No ice candidate for ${cluster}.`);
|
|
325
329
|
});
|
|
326
330
|
}
|
|
327
331
|
|
|
@@ -5,14 +5,25 @@ import {
|
|
|
5
5
|
API
|
|
6
6
|
} from '../constants';
|
|
7
7
|
|
|
8
|
+
export interface ClusterNode {
|
|
9
|
+
isVideoMesh: boolean;
|
|
10
|
+
udp: Array<string>;
|
|
11
|
+
tcp: Array<string>;
|
|
12
|
+
xtls: Array<string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ClusterList = {
|
|
16
|
+
[key:string]: ClusterNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
/**
|
|
9
|
-
* @class
|
|
20
|
+
* @class ReachabilityRequest
|
|
10
21
|
*/
|
|
11
|
-
class
|
|
22
|
+
class ReachabilityRequest {
|
|
12
23
|
/**
|
|
13
|
-
* Creates an instance of
|
|
24
|
+
* Creates an instance of ReachabilityRequest.
|
|
14
25
|
* @param {object} webex
|
|
15
|
-
* @memberof
|
|
26
|
+
* @memberof ReachabilityRequest
|
|
16
27
|
*/
|
|
17
28
|
constructor(webex) {
|
|
18
29
|
this.webex = webex;
|
|
@@ -20,9 +31,11 @@ class RechabilityRequest {
|
|
|
20
31
|
|
|
21
32
|
/**
|
|
22
33
|
* gets the cluster information
|
|
34
|
+
*
|
|
35
|
+
* @param {boolean} includeVideoMesh whether to include the video mesh clusters in the result or not
|
|
23
36
|
* @returns {Promise}
|
|
24
37
|
*/
|
|
25
|
-
getClusters = () => this.webex.request({
|
|
38
|
+
getClusters = (): Promise<ClusterList> => this.webex.request({
|
|
26
39
|
method: HTTP_VERBS.GET,
|
|
27
40
|
shouldRefreshAccessToken: false,
|
|
28
41
|
api: API.CALLIOPEDISCOVERY,
|
|
@@ -31,6 +44,10 @@ class RechabilityRequest {
|
|
|
31
44
|
.then((res) => {
|
|
32
45
|
const {clusters} = res.body;
|
|
33
46
|
|
|
47
|
+
Object.keys(clusters).forEach((key) => {
|
|
48
|
+
clusters[key].isVideoMesh = res.body.clusterClasses?.hybridMedia?.includes(key);
|
|
49
|
+
});
|
|
50
|
+
|
|
34
51
|
LoggerProxy.logger.log(`Reachability:request#getClusters --> get clusters successful:${JSON.stringify(clusters)}`);
|
|
35
52
|
|
|
36
53
|
return clusters;
|
|
@@ -55,4 +72,4 @@ class RechabilityRequest {
|
|
|
55
72
|
});
|
|
56
73
|
}
|
|
57
74
|
|
|
58
|
-
export default
|
|
75
|
+
export default ReachabilityRequest;
|
|
@@ -3227,7 +3227,9 @@ describe('plugin-meetings', () => {
|
|
|
3227
3227
|
});
|
|
3228
3228
|
describe('#setUpLocusInfoSelfListener', () => {
|
|
3229
3229
|
it('listens to the self unadmitted guest event', (done) => {
|
|
3230
|
+
meeting.startKeepAlive = sinon.stub();
|
|
3230
3231
|
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_UNADMITTED_GUEST', test1);
|
|
3232
|
+
assert.calledOnceWithExactly(meeting.startKeepAlive);
|
|
3231
3233
|
assert.calledTwice(TriggerProxy.trigger);
|
|
3232
3234
|
assert.calledWith(
|
|
3233
3235
|
TriggerProxy.trigger,
|
|
@@ -3239,7 +3241,9 @@ describe('plugin-meetings', () => {
|
|
|
3239
3241
|
done();
|
|
3240
3242
|
});
|
|
3241
3243
|
it('listens to the self admitted guest event', (done) => {
|
|
3244
|
+
meeting.stopKeepAlive = sinon.stub();
|
|
3242
3245
|
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
|
|
3246
|
+
assert.calledOnceWithExactly(meeting.stopKeepAlive);
|
|
3243
3247
|
assert.calledTwice(TriggerProxy.trigger);
|
|
3244
3248
|
assert.calledWith(
|
|
3245
3249
|
TriggerProxy.trigger,
|
|
@@ -4328,6 +4332,157 @@ describe('plugin-meetings', () => {
|
|
|
4328
4332
|
});
|
|
4329
4333
|
});
|
|
4330
4334
|
});
|
|
4335
|
+
|
|
4336
|
+
describe('#startKeepAlive', () => {
|
|
4337
|
+
let clock;
|
|
4338
|
+
const defaultKeepAliveUrl = 'keep.alive.url';
|
|
4339
|
+
const defaultKeepAliveSecs = 23;
|
|
4340
|
+
const defaultExpectedInterval = (defaultKeepAliveSecs - 1) * 750;
|
|
4341
|
+
|
|
4342
|
+
beforeEach(() => {
|
|
4343
|
+
clock = sinon.useFakeTimers();
|
|
4344
|
+
});
|
|
4345
|
+
afterEach(() => {
|
|
4346
|
+
clock.restore();
|
|
4347
|
+
});
|
|
4348
|
+
|
|
4349
|
+
const progressTime = async (interval) => {
|
|
4350
|
+
await clock.tickAsync(interval);
|
|
4351
|
+
await testUtils.flushPromises();
|
|
4352
|
+
};
|
|
4353
|
+
|
|
4354
|
+
it('startKeepAlive starts the keep alive', async () => {
|
|
4355
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4356
|
+
|
|
4357
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4358
|
+
meeting.joinedWith = {
|
|
4359
|
+
keepAliveUrl: defaultKeepAliveUrl,
|
|
4360
|
+
keepAliveSecs: defaultKeepAliveSecs
|
|
4361
|
+
};
|
|
4362
|
+
meeting.startKeepAlive();
|
|
4363
|
+
assert.isNumber(meeting.keepAliveTimerId.id);
|
|
4364
|
+
await testUtils.flushPromises();
|
|
4365
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4366
|
+
await progressTime(defaultExpectedInterval);
|
|
4367
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
|
|
4368
|
+
await progressTime(defaultExpectedInterval);
|
|
4369
|
+
assert.calledTwice(meeting.meetingRequest.keepAlive);
|
|
4370
|
+
assert.alwaysCalledWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
|
|
4371
|
+
});
|
|
4372
|
+
it('startKeepAlive handles existing keepAliveTimerId', async () => {
|
|
4373
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4374
|
+
logger.warn = sinon.spy();
|
|
4375
|
+
|
|
4376
|
+
meeting.keepAliveTimerId = 7;
|
|
4377
|
+
meeting.joinedWith = {
|
|
4378
|
+
keepAliveUrl: defaultKeepAliveUrl,
|
|
4379
|
+
keepAliveSecs: defaultKeepAliveSecs
|
|
4380
|
+
};
|
|
4381
|
+
meeting.startKeepAlive();
|
|
4382
|
+
assert.equal(meeting.keepAliveTimerId, 7);
|
|
4383
|
+
await progressTime(defaultExpectedInterval);
|
|
4384
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4385
|
+
});
|
|
4386
|
+
it('startKeepAlive handles missing keepAliveUrl', async () => {
|
|
4387
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4388
|
+
logger.warn = sinon.spy();
|
|
4389
|
+
|
|
4390
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4391
|
+
meeting.joinedWith = {
|
|
4392
|
+
keepAliveSecs: defaultKeepAliveSecs
|
|
4393
|
+
};
|
|
4394
|
+
meeting.startKeepAlive();
|
|
4395
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4396
|
+
await progressTime(defaultExpectedInterval);
|
|
4397
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4398
|
+
});
|
|
4399
|
+
it('startKeepAlive handles missing keepAliveSecs', async () => {
|
|
4400
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4401
|
+
logger.warn = sinon.spy();
|
|
4402
|
+
|
|
4403
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4404
|
+
meeting.joinedWith = {
|
|
4405
|
+
keepAliveUrl: defaultKeepAliveUrl
|
|
4406
|
+
};
|
|
4407
|
+
meeting.startKeepAlive();
|
|
4408
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4409
|
+
await progressTime(1);
|
|
4410
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4411
|
+
});
|
|
4412
|
+
it('startKeepAlive handles too low keepAliveSecs', async () => {
|
|
4413
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4414
|
+
logger.warn = sinon.spy();
|
|
4415
|
+
|
|
4416
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4417
|
+
meeting.joinedWith = {
|
|
4418
|
+
keepAliveUrl: defaultKeepAliveUrl,
|
|
4419
|
+
keepAliveSecs: 1
|
|
4420
|
+
};
|
|
4421
|
+
meeting.startKeepAlive();
|
|
4422
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4423
|
+
await progressTime(1);
|
|
4424
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4425
|
+
});
|
|
4426
|
+
it('failed keepAlive stops the keep alives', async () => {
|
|
4427
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.reject());
|
|
4428
|
+
|
|
4429
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4430
|
+
meeting.joinedWith = {
|
|
4431
|
+
keepAliveUrl: defaultKeepAliveUrl,
|
|
4432
|
+
keepAliveSecs: defaultKeepAliveSecs
|
|
4433
|
+
};
|
|
4434
|
+
meeting.startKeepAlive();
|
|
4435
|
+
assert.isNumber(meeting.keepAliveTimerId.id);
|
|
4436
|
+
await testUtils.flushPromises();
|
|
4437
|
+
assert.notCalled(meeting.meetingRequest.keepAlive);
|
|
4438
|
+
await progressTime(defaultExpectedInterval);
|
|
4439
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
|
|
4440
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4441
|
+
await progressTime(defaultExpectedInterval);
|
|
4442
|
+
assert.calledOnce(meeting.meetingRequest.keepAlive);
|
|
4443
|
+
});
|
|
4444
|
+
});
|
|
4445
|
+
describe('#stopKeepAlive', () => {
|
|
4446
|
+
let clock;
|
|
4447
|
+
const defaultKeepAliveUrl = 'keep.alive.url';
|
|
4448
|
+
const defaultKeepAliveSecs = 23;
|
|
4449
|
+
const defaultExpectedInterval = (defaultKeepAliveSecs - 1) * 750;
|
|
4450
|
+
|
|
4451
|
+
beforeEach(() => {
|
|
4452
|
+
clock = sinon.useFakeTimers();
|
|
4453
|
+
});
|
|
4454
|
+
afterEach(() => {
|
|
4455
|
+
clock.restore();
|
|
4456
|
+
});
|
|
4457
|
+
|
|
4458
|
+
const progressTime = async (interval) => {
|
|
4459
|
+
await clock.tickAsync(interval);
|
|
4460
|
+
await testUtils.flushPromises();
|
|
4461
|
+
};
|
|
4462
|
+
|
|
4463
|
+
it('stopKeepAlive stops the keep alive', async () => {
|
|
4464
|
+
meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
|
|
4465
|
+
|
|
4466
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4467
|
+
meeting.joinedWith = {
|
|
4468
|
+
keepAliveUrl: defaultKeepAliveUrl,
|
|
4469
|
+
keepAliveSecs: defaultKeepAliveSecs
|
|
4470
|
+
};
|
|
4471
|
+
meeting.startKeepAlive();
|
|
4472
|
+
assert.isNumber(meeting.keepAliveTimerId.id);
|
|
4473
|
+
await progressTime(defaultExpectedInterval);
|
|
4474
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
|
|
4475
|
+
|
|
4476
|
+
meeting.stopKeepAlive();
|
|
4477
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4478
|
+
await progressTime(defaultExpectedInterval);
|
|
4479
|
+
assert.calledOnce(meeting.meetingRequest.keepAlive);
|
|
4480
|
+
});
|
|
4481
|
+
it('stopKeepAlive handles missing keepAliveTimerId', async () => {
|
|
4482
|
+
assert.isNull(meeting.keepAliveTimerId);
|
|
4483
|
+
meeting.stopKeepAlive();
|
|
4484
|
+
});
|
|
4485
|
+
});
|
|
4331
4486
|
});
|
|
4332
4487
|
});
|
|
4333
4488
|
});
|