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