UNPKG

sbis3-node-ws

Version:

Модуль позволяет использовать ядро интерфейсного фреймворка sbis3-ws(`core.js`) и модуль работы с данными (`Source.js`) в приложении на nodejs.

233 lines (206 loc) 7.89 kB
define('js!SBIS3.CORE.ServerEventBus/resources/reconnect', [ 'browser!cdn!/sockjs/1.0.3-p1/sockjs-min.js' ], function(SockJS) { /** * Производим переподключение только в том случае, если код указан в этом хэше и содержит значение true * @see http://tools.ietf.org/html/rfc6455#section-7.4.1 */ var RECONNECT_BY_CODE = { // connection cleanly closed 1000: false, // endpoint going down 1001: false, // some protocol error (STOMP 502) 1002: true, // WTF 1005: true, // "to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame." 1006: true, // message too big 1009: true, // All transports failed 2000: true, // Transport timeouted 2007: true, // Transport timeout in reconnect 3007: true }; var PROTOCOLS = [ 'xhr-streaming', 'xhr-polling', 'xdr-streaming', 'xdr-polling', 'iframe-xhr-polling' ]; if (typeof window !== 'undefined' && window.WebSocket) { PROTOCOLS.unshift('websocket'); } function ReconnectingSockJS(url) { // These can be altered by calling code. this.debug = false; this.timeoutInterval = 5000; this.reconnectFailures = 0; var self = this; var ws; var forcedClose = false; var timedOut = false; this.url = url; this.transports = PROTOCOLS; this.readyState = 0; this.URL = url; // Public API this.transport = ''; this.getReconnectInterval = function() { // Максимальный интервал переподключения - 1 час return Math.min(Math.exp(this.reconnectFailures / 2) * 300 + 1000, 60 * 60 * 1000); }; /** * Reconnect only on unclean connection shutdown * @param event * @returns {*|boolean} */ this.isNeedReconnect = function(event) { return (event && RECONNECT_BY_CODE[event.code] || false); }; this.onopen = function(event) { }; this.onclose = function(event) { }; this.onconnecting = function(event) { }; this.onmessage = function(event) { }; this.onerror = function(event) { }; function connect(reconnectAttempt) { if (forcedClose && (self.debug || ReconnectingSockJS.debugAll)) { console.error('ReconnectingSockJS', 'connection when forcedClose', url); } var config = PROTOCOLS.length ? { transports: PROTOCOLS } : undefined; ws = new SockJS(url, null, config); self.onconnecting && self.onconnecting(); if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'attempt-connect', url); } var localWs = ws; var timeout = setTimeout(function() { if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'connection-timeout', url); } timedOut = true; localWs.close(3007, "Transport timeout"); timedOut = false; }, self.timeoutInterval); ws.onopen = function(event) { clearTimeout(timeout); if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'onopen', url); } self.readyState = 1; self.transport = ws.transport; reconnectAttempt = false; self.reconnectFailures = 0; self.onopen && self.onopen(event); }; ws.onclose = function(event) { if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'underlyingSocket.onclose', JSON.stringify(event)); } clearTimeout(timeout); if (forcedClose) { self.readyState = 3; if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'manually closed', url); } self.onclose && self.onclose(event); } else { self.readyState = 0; if (!reconnectAttempt && !timedOut) { // Между onclose и onopen все другие onclose игнорируются пока не удастся подключиться if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'onclose', url); } self.onclose && self.onclose(event); // Если кто то решил закрыть сокет во время onclose if (forcedClose) { forcedClose = false; } } self.reconnectFailures++; if (self.isNeedReconnect(event)) { var interval = self.getReconnectInterval(); if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'Planning to reconnect in ' + interval, url); } setTimeout(function() { connect(true); }, interval); } else { self.pausingToReconnectCloseEvent = { event: event, stack: (new Error().stack) }; if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'needReconnect=false. Give up reconnecting.', url); } } } ws = null; }; ws.onmessage = function(event) { if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'onmessage', url, event.data); } self.onmessage && self.onmessage(event); }; ws.onerror = function(event) { if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'onerror', url, event); } self.onerror && self.onerror(event); }; } connect(); this.send = function(data) { if (ws) { if (self.debug || ReconnectingSockJS.debugAll) { console.log('ReconnectingSockJS', 'send', url, data); } try { return ws.send(data); } catch(e) { if (e.message != 'InvalidStateError: The connection has not been established yet') { throw e; } } } else if (!!self.pausingToReconnectCloseEvent) { throw 'INVALID_STATE_ERR : Pausing to reconnect websocket. Close code: ' + self.pausingToReconnectCloseEvent.event.code + '. \n' + self.pausingToReconnectCloseEvent.stack; } else { throw 'INVALID_STATE_ERR : Pausing to reconnect websocket' + '. \n' + (new Error().stack); } }; 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 ReconnectingSockJS.debug to true. */ ReconnectingSockJS.debugAll = false; return ReconnectingSockJS; });