1 /* 2 * Copyright (c) 2016-2019 LabKey Corporation 3 * 4 * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 5 */ 6 if(!LABKEY.WebSocket) { 7 LABKEY.WebSocket = {}; 8 } 9 10 LABKEY.WebSocket = new function () 11 { 12 var $ = jQuery; 13 var _websocket = null; 14 var _callbacks = {}; 15 16 function openWebsocket() { 17 _websocket = new WebSocket((window.location.protocol==="http:"?"ws:":"wss:") + "//" + window.location.host + LABKEY.contextPath + "/_websocket/notifications"); 18 _websocket.onmessage = websocketOnMessage; 19 _websocket.onclose = websocketOnclose; 20 } 21 22 function websocketOnMessage (evt) { 23 var json = JSON.parse(evt.data); 24 var event = json.event; 25 console.info("websocket.onmessage", event); 26 27 var list = _callbacks[event] || []; 28 list.forEach(function(cb){cb(json)}); 29 } 30 31 function websocketOnclose(evt) { 32 console.info("websocket.onclose", evt); 33 34 if (evt.wasClean) 35 { 36 // first chance at handling the event goes to any registered callback listeners 37 if (_callbacks[evt.code]) { 38 _callbacks[evt.code].forEach(function(cb){cb(evt)}); 39 } 40 else if (_callbacks[evt.reason]) { 41 _callbacks[evt.reason].forEach(function(cb){cb(evt)}); 42 } 43 else if (evt.code === 1000 || evt.code === 1003) { 44 // normal close 45 if (evt.reason === "org.labkey.api.security.AuthNotify#LoggedOut") { 46 setTimeout(function(){ 47 displayModal('Logged Out', 'You have been logged out. Please reload the page to continue.'); 48 }, 1000); 49 } 50 } 51 else if (evt.code === 1001 && evt.reason && evt.reason !== "") { 52 // 1001 sent when server is shutdown normally (AND on page reload in FireFox, but that one doesn't have a reason) 53 setTimeout(showDisconnectedMessage, 1000); 54 } 55 else if (evt.code === 1006) { 56 // 1006 abnormal close (e.g, server process died) 57 setTimeout(showDisconnectedMessage, 1000); 58 } 59 else if (evt.code === 1008) { 60 // Tomcat closes the websocket with "1008 Policy Violation" code when the session has expired. 61 // evt.reason === "This connection was established under an authenticated HTTP session that has ended." 62 setTimeout(function() { 63 LABKEY.Ajax.request({ 64 url: LABKEY.ActionURL.buildURL("login", "whoami.api"), 65 success: LABKEY.Utils.getCallbackWrapper(function(response) { 66 // If the user was previously a guest, don't warn them about session expiration as they 67 // just logged in. See issue 39337 68 if (LABKEY.user.id !== response.id && !LABKEY.user.isGuest) { 69 displayModal("Session Expired", 'Your session has expired. Please reload the page to continue.'); 70 } 71 }), 72 failure: function () { 73 setTimeout(showDisconnectedMessage, 1000); 74 } 75 }); 76 }, 1000); 77 } 78 } 79 } 80 81 function showDisconnectedMessage() { 82 displayModal("Server Unavailable", "The server is currently unavailable. Please try reloading the page to continue."); 83 // CONSIDER: Periodically attempt to reestablish connection until the server comes back up. 84 // CONSIDER: Once reconnected, reload the page unless page is dirty -- LABKEY.isDirty() 85 } 86 87 function displayModal(title, message) { 88 if (LABKEY.Utils && LABKEY.Utils.modal) { 89 LABKEY.Utils.modal(title, null, function() { 90 $("#modal-fn-body").html([ 91 '<div style="margin-bottom: 40px;">', 92 '<p>' + message + '<br></p>', 93 '<a class="btn btn-default" style="float: right" id="lk-websocket-reload">Reload Page</a>', 94 '</div>', 95 ].join('')); 96 97 // add the on click handler for the reload page button 98 $('#lk-websocket-reload').on('click', function() { 99 window.location.reload(); 100 }); 101 }, null, true); 102 } 103 else { 104 // fall back to using standard alert message if for some reason the jQuery modal isn't available 105 alert(message); 106 } 107 } 108 109 /** Add a general purpose listener for server events. */ 110 var addServerEventListener = function(event, cb) { 111 if (LABKEY.user.isSignedIn && 'WebSocket' in window) { 112 if (null === _websocket) { 113 openWebsocket(); 114 } 115 116 var list = _callbacks[event] || []; 117 list.push(cb); 118 _callbacks[event] = list; 119 } 120 }; 121 122 // initial call open the WebSocket to at least handle the logout and session timeout events 123 // other apps or code can register their own event listeners as well 124 openWebsocket(); 125 126 return { 127 addServerEventListener: addServerEventListener 128 }; 129 }; 130