1 | // MIT License: |
2 | // |
3 | // Copyright (c) 2010-2012, Joe Walnes |
4 | // |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | // of this software and associated documentation files (the "Software"), to deal |
7 | // in the Software without restriction, including without limitation the rights |
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | // copies of the Software, and to permit persons to whom the Software is |
10 | // furnished to do so, subject to the following conditions: |
11 | // |
12 | // The above copyright notice and this permission notice shall be included in |
13 | // all copies or substantial portions of the Software. |
14 | // |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
21 | // THE SOFTWARE. |
22 | |
23 | /** |
24 | * This behaves like a WebSocket in every way, except if it fails to connect, |
25 | * or it gets disconnected, it will repeatedly poll until it successfully connects |
26 | * again. |
27 | * |
28 | * It is API compatible, so when you have: |
29 | * ws = new WebSocket('ws://....'); |
30 | * you can replace with: |
31 | * ws = new ReconnectingWebSocket('ws://....'); |
32 | * |
33 | * The event stream will typically look like: |
34 | * onconnecting |
35 | * onopen |
36 | * onmessage |
37 | * onmessage |
38 | * onclose // lost connection |
39 | * onconnecting |
40 | * onopen // sometime later... |
41 | * onmessage |
42 | * onmessage |
43 | * etc... |
44 | * |
45 | * It is API compatible with the standard WebSocket API, apart from the following members: |
46 | * |
47 | * - `bufferedAmount` |
48 | * - `extensions` |
49 | * - `binaryType` |
50 | * |
51 | * Latest version: https://github.com/joewalnes/reconnecting-websocket/ |
52 | * - Joe Walnes |
53 | * |
54 | * Syntax |
55 | * ====== |
56 | * var socket = new ReconnectingWebSocket(url, protocols, options); |
57 | * |
58 | * Parameters |
59 | * ========== |
60 | * url - The url you are connecting to. |
61 | * protocols - Optional string or array of protocols. |
62 | * options - See below |
63 | * |
64 | * Options |
65 | * ======= |
66 | * Options can either be passed upon instantiation or set after instantiation: |
67 | * |
68 | * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 }); |
69 | * |
70 | * or |
71 | * |
72 | * var socket = new ReconnectingWebSocket(url); |
73 | * socket.debug = true; |
74 | * socket.reconnectInterval = 4000; |
75 | * |
76 | * debug |
77 | * - Whether this instance should log debug messages. Accepts true or false. Default: false. |
78 | * |
79 | * automaticOpen |
80 | * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close(). |
81 | * |
82 | * reconnectInterval |
83 | * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000. |
84 | * |
85 | * maxReconnectInterval |
86 | * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000. |
87 | * |
88 | * reconnectDecay |
89 | * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5. |
90 | * |
91 | * timeoutInterval |
92 | * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000. |
93 | * |
94 | */ |
95 | (function (global, factory) { |
96 | if (typeof define === 'function' && define.amd) { |
97 | define([], factory); |
98 | } else if (typeof module !== 'undefined' && module.exports){ |
99 | module.exports = factory(); |
100 | } else { |
101 | global.ReconnectingWebSocket = factory(); |
102 | } |
103 | })(this, function () { |
104 | |
105 | if (!('WebSocket' in window)) { |
106 | return; |
107 | } |
108 | |
109 | function ReconnectingWebSocket(url, protocols, options) { |
110 | |
111 | // Default settings |
112 | var settings = { |
113 | |
114 | /** Whether this instance should log debug messages. */ |
115 | debug: false, |
116 | |
117 | /** Whether or not the websocket should attempt to connect immediately upon instantiation. */ |
118 | automaticOpen: true, |
119 | |
120 | /** The number of milliseconds to delay before attempting to reconnect. */ |
121 | reconnectInterval: 1000, |
122 | /** The maximum number of milliseconds to delay a reconnection attempt. */ |
123 | maxReconnectInterval: 30000, |
124 | /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */ |
125 | reconnectDecay: 1.5, |
126 | |
127 | /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */ |
128 | timeoutInterval: 2000, |
129 | |
130 | /** The maximum number of reconnection attempts to make. Unlimited if null. */ |
131 | maxReconnectAttempts: null, |
132 | |
133 | /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */ |
134 | binaryType: 'blob' |
135 | } |
136 | if (!options) { options = {}; } |
137 | |
138 | // Overwrite and define settings with options if they exist. |
139 | for (var key in settings) { |
140 | if (typeof options[key] !== 'undefined') { |
141 | this[key] = options[key]; |
142 | } else { |
143 | this[key] = settings[key]; |
144 | } |
145 | } |
146 | |
147 | // These should be treated as read-only properties |
148 | |
149 | /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */ |
150 | this.url = url; |
151 | |
152 | /** The number of attempted reconnects since starting, or the last successful connection. Read only. */ |
153 | this.reconnectAttempts = 0; |
154 | |
155 | /** |
156 | * The current state of the connection. |
157 | * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED |
158 | * Read only. |
159 | */ |
160 | this.readyState = WebSocket.CONNECTING; |
161 | |
162 | /** |
163 | * A string indicating the name of the sub-protocol the server selected; this will be one of |
164 | * the strings specified in the protocols parameter when creating the WebSocket object. |
165 | * Read only. |
166 | */ |
167 | this.protocol = null; |
168 | |
169 | // Private state variables |
170 | |
171 | var self = this; |
172 | var ws; |
173 | var forcedClose = false; |
174 | var timedOut = false; |
175 | var eventTarget = document.createElement('div'); |
176 | |
177 | // Wire up "on*" properties as event handlers |
178 | |
179 | eventTarget.addEventListener('open', function(event) { self.onopen(event); }); |
180 | eventTarget.addEventListener('close', function(event) { self.onclose(event); }); |
181 | eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); }); |
182 | eventTarget.addEventListener('message', function(event) { self.onmessage(event); }); |
183 | eventTarget.addEventListener('error', function(event) { self.onerror(event); }); |
184 | |
185 | // Expose the API required by EventTarget |
186 | |
187 | this.addEventListener = eventTarget.addEventListener.bind(eventTarget); |
188 | this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget); |
189 | this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget); |
190 | |
191 | /** |
192 | * This function generates an event that is compatible with standard |
193 | * compliant browsers and IE9 - IE11 |
194 | * |
195 | * This will prevent the error: |
196 | * Object doesn't support this action |
197 | * |
198 | * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563 |
199 | * @param s String The name that the event should use |
200 | * @param args Object an optional object that the event will use |
201 | */ |
202 | function generateEvent(s, args) { |
203 | var evt = document.createEvent("CustomEvent"); |
204 | evt.initCustomEvent(s, false, false, args); |
205 | return evt; |
206 | }; |
207 | |
208 | this.open = function (reconnectAttempt) { |
209 | ws = new WebSocket(self.url, protocols || []); |
210 | ws.binaryType = this.binaryType; |
211 | |
212 | if (reconnectAttempt) { |
213 | if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) { |
214 | return; |
215 | } |
216 | } else { |
217 | eventTarget.dispatchEvent(generateEvent('connecting')); |
218 | this.reconnectAttempts = 0; |
219 | } |
220 | |
221 | if (self.debug || ReconnectingWebSocket.debugAll) { |
222 | console.debug('ReconnectingWebSocket', 'attempt-connect', self.url); |
223 | } |
224 | |
225 | var localWs = ws; |
226 | var timeout = setTimeout(function() { |
227 | if (self.debug || ReconnectingWebSocket.debugAll) { |
228 | console.debug('ReconnectingWebSocket', 'connection-timeout', self.url); |
229 | } |
230 | timedOut = true; |
231 | localWs.close(); |
232 | timedOut = false; |
233 | }, self.timeoutInterval); |
234 | |
235 | ws.onopen = function(event) { |
236 | clearTimeout(timeout); |
237 | if (self.debug || ReconnectingWebSocket.debugAll) { |
238 | console.debug('ReconnectingWebSocket', 'onopen', self.url); |
239 | } |
240 | self.protocol = ws.protocol; |
241 | self.readyState = WebSocket.OPEN; |
242 | self.reconnectAttempts = 0; |
243 | var e = generateEvent('open'); |
244 | e.isReconnect = reconnectAttempt; |
245 | reconnectAttempt = false; |
246 | eventTarget.dispatchEvent(e); |
247 | }; |
248 | |
249 | ws.onclose = function(event) { |
250 | clearTimeout(timeout); |
251 | ws = null; |
252 | if (forcedClose) { |
253 | self.readyState = WebSocket.CLOSED; |
254 | eventTarget.dispatchEvent(generateEvent('close')); |
255 | } else { |
256 | self.readyState = WebSocket.CONNECTING; |
257 | var e = generateEvent('connecting'); |
258 | e.code = event.code; |
259 | e.reason = event.reason; |
260 | e.wasClean = event.wasClean; |
261 | eventTarget.dispatchEvent(e); |
262 | if (!reconnectAttempt && !timedOut) { |
263 | if (self.debug || ReconnectingWebSocket.debugAll) { |
264 | console.debug('ReconnectingWebSocket', 'onclose', self.url); |
265 | } |
266 | eventTarget.dispatchEvent(generateEvent('close')); |
267 | } |
268 | |
269 | var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts); |
270 | setTimeout(function() { |
271 | self.reconnectAttempts++; |
272 | self.open(true); |
273 | }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout); |
274 | } |
275 | }; |
276 | ws.onmessage = function(event) { |
277 | if (self.debug || ReconnectingWebSocket.debugAll) { |
278 | console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data); |
279 | } |
280 | var e = generateEvent('message'); |
281 | e.data = event.data; |
282 | eventTarget.dispatchEvent(e); |
283 | }; |
284 | ws.onerror = function(event) { |
285 | if (self.debug || ReconnectingWebSocket.debugAll) { |
286 | console.debug('ReconnectingWebSocket', 'onerror', self.url, event); |
287 | } |
288 | eventTarget.dispatchEvent(generateEvent('error')); |
289 | }; |
290 | } |
291 | |
292 | // Whether or not to create a websocket upon instantiation |
293 | if (this.automaticOpen == true) { |
294 | this.open(false); |
295 | } |
296 | |
297 | /** |
298 | * Transmits data to the server over the WebSocket connection. |
299 | * |
300 | * @param data a text string, ArrayBuffer or Blob to send to the server. |
301 | */ |
302 | this.send = function(data) { |
303 | if (ws) { |
304 | if (self.debug || ReconnectingWebSocket.debugAll) { |
305 | console.debug('ReconnectingWebSocket', 'send', self.url, data); |
306 | } |
307 | return ws.send(data); |
308 | } else { |
309 | throw 'INVALID_STATE_ERR : Pausing to reconnect websocket'; |
310 | } |
311 | }; |
312 | |
313 | /** |
314 | * Closes the WebSocket connection or connection attempt, if any. |
315 | * If the connection is already CLOSED, this method does nothing. |
316 | */ |
317 | this.close = function(code, reason) { |
318 | // Default CLOSE_NORMAL code |
319 | if (typeof code == 'undefined') { |
320 | code = 1000; |
321 | } |
322 | forcedClose = true; |
323 | if (ws) { |
324 | ws.close(code, reason); |
325 | } |
326 | }; |
327 | |
328 | /** |
329 | * Additional public API method to refresh the connection if still open (close, re-open). |
330 | * For example, if the app suspects bad data / missed heart beats, it can try to refresh. |
331 | */ |
332 | this.refresh = function() { |
333 | if (ws) { |
334 | ws.close(); |
335 | } |
336 | }; |
337 | } |
338 | |
339 | /** |
340 | * An event listener to be called when the WebSocket connection's readyState changes to OPEN; |
341 | * this indicates that the connection is ready to send and receive data. |
342 | */ |
343 | ReconnectingWebSocket.prototype.onopen = function(event) {}; |
344 | /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */ |
345 | ReconnectingWebSocket.prototype.onclose = function(event) {}; |
346 | /** An event listener to be called when a connection begins being attempted. */ |
347 | ReconnectingWebSocket.prototype.onconnecting = function(event) {}; |
348 | /** An event listener to be called when a message is received from the server. */ |
349 | ReconnectingWebSocket.prototype.onmessage = function(event) {}; |
350 | /** An event listener to be called when an error occurs. */ |
351 | ReconnectingWebSocket.prototype.onerror = function(event) {}; |
352 | |
353 | /** |
354 | * Whether all instances of ReconnectingWebSocket should log debug messages. |
355 | * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true. |
356 | */ |
357 | ReconnectingWebSocket.debugAll = false; |
358 | |
359 | ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING; |
360 | ReconnectingWebSocket.OPEN = WebSocket.OPEN; |
361 | ReconnectingWebSocket.CLOSING = WebSocket.CLOSING; |
362 | ReconnectingWebSocket.CLOSED = WebSocket.CLOSED; |
363 | |
364 | return ReconnectingWebSocket; |
365 | }); |
https://github.com/joewalnes/reconnecting-websocket/blob/master/reconnecting-websocket.js
Travelled to 12 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1013375 |
Snippet name: | reconnecting-websocket.js |
Eternal ID of this version: | #1013375/1 |
Text MD5: | 82ee367b01b014bfdcb87736ab90dbba |
Author: | stefan |
Category: | javax |
Type: | Document |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2018-01-03 21:59:22 |
Source code size: | 14792 bytes / 365 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 441 / 456 |
Referenced in: | [show references] |