From df160c0d44e8d0439d54313f097b2a4d9ada7357 Mon Sep 17 00:00:00 2001
From: Ivan Alejandro <ivanalejandro0@gmail.com>
Date: Thu, 24 Jul 2014 12:54:12 -0300
Subject: Allow frontend and backend to be run separately.

Add the 'check_online' method to check whether the backend is accessible
or not.
Reduce the wait for running threads timeout on quit.
Add retry feature to the backend requests send.
---
 src/leap/bitmask/app.py                   | 31 +++++++++++++++++++++----------
 src/leap/bitmask/backend/backend.py       |  2 +-
 src/leap/bitmask/backend/backend_proxy.py | 30 ++++++++++++++++++++++++++++--
 src/leap/bitmask/backend/signaler.py      |  1 +
 src/leap/bitmask/backend_app.py           | 13 +++++++++++--
 src/leap/bitmask/frontend_app.py          |  2 +-
 src/leap/bitmask/gui/app.py               |  1 +
 7 files changed, 64 insertions(+), 16 deletions(-)

(limited to 'src')

diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py
index 87f22d88..ef156671 100644
--- a/src/leap/bitmask/app.py
+++ b/src/leap/bitmask/app.py
@@ -44,6 +44,7 @@ import os
 import sys
 
 
+from leap.bitmask.backend.backend_proxy import BackendProxy
 from leap.bitmask.backend.utils import generate_certificates
 
 from leap.bitmask import __version__ as VERSION
@@ -178,19 +179,29 @@ def start_app():
 
     logger.info('Starting app')
 
-    generate_certificates()
+    backend = BackendProxy()
+    backend_running = backend.check_online()
 
-    flags_dict = flags_to_dict()
+    logger.debug("Backend online: {0}".format(backend_running))
+
+    if not backend_running:
+        generate_certificates()
 
-    frontend_pid = os.getpid()
-    backend = lambda: run_backend(opts.danger, flags_dict, frontend_pid)
-    backend_process = multiprocessing.Process(target=backend, name='Backend')
-    # we don't set the 'daemon mode' since we need to start child processes in
-    # the backend
-    # backend_process.daemon = True
-    backend_process.start()
+    flags_dict = flags_to_dict()
 
-    run_frontend(options, flags_dict, backend_pid=backend_process.pid)
+    backend_pid = None
+    if not backend_running:
+        frontend_pid = os.getpid()
+        backend = lambda: run_backend(opts.danger, flags_dict, frontend_pid)
+        backend_process = multiprocessing.Process(target=backend,
+                                                  name='Backend')
+        # we don't set the 'daemon mode' since we need to start child processes
+        # in the backend
+        # backend_process.daemon = True
+        backend_process.start()
+        backend_pid = backend_process.pid
+
+    run_frontend(options, flags_dict, backend_pid=backend_pid)
 
 
 if __name__ == "__main__":
diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py
index 37535f37..75eff8a9 100644
--- a/src/leap/bitmask/backend/backend.py
+++ b/src/leap/bitmask/backend/backend.py
@@ -135,7 +135,7 @@ class Backend(object):
         i.e.:
             use threads.deferToThread(this_method) instead of this_method()
         """
-        wait_max = 5  # seconds
+        wait_max = 3  # seconds
         wait_step = 0.5
         wait = 0
         while self._ongoing_defers and wait < wait_max:
diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py
index e2611251..9de3501e 100644
--- a/src/leap/bitmask/backend/backend_proxy.py
+++ b/src/leap/bitmask/backend/backend_proxy.py
@@ -67,6 +67,7 @@ class BackendProxy(object):
         socket.curve_serverkey = public
 
         socket.setsockopt(zmq.RCVTIMEO, 1000)
+        socket.setsockopt(zmq.LINGER, 0)  # Terminate early
         socket.connect(self.SERVER)
         self._socket = socket
 
@@ -75,8 +76,23 @@ class BackendProxy(object):
 
         self._call_queue = Queue.Queue()
         self._worker_caller = threading.Thread(target=self._worker)
+
+    def start(self):
         self._worker_caller.start()
 
+    def check_online(self):
+        """
+        Return whether the backend is accessible or not.
+        You don't need to do `run` in order to use this.
+
+        :rtype: bool
+        """
+        # we use a small timeout in order to response quickly if the backend is
+        # offline
+        self._send_request(PING_REQUEST, retry=False, timeout=500)
+        self._socket.close()
+        return self.online
+
     def _worker(self):
         """
         Worker loop that processes the Queue of pending requests to do.
