From 94ec2c720d2d89c11f3a44328f184a42fc6cc4e2 Mon Sep 17 00:00:00 2001 From: Andrei Gavrilescu <51706180+andrei-gavrilescu@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:32:18 +0200 Subject: [PATCH] feat(rtcstats): report pc connection failure (#12560) * report pc connection failure * typos * code review / update rtcstats * check for undefined APP --- modules/API/API.js | 21 +++++++++++++ modules/API/external/external_api.js | 1 + package-lock.json | 14 ++++----- package.json | 2 +- react/features/rtcstats/RTCStats.ts | 47 +++++++++++++++++++++++++++- 5 files changed, 76 insertions(+), 9 deletions(-) diff --git a/modules/API/API.js b/modules/API/API.js index e61e5a394..1dad41d68 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -1936,6 +1936,27 @@ class API { }); } + /** + * Notify the external application that a PeerConnection lost connectivity. This event is fired only if + * a PC `failed` but connectivity to the rtcstats server is still maintained signaling that there is a + * problem establishing a link between the app and the JVB server or the remote peer in case of P2P. + * Will only fire if rtcstats is enabled. + * + * @param {boolean} isP2P - Type of PC. + * @param {boolean} wasConnected - Was this connection previously connected. If it was it could mean + * that connectivity was disrupted, if not it most likely means that the app could not reach + * the JVB server, or the other peer in case of P2P. + * + * @returns {void} + */ + notifyPeerConnectionFailure(isP2P, wasConnected) { + this._sendEvent({ + name: 'peer-connection-failure', + isP2P, + wasConnected + }); + } + /** * Disposes the allocated resources. * diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index 47c288459..3380a7a91 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -135,6 +135,7 @@ const events = { 'participant-role-changed': 'participantRoleChanged', 'participants-pane-toggled': 'participantsPaneToggled', 'password-required': 'passwordRequired', + 'peer-connection-failure': 'peerConnectionFailure', 'prejoin-screen-loaded': 'prejoinScreenLoaded', 'proxy-connection-event': 'proxyConnectionEvent', 'raise-hand-updated': 'raiseHandUpdated', diff --git a/package-lock.json b/package-lock.json index 9c6d812c4..d57a85dd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@jitsi/js-utils": "2.0.4", "@jitsi/logger": "2.0.0", "@jitsi/rnnoise-wasm": "0.1.0", - "@jitsi/rtcstats": "9.4.1", + "@jitsi/rtcstats": "9.5.0", "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz", "@microsoft/microsoft-graph-client": "3.0.1", "@mui/material": "5.10.2", @@ -3780,9 +3780,9 @@ "integrity": "sha512-JujivPbOUvdRYa2xqByHYKfKGNGa7ZPyNLaNuh8hEp9XsiNfjaJAHdboq6M1VY9TP+765nyxC0LjpAw1VkikOQ==" }, "node_modules/@jitsi/rtcstats": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.4.1.tgz", - "integrity": "sha512-JrRBk9xLAnRgBP9aqTjR41DBAQYMkupOfy8XMIumdjxlDqf8dQygvYRc253xdHejr/kSHCvnaFoVIM3hHfeooQ==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.5.0.tgz", + "integrity": "sha512-jKB+1IzKuqynA2etmWAA4uDFF0oAFUZWxRq+m+rOt8FfBp6pXojWbWA7xblcjxerj/3njGc8nEQbcK9qck1How==", "dependencies": { "@jitsi/js-utils": "^2.0.0", "sdp": "^3.0.3", @@ -23208,9 +23208,9 @@ "integrity": "sha512-JujivPbOUvdRYa2xqByHYKfKGNGa7ZPyNLaNuh8hEp9XsiNfjaJAHdboq6M1VY9TP+765nyxC0LjpAw1VkikOQ==" }, "@jitsi/rtcstats": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.4.1.tgz", - "integrity": "sha512-JrRBk9xLAnRgBP9aqTjR41DBAQYMkupOfy8XMIumdjxlDqf8dQygvYRc253xdHejr/kSHCvnaFoVIM3hHfeooQ==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.5.0.tgz", + "integrity": "sha512-jKB+1IzKuqynA2etmWAA4uDFF0oAFUZWxRq+m+rOt8FfBp6pXojWbWA7xblcjxerj/3njGc8nEQbcK9qck1How==", "requires": { "@jitsi/js-utils": "^2.0.0", "sdp": "^3.0.3", diff --git a/package.json b/package.json index 279c4dc88..e9940305e 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@jitsi/js-utils": "2.0.4", "@jitsi/logger": "2.0.0", "@jitsi/rnnoise-wasm": "0.1.0", - "@jitsi/rtcstats": "9.4.1", + "@jitsi/rtcstats": "9.5.0", "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz", "@microsoft/microsoft-graph-client": "3.0.1", "@mui/material": "5.10.2", diff --git a/react/features/rtcstats/RTCStats.ts b/react/features/rtcstats/RTCStats.ts index eabbd2bed..69567aa54 100644 --- a/react/features/rtcstats/RTCStats.ts +++ b/react/features/rtcstats/RTCStats.ts @@ -1,9 +1,18 @@ // @ts-ignore +import { PC_CON_STATE_CHANGE, + PC_STATE_CONNECTED, + PC_STATE_FAILED + + // @ts-ignore +} from '@jitsi/rtcstats/events'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore import rtcstatsInit from '@jitsi/rtcstats/rtcstats'; // eslint-disable-next-line lines-around-comment // @ts-ignore import traceInit from '@jitsi/rtcstats/trace-ws'; + import { createRTCStatsTraceCloseEvent } from '../analytics/AnalyticsEvents'; import { sendAnalytics } from '../analytics/functions'; @@ -39,6 +48,7 @@ function connectionFilter(config: any) { class RTCStats { trace: any; initialized = false; + connStateEvents: any = []; /** * Initialize the rtcstats components. First off we initialize the trace, which is a wrapped websocket @@ -71,7 +81,8 @@ class RTCStats { connectionFilter, pollInterval, useLegacy, - sendSdp + sendSdp, + eventCallback: this.handleRtcstatsEvent.bind(this) }; this.trace = traceInit(traceOptions); @@ -185,6 +196,40 @@ class RTCStats { this.trace?.close(); } + /** + * RTCStats client can notify the APP of any PeerConnection related event that occurs. + * + * @param {Object} event - Rtcstats event. + * @returns {void} + */ + handleRtcstatsEvent(event: any) { + switch (event.type) { + case PC_CON_STATE_CHANGE: { + const { body: { isP2P, state } = { state: null, + isP2P: null } } = event; + + this.connStateEvents.push(event.body); + + // We only report PC related connection issues. If the rtcstats websocket is not connected at this point + // it usually means that none of our services can be reached i.e. there's problem with the internet + // connection and not necessarily with reaching the JVB (due to a firewall or other reasons). + if (state === PC_STATE_FAILED && this.trace.isConnected()) { + const connectionType = isP2P ? 'P2P' : 'JVB'; + const wasConnected = this.connStateEvents.some((connectionEvent: { isP2P: any; state: string; }) => + (connectionEvent.isP2P === isP2P) && (connectionEvent.state === PC_STATE_CONNECTED)); + + logger.info(`${connectionType} PeerConnection failed, previously connected: ${wasConnected}`); + + if (typeof APP !== 'undefined') { + APP.API.notifyPeerConnectionFailure(isP2P, wasConnected); + } + } + + break; + } + } + } + /** * The way rtcstats is currently designed the ws wouldn't normally be closed by the application logic but rather * by the page being closed/reloaded. Using this assumption any onclose event is most likely something abnormal