Avoid closing of web socket during the idle time

"/subscribe" route of web socket is getting closed if it remains
idle for more than 60 seconds. Once web socket get closed, it can't
handle further event notifications. To avoid this, send a ping to
wake it up during constant intervals.

Resolves: Part of openbmc/openbmc#3102

Tested: Running GUI locally and verified the command line and web gui
power on/off operations. Also could not see web socket getting closed
during idle time.

Change-Id: Ic3da0d30b99d1c3ac5ce4311204e6f6b09b8c1f0
Signed-off-by: Jayashankar Padath <jayashankar.padath@in.ibm.com>
diff --git a/module/obmc/wsgi/apps/rest_dbus.py b/module/obmc/wsgi/apps/rest_dbus.py
index bf4fe7c..f92a67a 100644
--- a/module/obmc/wsgi/apps/rest_dbus.py
+++ b/module/obmc/wsgi/apps/rest_dbus.py
@@ -54,6 +54,7 @@
 DBUS_TYPE_ERROR = 'org.freedesktop.DBus.Python.TypeError'
 DELETE_IFACE = 'xyz.openbmc_project.Object.Delete'
 SOFTWARE_PATH = '/xyz/openbmc_project/software'
+WEBSOCKET_TIMEOUT = 45
 
 _4034_msg = "The specified %s cannot be %s: '%s'"
 
@@ -168,6 +169,22 @@
         return converted_container
 
 
+def send_ws_ping(wsock, timeout) :
+    # Most webservers close websockets after 60 seconds of
+    # inactivity. Make sure to send a ping before that.
+    payload = "ping"
+    # the ping payload can be anything, the receiver has to just
+    # return the same back.
+    while True:
+        gevent.sleep(timeout)
+        try:
+            if wsock:
+                wsock.send_frame(payload, wsock.OPCODE_PING)
+        except Exception as e:
+            wsock.close()
+            return
+
+
 class UserInGroup:
     ''' Authorization plugin callback that checks that the user is logged in
     and a member of a group. '''
@@ -943,11 +960,11 @@
         wsock = request.environ.get('wsgi.websocket')
         if not wsock:
             abort(400, 'Expected WebSocket request.')
+        ping_sender = Greenlet.spawn(send_ws_ping, wsock, WEBSOCKET_TIMEOUT)
         filters = wsock.receive()
         filters = json.loads(filters)
         notifier = EventNotifier(wsock, filters)
 
-
 class HostConsoleHandler(RouteHandler):
     ''' Handles the /console route, for clients to be able
         read/write the host serial console. The way this is
@@ -993,17 +1010,6 @@
                 wsock.close()
                 return
 
-    def send_ping(self, wsock) :
-        # Most webservers close websockets after 60 seconds of
-        # inactivity. Make sure to send a ping before that.
-        timeout = 45
-        payload = "ping"
-        # the ping payload can be anything, the receiver has to just
-        # return the same back.
-        while True:
-            gevent.sleep(timeout)
-            wsock.send_frame(payload, wsock.OPCODE_PING)
-
     def do_get(self):
         wsock = request.environ.get('wsgi.websocket')
         if not wsock:
@@ -1023,7 +1029,7 @@
 
         wsock_reader = Greenlet.spawn(self.read_wsock, wsock, sock)
         sock_reader = Greenlet.spawn(self.read_sock, sock, wsock)
-        ping_sender = Greenlet.spawn(self.send_ping, wsock)
+        ping_sender = Greenlet.spawn(send_ws_ping, wsock, WEBSOCKET_TIMEOUT)
         gevent.joinall([wsock_reader, sock_reader, ping_sender])