diff --git a/reconnecting-websocket.js b/reconnecting-websocket.js index 0cd4332..5e6b400 100644 --- a/reconnecting-websocket.js +++ b/reconnecting-websocket.js @@ -100,41 +100,44 @@ } else { global.ReconnectingWebSocket = factory(); } -})(this, function () { - + })(this, function () { + if (!('WebSocket' in window)) { return; } - + function ReconnectingWebSocket(url, protocols, options) { - + // Default settings var settings = { - + /** Whether this instance should log debug messages. */ debug: false, - + /** Whether or not the websocket should attempt to connect immediately upon instantiation. */ automaticOpen: true, - + /** The number of milliseconds to delay before attempting to reconnect. */ reconnectInterval: 1000, /** The maximum number of milliseconds to delay a reconnection attempt. */ maxReconnectInterval: 30000, /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */ reconnectDecay: 1.5, - + /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */ timeoutInterval: 2000, - + /** The maximum number of reconnection attempts to make. Unlimited if null. */ maxReconnectAttempts: null, - + /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */ - binaryType: 'blob' + binaryType: 'blob', + + /** The Callback to js to fetch new url if found unauthorized. */ + fetchUrlCallback: '' } if (!options) { options = {}; } - + // Overwrite and define settings with options if they exist. for (var key in settings) { if (typeof options[key] !== 'undefined') { @@ -143,51 +146,51 @@ this[key] = settings[key]; } } - + // These should be treated as read-only properties - + /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */ this.url = url; - + /** The number of attempted reconnects since starting, or the last successful connection. Read only. */ this.reconnectAttempts = 0; - + /** * The current state of the connection. * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED * Read only. */ this.readyState = WebSocket.CONNECTING; - + /** * A string indicating the name of the sub-protocol the server selected; this will be one of * the strings specified in the protocols parameter when creating the WebSocket object. * Read only. */ this.protocol = null; - + // Private state variables - + var self = this; var ws; var forcedClose = false; var timedOut = false; var eventTarget = document.createElement('div'); - + // Wire up "on*" properties as event handlers - + eventTarget.addEventListener('open', function(event) { self.onopen(event); }); eventTarget.addEventListener('close', function(event) { self.onclose(event); }); eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); }); eventTarget.addEventListener('message', function(event) { self.onmessage(event); }); eventTarget.addEventListener('error', function(event) { self.onerror(event); }); - + // Expose the API required by EventTarget - + this.addEventListener = eventTarget.addEventListener.bind(eventTarget); this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget); this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget); - + /** * This function generates an event that is compatible with standard * compliant browsers and IE9 - IE11 @@ -200,15 +203,18 @@ * @param args Object an optional object that the event will use */ function generateEvent(s, args) { - var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(s, false, false, args); - return evt; + var evt = document.createEvent("CustomEvent"); + evt.initCustomEvent(s, false, false, args); + return evt; }; - - this.open = function (reconnectAttempt) { + + this.open = function (reconnectAttempt, newUrl) { + if(newUrl) { + this.url = newUrl; + } ws = new WebSocket(self.url, protocols || []); ws.binaryType = this.binaryType; - + if (reconnectAttempt) { if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) { return; @@ -217,11 +223,11 @@ eventTarget.dispatchEvent(generateEvent('connecting')); this.reconnectAttempts = 0; } - + if (self.debug || ReconnectingWebSocket.debugAll) { console.debug('ReconnectingWebSocket', 'attempt-connect', self.url); } - + var localWs = ws; var timeout = setTimeout(function() { if (self.debug || ReconnectingWebSocket.debugAll) { @@ -231,7 +237,7 @@ localWs.close(); timedOut = false; }, self.timeoutInterval); - + ws.onopen = function(event) { clearTimeout(timeout); if (self.debug || ReconnectingWebSocket.debugAll) { @@ -245,7 +251,7 @@ reconnectAttempt = false; eventTarget.dispatchEvent(e); }; - + ws.onclose = function(event) { clearTimeout(timeout); ws = null; @@ -265,11 +271,15 @@ } eventTarget.dispatchEvent(generateEvent('close')); } - + var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts); setTimeout(function() { - self.reconnectAttempts++; - self.open(true); + self.reconnectAttempts++; + self.fetchUrlCallback().then(function(successData){ + var newUrl = successData.url ? successData.url : self.url; + self.open(true, newUrl); + + }) }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout); } }; @@ -288,12 +298,12 @@ eventTarget.dispatchEvent(generateEvent('error')); }; } - + // Whether or not to create a websocket upon instantiation if (this.automaticOpen == true) { this.open(false); } - + /** * Transmits data to the server over the WebSocket connection. * @@ -309,7 +319,7 @@ throw 'INVALID_STATE_ERR : Pausing to reconnect websocket'; } }; - + /** * Closes the WebSocket connection or connection attempt, if any. * If the connection is already CLOSED, this method does nothing. @@ -324,7 +334,7 @@ ws.close(code, reason); } }; - + /** * 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. @@ -335,7 +345,7 @@ } }; } - + /** * An event listener to be called when the WebSocket connection's readyState changes to OPEN; * this indicates that the connection is ready to send and receive data. @@ -349,17 +359,17 @@ ReconnectingWebSocket.prototype.onmessage = function(event) {}; /** An event listener to be called when an error occurs. */ ReconnectingWebSocket.prototype.onerror = function(event) {}; - + /** * Whether all instances of ReconnectingWebSocket should log debug messages. * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true. */ ReconnectingWebSocket.debugAll = false; - + ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING; ReconnectingWebSocket.OPEN = WebSocket.OPEN; ReconnectingWebSocket.CLOSING = WebSocket.CLOSING; ReconnectingWebSocket.CLOSED = WebSocket.CLOSED; - + return ReconnectingWebSocket; -}); + }); \ No newline at end of file diff --git a/reconnecting-websocket.min.js b/reconnecting-websocket.min.js index 3015099..880aa91 100644 --- a/reconnecting-websocket.min.js +++ b/reconnecting-websocket.min.js @@ -1 +1 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a}); +!function(e,n){"function"==typeof define&&define.amd?define([],n):"undefined"!=typeof module&&module.exports?module.exports=n():e.ReconnectingWebSocket=n()}(this,function(){if("WebSocket"in window)return e.prototype.onopen=function(e){},e.prototype.onclose=function(e){},e.prototype.onconnecting=function(e){},e.prototype.onmessage=function(e){},e.prototype.onerror=function(e){},e.debugAll=!1,e.CONNECTING=WebSocket.CONNECTING,e.OPEN=WebSocket.OPEN,e.CLOSING=WebSocket.CLOSING,e.CLOSED=WebSocket.CLOSED,e;function e(n,t,o){var c={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3,maxReconnectAttempts:null,binaryType:"blob",fetchUrlCallback:""};o||(o={});for(var i in c)void 0!==o[i]?this[i]=o[i]:this[i]=c[i];this.url=n,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var r,s=this,u=!1,a=!1,l=document.createElement("div");function d(e,n){var t=document.createEvent("CustomEvent");return t.initCustomEvent(e,!1,!1,n),t}l.addEventListener("open",function(e){s.onopen(e)}),l.addEventListener("close",function(e){s.onclose(e)}),l.addEventListener("connecting",function(e){s.onconnecting(e)}),l.addEventListener("message",function(e){s.onmessage(e)}),l.addEventListener("error",function(e){s.onerror(e)}),this.addEventListener=l.addEventListener.bind(l),this.removeEventListener=l.removeEventListener.bind(l),this.dispatchEvent=l.dispatchEvent.bind(l),this.open=function(n,o){if(o&&(this.url=o),(r=new WebSocket(s.url,t||[])).binaryType=this.binaryType,n){if(this.maxReconnectAttempts&&this.reconnectAttempts>this.maxReconnectAttempts)return}else l.dispatchEvent(d("connecting")),this.reconnectAttempts=0;(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",s.url);var c=r,i=setTimeout(function(){(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",s.url),a=!0,c.close(),a=!1},s.timeoutInterval);r.onopen=function(t){clearTimeout(i),(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","onopen",s.url),s.protocol=r.protocol,s.readyState=WebSocket.OPEN,s.reconnectAttempts=0;var o=d("open");o.isReconnect=n,n=!1,l.dispatchEvent(o)},r.onclose=function(t){if(clearTimeout(c),r=null,u)s.readyState=WebSocket.CLOSED,l.dispatchEvent(d("close"));else{s.readyState=WebSocket.CONNECTING;var o=d("connecting");o.code=t.code,o.reason=t.reason,o.wasClean=t.wasClean,l.dispatchEvent(o),n||a||((s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","onclose",s.url),l.dispatchEvent(d("close")));var c=s.reconnectInterval*Math.pow(s.reconnectDecay,s.reconnectAttempts);setTimeout(function(){s.reconnectAttempts++,s.fetchUrlCallback().then(function(e){var n=e.url?e.url:s.url;s.open(!0,n)})},c>s.maxReconnectInterval?s.maxReconnectInterval:c)}},r.onmessage=function(n){(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",s.url,n.data);var t=d("message");t.data=n.data,l.dispatchEvent(t)},r.onerror=function(n){(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","onerror",s.url,n),l.dispatchEvent(d("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(n){if(r)return(s.debug||e.debugAll)&&console.debug("ReconnectingWebSocket","send",s.url,n),r.send(n);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(e,n){void 0===e&&(e=1e3),u=!0,r&&r.close(e,n)},this.refresh=function(){r&&r.close()}}}); \ No newline at end of file