[tor-commits] [ooni-probe/master] Start the director after the GUI is available. Implement a long polling mechanism.

art at torproject.org art at torproject.org
Mon Sep 19 12:14:24 UTC 2016


commit f7f272d93a640d091855c7ca85c274b3aa7113e9
Author: Arturo Filastò <arturo at filasto.net>
Date:   Mon Jul 18 19:18:39 2016 +0200

    Start the director after the GUI is available. Implement a long polling mechanism.
---
 ooni/ui/web/server.py | 80 +++++++++++++++++++++++++++++++++++++++++++++------
 ooni/ui/web/web.py    | 14 ++-------
 2 files changed, 75 insertions(+), 19 deletions(-)

diff --git a/ooni/ui/web/server.py b/ooni/ui/web/server.py
index ccc5d87..ca51041 100644
--- a/ooni/ui/web/server.py
+++ b/ooni/ui/web/server.py
@@ -3,7 +3,7 @@ from __future__ import print_function
 import os
 import json
 
-from twisted.internet import defer
+from twisted.internet import defer, task, reactor
 from twisted.python import usage
 from twisted.python.filepath import FilePath, InsecurePath
 from twisted.web import static
@@ -11,6 +11,7 @@ from twisted.web import static
 from klein import Klein
 from werkzeug.exceptions import NotFound
 
+from ooni import __version__ as ooniprobe_version
 from ooni import errors
 from ooni.deck import Deck
 from ooni.settings import config
@@ -46,11 +47,74 @@ def getNetTestLoader(test_options, test_file):
 
 class WebUIAPI(object):
     app = Klein()
+    # Maximum number in seconds after which to return a result even if not
+    # change happenned.
+    _long_polling_timeout = 5
+    _reactor = reactor
 
     def __init__(self, config, director):
         self.director = director
         self.config = config
-        self.active_measurements = {}
+        self.status = {
+            "software_version": ooniprobe_version,
+            "software_name": "ooniprobe",
+            "asn": config.probe_ip.geodata['asn'],
+            "country_code": config.probe_ip.geodata['countrycode'],
+            "active_measurements": {},
+            "completed_measurements": [],
+            "director_started": False,
+            "failures": []
+        }
+        self.status_updates = []
+        d = self.director.start(start_tor=True)
+
+        d.addCallback(self.director_started)
+        d.addErrback(self.director_startup_failed)
+        d.addBoth(lambda _: self.broadcast_status_update())
+
+    def add_failure(self, failure):
+        self.status['failures'].append(failure)
+
+    def director_started(self, _):
+        self.status['director_started'] = True
+        self.status["asn"] = config.probe_ip.geodata['asn']
+        self.status["country_code"] = config.probe_ip.geodata['countrycode']
+
+    def director_startup_failed(self, failure):
+        self.add_failure(failure)
+
+    def broadcast_status_update(self):
+        for su in self.status_updates:
+            if not su.called:
+                su.callback(None)
+
+    def completed_measurement(self, measurement_id):
+        del self.status['active_measurements'][measurement_id]
+        self.status['completed_measurements'].append(measurement_id)
+
+    def failed_measurement(self, measurement_id, failure):
+        del self.status['active_measurements'][measurement_id]
+        self.add_failure(failure)
+
+    @app.route('/api/status', methods=["GET"])
+    def api_status(self, request):
+        print("Rendering status...")
+        return self.render_json(self.status, request)
+
+    @app.route('/api/status/update', methods=["GET"])
+    def api_status_update(self, request):
+        status_update = defer.Deferred()
+        status_update.addCallback(lambda _:
+                                  self.status_updates.remove(status_update))
+        status_update.addCallback(lambda _: self.api_status(request))
+
+        self.status_updates.append(status_update)
+
+        # After long_polling_timeout we fire the callback
+        task.deferLater(self._reactor, self._long_polling_timeout,
+                        status_update.callback, None)
+
+        return status_update
 
     @app.handle_errors(NotFound)
     def not_found(self, request, _):
@@ -91,11 +155,15 @@ class WebUIAPI(object):
             report_filename = os.path.join(measurement_dir,
                                            "measurements.njson")
             measurement_ids.append(measurement_id)
-            self.active_measurements[measurement_id] = {
+            self.status['active_measurements'][measurement_id] = {
                 'test_name': test_details['test_name'],
                 'test_start_time': test_details['test_start_time']
             }
-            self.director.startNetTest(net_test_loader, report_filename)
+            self.broadcast_status_update()
+            d = self.director.startNetTest(net_test_loader, report_filename)
+            d.addCallback(lambda _: self.completed_measurement(measurement_id))
+            d.addErrback(lambda failure:
+                         self.failed_measurement(measurement_id, failure))
 
     @app.route('/api/nettest/<string:test_name>/start', methods=["POST"])
     def api_nettest_start(self, request, test_name):
@@ -147,10 +215,6 @@ class WebUIAPI(object):
     def api_nettest_list(self, request):
         return self.render_json(self.director.netTests, request)
 
-    @app.route('/api/status', methods=["GET"])
-    def api_status(self):
-        return self.render_json()
-
     @app.route('/api/measurement', methods=["GET"])
     def api_measurement_list(self, request):
         measurement_ids = os.listdir(os.path.join(config.ooni_home,
diff --git a/ooni/ui/web/web.py b/ooni/ui/web/web.py
index 6c6971c..40ee3b4 100644
--- a/ooni/ui/web/web.py
+++ b/ooni/ui/web/web.py
@@ -19,19 +19,11 @@ class WebUIService(service.MultiService):
         config.set_paths()
         config.initialize_ooni_home()
         config.read_config_file()
-        def _started(res):
-            log.msg("Director started")
-            root = server.Site(WebUIAPI(config, director).app.resource())
-            self._port = reactor.listenTCP(self.portNum, root)
         director = Director()
+        web_ui_api = WebUIAPI(config, director)
+        root = server.Site(web_ui_api.app.resource())
+        self._port = reactor.listenTCP(self.portNum, root)
         d = director.start()
-        d.addCallback(_started)
-        d.addErrback(self._startupFailed)
-
-    def _startupFailed(self, err):
-        log.err("Failed to start the director")
-        log.exception(err)
-        os.abort()
 
     def stopService(self):
         if self._port:





More information about the tor-commits mailing list