openvidu-browser: reconnection fix for unexpected ws close (HttpSession expiring)

pull/178/head
pabloFuente 2019-01-14 10:18:04 +01:00
parent 05dc6e21aa
commit cb8594373e
6 changed files with 83 additions and 66 deletions

View File

@ -59,6 +59,11 @@ export class Connection {
*/ */
disposed = false; disposed = false;
/**
* @hidden
*/
rpcSessionId: string;
/** /**
* @hidden * @hidden
*/ */

View File

@ -91,7 +91,7 @@ export class OpenVidu {
const getNewVideoDimensions = (): Promise<{ newWidth: number, newHeight: number }> => { const getNewVideoDimensions = (): Promise<{ newWidth: number, newHeight: number }> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (platform['isIonicIos']) { if (platform['isIonicIos']) {
// iOS Ionic. Limitation: must get new dimensions from an existing video element already inserted into DOM // iOS Ionic. Limitation: must get new dimensions from an existing video element already inserted into DOM
resolve({ resolve({
newWidth: publisher.stream.streamManager.videos[0].video.videoWidth, newWidth: publisher.stream.streamManager.videos[0].video.videoWidth,
newHeight: publisher.stream.streamManager.videos[0].video.videoHeight newHeight: publisher.stream.streamManager.videos[0].video.videoHeight
@ -636,7 +636,7 @@ export class OpenVidu {
* @hidden * @hidden
*/ */
closeWs(): void { closeWs(): void {
this.jsonRpcClient.close(); this.jsonRpcClient.close(4102, "Connection closed by client");
} }
/** /**
@ -678,7 +678,7 @@ export class OpenVidu {
private disconnectCallback(): void { private disconnectCallback(): void {
console.warn('Websocket connection lost'); console.warn('Websocket connection lost');
if (this.isRoomAvailable()) { if (this.isRoomAvailable()) {
this.session.onLostConnection(); this.session.onLostConnection('networkDisconnect');
} else { } else {
alert('Connection error. Please reload page.'); alert('Connection error. Please reload page.');
} }
@ -686,9 +686,7 @@ export class OpenVidu {
private reconnectingCallback(): void { private reconnectingCallback(): void {
console.warn('Websocket connection lost (reconnecting)'); console.warn('Websocket connection lost (reconnecting)');
if (this.isRoomAvailable()) { if (!this.isRoomAvailable()) {
this.session.onLostConnection();
} else {
alert('Connection error. Please reload page.'); alert('Connection error. Please reload page.');
} }
} }
@ -696,7 +694,16 @@ export class OpenVidu {
private reconnectedCallback(): void { private reconnectedCallback(): void {
console.warn('Websocket reconnected'); console.warn('Websocket reconnected');
if (this.isRoomAvailable()) { if (this.isRoomAvailable()) {
this.session.onRecoveredConnection(); this.sendRequest('connect', { sessionId: this.session.connection.rpcSessionId }, (error, response) => {
if (!!error) {
console.error(error);
this.session.onLostConnection("networkDisconnect");
this.jsonRpcClient.close(4101, "Reconnection fault");
} else {
this.jsonRpcClient.resetPing();
this.session.onRecoveredConnection();
}
});
} else { } else {
alert('Connection error. Please reload page.'); alert('Connection error. Please reload page.');
} }

View File

@ -899,22 +899,10 @@ export class Session implements EventDispatcher {
/** /**
* @hidden * @hidden
*/ */
onLostConnection(): void { onLostConnection(reason: string): void {
console.warn('Lost connection in session ' + this.sessionId + ' waiting for reconnect');
/*if (!this.connection) {
console.warn('Not connected to session: if you are not debugging, this is probably a certificate error');
const url = 'https://' + this.openvidu.getWsUri().split('wss://')[1].split('/openvidu')[0];
if (window.confirm('If you are not debugging, this is probably a certificate error at \"' + url + '\"\n\nClick OK to navigate and accept it')) {
location.assign(url + '/accept-certificate');
}
return;
}*/
console.warn('Lost connection in Session ' + this.sessionId);
if (!!this.sessionId && !this.connection.disposed) { if (!!this.sessionId && !this.connection.disposed) {
this.leave(true, 'networkDisconnect'); this.leave(true, reason);
} }
} }
@ -923,7 +911,7 @@ export class Session implements EventDispatcher {
*/ */
onRecoveredConnection(): void { onRecoveredConnection(): void {
console.warn('Recovered connection in Session ' + this.sessionId); console.warn('Recovered connection in Session ' + this.sessionId);
this.ee.emitEvent('connectionRecovered', []); // this.ee.emitEvent('connectionRecovered', []);
} }
/** /**
@ -1048,6 +1036,7 @@ export class Session implements EventDispatcher {
this.connection.connectionId = response.id; this.connection.connectionId = response.id;
this.connection.creationTime = response.createdAt; this.connection.creationTime = response.createdAt;
this.connection.data = response.metadata; this.connection.data = response.metadata;
this.connection.rpcSessionId = response.sessionId;
// Initialize remote Connections with value returned by openvidu-server // Initialize remote Connections with value returned by openvidu-server
const events = { const events = {

View File

@ -18,7 +18,7 @@
var RpcBuilder = require('../'); var RpcBuilder = require('../');
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection'); var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
Date.now = Date.now || function() { Date.now = Date.now || function () {
return +new Date; return +new Date;
}; };
@ -39,7 +39,7 @@ var Logger = console;
* uri : URI to conntect to, * uri : URI to conntect to,
* useSockJS : true (use SockJS) / false (use WebSocket) by default, * useSockJS : true (use SockJS) / false (use WebSocket) by default,
* onconnected : callback method to invoke when connection is successful, * onconnected : callback method to invoke when connection is successful,
* ondisconnect : callback method to invoke when the connection is lost, * ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
* onreconnecting : callback method to invoke when the client is reconnecting, * onreconnecting : callback method to invoke when the client is reconnecting,
* onreconnected : callback method to invoke when the client successfully reconnects, * onreconnected : callback method to invoke when the client successfully reconnects,
* onerror : callback method to invoke when there is an error * onerror : callback method to invoke when there is an error
@ -71,24 +71,26 @@ function JsonRpcClient(configuration) {
var onconnected = wsConfig.onconnected; var onconnected = wsConfig.onconnected;
var onerror = wsConfig.onerror; var onerror = wsConfig.onerror;
configuration.rpc.pull = function(params, request) { configuration.rpc.pull = function (params, request) {
request.reply(null, "push"); request.reply(null, "push");
} }
wsConfig.onreconnecting = function() { wsConfig.onreconnecting = function () {
Logger.debug("--------- ONRECONNECTING -----------"); Logger.debug("--------- ONRECONNECTING -----------");
if (status === RECONNECTING) { if (status === RECONNECTING) {
Logger.error("Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it"); Logger.error("Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it");
return; return;
} }
stopPing();
status = RECONNECTING; status = RECONNECTING;
if (onreconnecting) { if (onreconnecting) {
onreconnecting(); onreconnecting();
} }
} }
wsConfig.onreconnected = function() { wsConfig.onreconnected = function () {
Logger.debug("--------- ONRECONNECTED -----------"); Logger.debug("--------- ONRECONNECTED -----------");
if (status === CONNECTED) { if (status === CONNECTED) {
Logger.error("Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it"); Logger.error("Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it");
@ -96,16 +98,14 @@ function JsonRpcClient(configuration) {
} }
status = CONNECTED; status = CONNECTED;
enabledPings = true;
updateNotReconnectIfLessThan(); updateNotReconnectIfLessThan();
usePing();
if (onreconnected) { if (onreconnected) {
onreconnected(); onreconnected();
} }
} }
wsConfig.onconnected = function() { wsConfig.onconnected = function () {
Logger.debug("--------- ONCONNECTED -----------"); Logger.debug("--------- ONCONNECTED -----------");
if (status === CONNECTED) { if (status === CONNECTED) {
Logger.error("Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it"); Logger.error("Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it");
@ -121,11 +121,13 @@ function JsonRpcClient(configuration) {
} }
} }
wsConfig.onerror = function(error) { wsConfig.onerror = function (error) {
Logger.debug("--------- ONERROR -----------"); Logger.debug("--------- ONERROR -----------");
status = DISCONNECTED; status = DISCONNECTED;
stopPing();
if (onerror) { if (onerror) {
onerror(error); onerror(error);
} }
@ -141,7 +143,7 @@ function JsonRpcClient(configuration) {
}; };
var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws, var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws,
function(request) { function (request) {
Logger.debug('Received request: ' + JSON.stringify(request)); Logger.debug('Received request: ' + JSON.stringify(request));
@ -159,14 +161,14 @@ function JsonRpcClient(configuration) {
} }
}); });
this.send = function(method, params, callback) { this.send = function (method, params, callback) {
if (method !== 'ping') { if (method !== 'ping') {
Logger.debug('Request: method:' + method + " params:" + JSON.stringify(params)); Logger.debug('Request: method:' + method + " params:" + JSON.stringify(params));
} }
var requestTime = Date.now(); var requestTime = Date.now();
rpc.encode(method, params, function(error, result) { rpc.encode(method, params, function (error, result) {
if (error) { if (error) {
try { try {
Logger.error("ERROR:" + error.message + " in Request: method:" + Logger.error("ERROR:" + error.message + " in Request: method:" +
@ -203,8 +205,8 @@ function JsonRpcClient(configuration) {
} }
pingNextNum++; pingNextNum++;
self.send('ping', params, (function(pingNum) { self.send('ping', params, (function (pingNum) {
return function(error, result) { return function (error, result) {
if (error) { if (error) {
Logger.debug("Error in ping request #" + pingNum + " (" + Logger.debug("Error in ping request #" + pingNum + " (" +
error.message + ")"); error.message + ")");
@ -224,9 +226,9 @@ function JsonRpcClient(configuration) {
} }
/* /*
* If configuration.hearbeat has any value, the ping-pong will work with the interval * If configuration.hearbeat has any value, the ping-pong will work with the interval
* of configuration.hearbeat * of configuration.hearbeat
*/ */
function usePing() { function usePing() {
if (!pingPongStarted) { if (!pingPongStarted) {
Logger.debug("Starting ping (if configured)") Logger.debug("Starting ping (if configured)")
@ -239,8 +241,16 @@ function JsonRpcClient(configuration) {
} }
} }
this.close = function() { function stopPing() {
Logger.debug("Closing jsonRpcClient explicitly by client"); clearInterval(pingInterval);
pingPongStarted = false;
enabledPings = false;
pingNextNum = -1;
rpc.cancel();
}
this.close = function (code, reason) {
Logger.debug("Closing with code: " + code + " because: " + reason);
if (pingInterval != undefined) { if (pingInterval != undefined) {
Logger.debug("Clearing ping interval"); Logger.debug("Clearing ping interval");
@ -251,26 +261,32 @@ function JsonRpcClient(configuration) {
if (configuration.sendCloseMessage) { if (configuration.sendCloseMessage) {
Logger.debug("Sending close message") Logger.debug("Sending close message")
this.send('closeSession', null, function(error, result) { this.send('closeSession', null, function (error, result) {
if (error) { if (error) {
Logger.error("Error sending close message: " + JSON.stringify(error)); Logger.error("Error sending close message: " + JSON.stringify(error));
} }
ws.close(); ws.close(code, reason);
}); });
} else { } else {
ws.close(); ws.close(code, reason);
} }
} }
// This method is only for testing // This method is only for testing
this.forceClose = function(millis) { this.forceClose = function (millis) {
ws.forceClose(millis); ws.forceClose(millis);
} }
this.reconnect = function() { this.reconnect = function () {
ws.reconnectWs(); ws.reconnectWs();
} }
this.resetPing = function () {
enabledPings = true;
pingNextNum = 0;
usePing();
}
} }
module.exports = JsonRpcClient; module.exports = JsonRpcClient;

View File

@ -48,7 +48,7 @@ config = {
uri : wsUri, uri : wsUri,
useSockJS : true (use SockJS) / false (use WebSocket) by default, useSockJS : true (use SockJS) / false (use WebSocket) by default,
onconnected : callback method to invoke when connection is successful, onconnected : callback method to invoke when connection is successful,
ondisconnect : callback method to invoke when the connection is lost, ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
onreconnecting : callback method to invoke when the client is reconnecting, onreconnecting : callback method to invoke when the client is reconnecting,
onreconnected : callback method to invoke when the client successfully reconnects, onreconnected : callback method to invoke when the client successfully reconnects,
}; };
@ -71,14 +71,14 @@ function WebSocketWithReconnection(config) {
ws = new WebSocket(wsUri); ws = new WebSocket(wsUri);
} }
ws.onopen = function() { ws.onopen = function () {
logConnected(ws, wsUri); logConnected(ws, wsUri);
if (config.onconnected) { if (config.onconnected) {
config.onconnected(); config.onconnected();
} }
}; };
ws.onerror = function(error) { ws.onerror = function (error) {
Logger.error("Could not connect to " + wsUri + " (invoking onerror if defined)", error); Logger.error("Could not connect to " + wsUri + " (invoking onerror if defined)", error);
if (config.onerror) { if (config.onerror) {
config.onerror(error); config.onerror(error);
@ -93,7 +93,7 @@ function WebSocketWithReconnection(config) {
} }
} }
var reconnectionOnClose = function() { var reconnectionOnClose = function () {
if (ws.readyState === CLOSED) { if (ws.readyState === CLOSED) {
if (closing) { if (closing) {
Logger.debug("Connection closed by user"); Logger.debug("Connection closed by user");
@ -129,11 +129,11 @@ function WebSocketWithReconnection(config) {
} else { } else {
if (config.newWsUriOnReconnection) { if (config.newWsUriOnReconnection) {
config.newWsUriOnReconnection(function(error, newWsUri) { config.newWsUriOnReconnection(function (error, newWsUri) {
if (error) { if (error) {
Logger.debug(error); Logger.debug(error);
setTimeout(function() { setTimeout(function () {
reconnectToSameUri(maxRetries, numRetries + 1); reconnectToSameUri(maxRetries, numRetries + 1);
}, RETRY_TIME_MS); }, RETRY_TIME_MS);
} else { } else {
@ -161,7 +161,7 @@ function WebSocketWithReconnection(config) {
newWs = new WebSocket(wsUri); newWs = new WebSocket(wsUri);
} }
newWs.onopen = function() { newWs.onopen = function () {
Logger.debug("Reconnected after " + numRetries + " attempts..."); Logger.debug("Reconnected after " + numRetries + " attempts...");
logConnected(newWs, wsUri); logConnected(newWs, wsUri);
reconnecting = false; reconnecting = false;
@ -173,7 +173,7 @@ function WebSocketWithReconnection(config) {
newWs.onclose = reconnectionOnClose; newWs.onclose = reconnectionOnClose;
}; };
var onErrorOrClose = function(error) { var onErrorOrClose = function (error) {
Logger.warn("Reconnection error: ", error); Logger.warn("Reconnection error: ", error);
if (numRetries === maxRetries) { if (numRetries === maxRetries) {
@ -181,7 +181,7 @@ function WebSocketWithReconnection(config) {
config.ondisconnect(); config.ondisconnect();
} }
} else { } else {
setTimeout(function() { setTimeout(function () {
reconnectToSameUri(maxRetries, numRetries + 1); reconnectToSameUri(maxRetries, numRetries + 1);
}, RETRY_TIME_MS); }, RETRY_TIME_MS);
} }
@ -192,14 +192,14 @@ function WebSocketWithReconnection(config) {
ws = newWs; ws = newWs;
} }
this.close = function() { this.close = function () {
closing = true; closing = true;
ws.close(); ws.close();
}; };
// This method is only for testing // This method is only for testing
this.forceClose = function(millis) { this.forceClose = function (millis) {
Logger.debug("Testing: Force WebSocket close"); Logger.debug("Testing: Force WebSocket close");
if (millis) { if (millis) {
@ -209,7 +209,7 @@ function WebSocketWithReconnection(config) {
forcingDisconnection = true; forcingDisconnection = true;
setTimeout(function() { setTimeout(function () {
Logger.debug("Testing: Recover good wsUri " + goodWsUri); Logger.debug("Testing: Recover good wsUri " + goodWsUri);
wsUri = goodWsUri; wsUri = goodWsUri;
@ -221,17 +221,17 @@ function WebSocketWithReconnection(config) {
ws.close(); ws.close();
}; };
this.reconnectWs = function() { this.reconnectWs = function () {
Logger.debug("reconnectWs"); Logger.debug("reconnectWs");
reconnectToSameUri(MAX_RETRIES, 1); reconnectToSameUri(MAX_RETRIES, 1);
}; };
this.send = function(message) { this.send = function (message) {
ws.send(message); ws.send(message);
}; };
this.addEventListener = function(type, callback) { this.addEventListener = function (type, callback) {
registerMessageHandler = function() { registerMessageHandler = function () {
ws.addEventListener(type, callback); ws.addEventListener(type, callback);
}; };
@ -239,4 +239,4 @@ function WebSocketWithReconnection(config) {
}; };
} }
module.exports = WebSocketWithReconnection; module.exports = WebSocketWithReconnection;

View File

@ -519,7 +519,7 @@ function RpcBuilder(packer, options, transport, onRequest)
// Prevent to receive new messages // Prevent to receive new messages
var transport = this.getTransport(); var transport = this.getTransport();
if(transport && transport.close) if(transport && transport.close)
transport.close(); transport.close(4003, "Cancel request");
// Request & processed responses // Request & processed responses
this.cancel(); this.cancel();