Permalink
Browse files

Added ReconnectingWebSocket

  • Loading branch information...
0 parents commit 6a84bc29d59f36a6a445c4e09f2ffe3c31bce2c5 Joe Walnes committed Jan 27, 2012
Showing with 204 additions and 0 deletions.
  1. +21 −0 LICENSE.txt
  2. +15 −0 README.md
  3. +168 −0 reconnecting-websocket.js
@@ -0,0 +1,21 @@
+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.
@@ -0,0 +1,15 @@
+ReconnectingWebSocket
+=====================
+
+A small JavaScript library that decorates the WebSocket API to provide
+a WebSocket connection that will automatically reconnect if the
+connection is dropped.
+
+It is API compatible, so when you have:
+
+ ws = new WebSocket('ws://....');
+
+you can replace with:
+
+ ws = new ReconnectingWebSocket('ws://....');
+
@@ -0,0 +1,168 @@
+// 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:
+ * onopen
+ * onmessage
+ * onmessage
+ * onclose // lost connection
+ * onopen // sometime later...
+ * onmessage
+ * onmessage
+ * etc...
+ *
+ * It is API compatible with the standard WebSocket API.
+ *
+ * - Joe Walnes
+ */
+function ReconnectingWebSocket(url) {
+
+ // 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.readyState = WebSocket.CONNECTING;
+ this.URL = url; // Public API
+
+ this.onopen = function(event) {
+ };
+
+ this.onclose = function(event) {
+ };
+
+ this.onmessage = function(event) {
+ };
+
+ this.onerror = function(event) {
+ };
+
+ function connect(reconnectAttempt) {
+ ws = new WebSocket(url);
+ 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;
+ 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;
+

0 comments on commit 6a84bc2

Please sign in to comment.