Recovering from an outage
Recover from network outage/issue/change
Please note that, this SDK doesn't detect network outage/issue/change. Our philosophy is to avoid adding any magic logic to the SDK.
For a working example to handle network outage/issue/change, please refer to https://github.com/tylerlong/rc-web-phone-demo-2/blob/main/src/store/after-login.ts. Scroll to the bottom part where it handles network outage/issue/change.
network outage
If you believe your app just recovered from network outage and the underlying
WebSocket connection is broken, you may call webPhone.start()
. It will create
a brand new websocket connection to the SIP server and re-register the SIP
client.
A sample implemetation could be as simple as this:
// browser issues network online event.
window.addEventListener("online", () => webPhone.start());
Please note that, in this case, existing calls will recover automatically by WebRTC unless the network changed(like from one WiFi to another, or from WiFi to cellular). For network change, please read sections below.
network issue
What if network is not offline, but underlying WebSocket connection is broken? This is very unlikely to happen, but if it happens, the following code will try to bring your web phone back to work:
import waitFor from "wait-for-async";
const closeListener = async (e) => {
webPhone.sipClient.wsc.removeEventListener("close", closeListener);
if (webPhone.disposed) {
// webPhone.dispose() has been called, no need to reconnect
return;
}
console.log("WebSocket disconnected unexpectedly", e);
let connected = false;
let delay = 2000; // initial delay
while (!connected) {
console.log(`Reconnect WebSocket in ${delay / 1000} seconds`);
await waitFor({ interval: delay });
try {
await webPhone.start();
connected = true;
} catch (e) {
console.log("Error connecting to WebSocket", e);
delay *= 2; // exponential backoff
delay = Math.min(delay, 60000); // max delay 60s
}
}
// because webPhone.start() will create a new webPhone.sipClient.wsc
webPhone.sipClient.wsc.addEventListener("close", closeListener);
};
webPhone.sipClient.wsc.addEventListener("close", closeListener);
By default the SDK will send a register
message around every 60 seconds. If
there is no response from server in 5 seconds(which indicates that the WebSocket
connection is probably broken), the SDK will proactively close the WebSocket
connection, which will trigger the logic above to invoke webPhone.start()
.
network change
Like switching from WiFi to mobile hot spot, or switching from one WiFi to another.
In such cases, both the WebSocket connection and the WebRTC connections will break.
webPhone.start()
will recover the WebSocket connection. But WebRTC connections
are still broken.
This is not an issue if there are no active call sessions ongoing. But if there are active call sessions when the network switches from one to another, the existing call sessions will become "silent".
The solution is to send "re-INVITE" for each ongoing call session:
webPhone.callSessions.forEach((callSession) => {
if (callSession.state === "answered") {
callSession.reInvite();
}
});
"re-INVITE" will re-establish the WebRTC connections based on latest network information.
Sample code to handle all cases (network outage/issue/change)
const recover = async () => {
await webPhone.start();
webPhone.callSessions.forEach((callSession) => {
if (callSession.state === "answered") {
// in case the network switches from one to another
callSession.reInvite();
}
});
};
// handle network outage
window.addEventListener("online", async () => {
await recover();
});
// handle network issues
const closeListener = async (e) => {
webPhone.sipClient.wsc.removeEventListener("close", closeListener);
if (webPhone.disposed) {
// webPhone.dispose() has been called, no need to reconnect
return;
}
console.log("WebSocket disconnected unexpectedly", e);
let connected = false;
let delay = 2000; // initial delay
while (!connected) {
console.log(`Reconnect WebSocket in ${delay / 1000} seconds`);
await waitFor({ interval: delay });
try {
await recover();
connected = true;
} catch (e) {
console.log("Error connecting to WebSocket", e);
delay *= 2; // exponential backoff
delay = Math.min(delay, 60000); // max delay 60s
}
}
// because webPhone.start() will create a new webPhone.sipClient.wsc
webPhone.sipClient.wsc.addEventListener("close", closeListener);
};
webPhone.sipClient.wsc.addEventListener("close", closeListener);
Latest tested code could be found here: https://github.com/tylerlong/rc-web-phone-demo-2/blob/main/src/store/after-login.ts Scroll to the bottom part where it handles network outage/issue/change.
switch to backup outbound proxy
There are both sipInfo.outboundProxy
and sipInfo.outboundProxyBackup
. By
default sipInfo.outboundProxy
is used. In very rare cases,
sipInfo.outboundProxy
is broken and you will need to connect to
sipInfo.outboundProxyBackup
instead. First of all, this shouldn't happen at
all. RingCentral will make sure that sipInfo.outboundProxy
is always up and
running. So sipInfo.outboundProxyBackup
is just in case.
The SDK doesn't automatically switch to backup outbound proxy because we don't want to add any magical logic to the code base. As we said you probably don't need to do this, but if you have to do, we have you covered.
We allow you to use your own SipClient:
const webPhone = new WebPhone({sipClient: new MyOwnSipClient()})
. If you do
so, you gain extreme flexibility. How to switch to backup outbound proxy is up
to you.
If you didn't specify your own SipClient
implementation,
DefaultSipClient
will be used. And to switch to backup outbound proxy:
(webPhone.sipClient as DefaultSipClient).toggleBackupOutboundProxy(true)
. To
switch back to the original outbound proxy:
(webPhone.sipClient as DefaultSipClient).toggleBackupOutboundProxy(false)
.
You will need to invoke webPhone.start()
to re-create the WebSocket
connection, otherwise it is still the old outbound proxy.