// MIT License: // // Copyright (c) 2010-2012, Joe Walnes // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. /** * This behaves like a WebSocket in every way, except if it fails to connect, * or it gets disconnected, it will repeatedly poll until it succesfully connects * again. * * It is API compatible, so when you have: * ws = new WebSocket('ws://....'); * you can replace with: * ws = new ReconnectingWebSocket('ws://....'); * * The event stream will typically look like: * onconnecting * onopen * onmessage * onmessage * onclose // lost connection * onconnecting * onopen // sometime later... * onmessage * onmessage * etc... * * It is API compatible with the standard WebSocket API. * * Latest version: https://github.com/joewalnes/reconnecting-websocket/ * - Joe Walnes */ function ReconnectingWebSocket(url, protocols) { protocols = protocols || []; // These can be altered by calling code. this.debug = false; this.reconnectInterval = 1000; this.timeoutInterval = 2000; var self = this; var ws; var forcedClose = false; var timedOut = false; this.url = url; this.protocols = protocols; this.readyState = WebSocket.CONNECTING; this.URL = url; // Public API this.onopen = function(event) { }; this.onclose = function(event) { }; this.onconnecting = function(event) { }; this.onmessage = function(event) { }; this.onerror = function(event) { }; function connect(reconnectAttempt) { ws = new WebSocket(url, protocols); self.onconnecting(); if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'attempt-connect', url); } var localWs = ws; var timeout = setTimeout(function() { if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'connection-timeout', url); } timedOut = true; localWs.close(); timedOut = false; }, self.timeoutInterval); ws.onopen = function(event) { clearTimeout(timeout); if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'onopen', url); } self.readyState = WebSocket.OPEN; reconnectAttempt = false; self.onopen(event); }; ws.onclose = function(event) { clearTimeout(timeout); ws = null; if (forcedClose) { self.readyState = WebSocket.CLOSED; self.onclose(event); } else { self.readyState = WebSocket.CONNECTING; self.onconnecting(); if (!reconnectAttempt && !timedOut) { if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'onclose', url); } self.onclose(event); } setTimeout(function() { connect(true); }, self.reconnectInterval); } }; ws.onmessage = function(event) { if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'onmessage', url, event.data); } self.onmessage(event); }; ws.onerror = function(event) { if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'onerror', url, event); } self.onerror(event); }; } connect(url); this.send = function(data) { if (ws) { if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'send', url, data); } return ws.send(data); } else { throw 'INVALID_STATE_ERR : Pausing to reconnect websocket'; } }; this.close = function() { if (ws) { forcedClose = true; ws.close(); } }; /** * Additional public API method to refresh the connection if still open (close, re-open). * For example, if the app suspects bad data / missed heart beats, it can try to refresh. */ this.refresh = function() { if (ws) { ws.close(); } }; } /** * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true. */ ReconnectingWebSocket.debugAll = false;