@@ -150,7 +166,7 @@ class BackendProxy(object):
         if api_method == STOP_REQUEST:
             self._call_queue.put(STOP_REQUEST)
 
-    def _send_request(self, request):
+    def _send_request(self, request, retry=True, timeout=None):
         """
         Send the given request to the server.
         This is used from a thread safe loop in order to avoid sending a
@@ -158,6 +174,10 @@ class BackendProxy(object):
 
         :param request: the request to send.
         :type request: str
+        :param retry: whether we should retry or not in case of timeout.
+        :type retry: bool
+        :param timeout: a custom timeout (milliseconds) to wait for a response.
+        :type timeout: int
         """
         # logger.debug("Sending request to backend: {0}".format(request))
         self._socket.send(request)
@@ -166,10 +186,16 @@ class BackendProxy(object):
         poll.register(self._socket, zmq.POLLIN)
 
         reply = None
+
         tries = 0
+        if not retry:
+            tries = self.POLL_TRIES + 1  # this means: no retries left
+
+        if timeout is None:
+            timeout = self.POLL_TIMEOUT
 
         while True:
-            socks = dict(poll.poll(self.POLL_TIMEOUT))
+            socks = dict(poll.poll(timeout))
             if socks.get(self._socket) == zmq.POLLIN:
                 reply = self._socket.recv()
                 break
diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py
index 574bfa71..43cba994 100644
--- a/src/leap/bitmask/backend/signaler.py
+++ b/src/leap/bitmask/backend/signaler.py
@@ -60,6 +60,7 @@ class Signaler(object):
         socket.curve_serverkey = public
 
         socket.setsockopt(zmq.RCVTIMEO, 1000)
+        socket.setsockopt(zmq.LINGER, 0)  # Terminate early
         socket.connect(self.SERVER)
         self._socket = socket
 
diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py
index 716ae4a7..ce75dc80 100644
--- a/src/leap/bitmask/backend_app.py
+++ b/src/leap/bitmask/backend_app.py
@@ -22,6 +22,8 @@ import multiprocessing
 import signal
 
 from leap.bitmask.backend.leapbackend import LeapBackend
+from leap.bitmask.backend.utils import generate_certificates
+from leap.bitmask.logs.utils import create_logger
 from leap.bitmask.util import dict_to_flags
 
 logger = logging.getLogger(__name__)
@@ -44,7 +46,7 @@ def signal_handler(signum, frame):
     logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum))
 
 
-def run_backend(bypass_checks, flags_dict, frontend_pid=None):
+def run_backend(bypass_checks=False, flags_dict=None, frontend_pid=None):
     """
     Run the backend for the application.
 
@@ -57,8 +59,15 @@ def run_backend(bypass_checks, flags_dict, frontend_pid=None):
     signal.signal(signal.SIGINT, signal.SIG_IGN)
     signal.signal(signal.SIGTERM, signal_handler)
 
-    dict_to_flags(flags_dict)
+    if flags_dict is not None:
+        dict_to_flags(flags_dict)
 
     backend = LeapBackend(bypass_checks=bypass_checks,
                           frontend_pid=frontend_pid)
     backend.run()
+
+
+if __name__ == '__main__':
+    logger = create_logger(debug=True)
+    generate_certificates()
+    run_backend()
diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py
index 909005f0..b0a149f9 100644
--- a/src/leap/bitmask/frontend_app.py
+++ b/src/leap/bitmask/frontend_app.py
@@ -54,7 +54,7 @@ def signal_handler(window, pid, signum, frame):
         window.quit()
 
 
-def run_frontend(options, flags_dict, backend_pid):
+def run_frontend(options, flags_dict, backend_pid=None):
     """
     Run the GUI for the application.
 
diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py
index eb1a58d5..75dc4a38 100644
--- a/src/leap/bitmask/gui/app.py
+++ b/src/leap/bitmask/gui/app.py
@@ -41,6 +41,7 @@ class App(QtGui.QWidget):
 
         self.settings = LeapSettings()
         self.backend = BackendProxy()
+        self.backend.start()
         self.signaler = LeapSignaler()
         self.signaler.start()
 
-- 
cgit v1.2.3