From 9d8dbb8856ae2b1fca9b4f45ab0882086954f702 Mon Sep 17 00:00:00 2001 From: Nico Alt <nicoalt@posteo.org> Date: Mon, 20 May 2019 00:25:26 +0200 Subject: [PATCH] Start API in background --- src/briar/api/api.py | 65 +++++++++++++++++++++++------ src/briar/gtk/application.py | 8 +--- src/briar/gtk/containers/startup.py | 22 ++++++---- src/briar/gtk/window.py | 8 ++-- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/briar/api/api.py b/src/briar/api/api.py index 97409fa..5afcacf 100644 --- a/src/briar/api/api.py +++ b/src/briar/api/api.py @@ -2,13 +2,17 @@ # SPDX-License-Identifier: AGPL-3.0-only # License-Filename: LICENSE.md -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, STDOUT +from threading import Thread class Api: + _process = None + def __init__(self, headless_jar, debug=False): - self.headless_jar = headless_jar + self._command = ['java', '-jar', headless_jar, '-v'] + self._debug = debug def has_account(self): from pathlib import Path @@ -16,18 +20,55 @@ class Api: from os.path import isdir, join return isdir(join(home, ".briar", "db")) + def is_running(self): + return (self._process is not None) and (self._process.poll() is None) + def login(self, password, callback): - p = Popen(['java', '-jar', self.headless_jar, '-v'], - stdin=PIPE, universal_newlines=True) - p.communicate(password + '\n') - callback() + self._start_and_watch(callback) + startup_thread = Thread(target=self._login, args=(password,), + daemon=True) + startup_thread.start() def register(self, credentials, callback): - p = Popen(['java', '-jar', self.headless_jar, '-v'], - stdin=PIPE, universal_newlines=True) - p.communicate(credentials[0] + '\n' + credentials[1] + - '\n' + credentials[1] + '\n') - callback() + if len(credentials) != 2: + raise Exception("Can't process credentials") + self._start_and_watch(callback) + startup_thread = Thread(target=self._register, args=(credentials,), + daemon=True) + startup_thread.start() def stop(self): - pass + if not self.is_running(): + raise Exception("Nothing to stop") + self._process.terminate() + + def _start_and_watch(self, callback): + if self.is_running(): + raise Exception("API already running") + self._process = Popen(self._command, stdin=PIPE, + stdout=PIPE, stderr=STDOUT) + watch_thread = Thread(target=self._watch_thread, args=(callback,), + daemon=True) + watch_thread.start() + + def _watch_thread(self, callback): + for line in self._process.stdout: + if self._debug: + print(line.decode("utf-8"), end='', flush=True) + # TODO: Sometimes we miss this line (or Briar doesn't send it?) + if "Listening on http://localhost:7000/" in line.decode("utf-8"): + callback(True) + return + callback(False) + + def _login(self, password): + if not self.is_running(): + raise Exception("Can't login; API not running") + self._process.communicate((password + '\n').encode("utf-8")) + + def _register(self, credentials): + if not self.is_running(): + raise Exception("Can't register; API not running") + self._process.communicate((credentials[0] + '\n' + + credentials[1] + '\n' + + credentials[1] + '\n').encode("utf-8")) diff --git a/src/briar/gtk/application.py b/src/briar/gtk/application.py index 5ab8c1d..ba120a5 100644 --- a/src/briar/gtk/application.py +++ b/src/briar/gtk/application.py @@ -36,7 +36,7 @@ class Application(Gtk.Application): Gtk.STYLE_PROVIDER_PRIORITY_USER) self.debug = True # TODO: Change this in production - self.__api = Api('/app/briar/briar-headless.jar', self.debug) + self.api = Api('/app/briar/briar-headless.jar', self.debug) def do_activate(self): if self.window is None: @@ -45,10 +45,6 @@ class Application(Gtk.Application): self.window.present() def quit(self): - self.__api.stop() + self.api.stop() self.window.hide() Gio.Application.quit(self) - - @property - def api(self): - return self.__api diff --git a/src/briar/gtk/containers/startup.py b/src/briar/gtk/containers/startup.py index 2667a6e..251af6d 100644 --- a/src/briar/gtk/containers/startup.py +++ b/src/briar/gtk/containers/startup.py @@ -14,8 +14,9 @@ class StartupContainer(Container): def __init__(self): super().__init__() - self.__setup_view() - self.__register_signals() + self._setup_view() + self._register_signals() + self._api = App().api def on_username_button_clicked(self, button): self.builder.get_object("username_grid").set_visible(False) @@ -24,14 +25,13 @@ class StartupContainer(Container): def on_password_button_clicked(self, button): password = self.builder.get_object("password_entry").get_text() - App().api.register((self.username, password), - self.__startup_completed) + self._api.register((self.username, password), self._startup_finished) def on_login_pressed(self, button): password = self.builder.get_object("password_entry").get_text() - App().api.login(password, self.__startup_completed) + self._api.login(password, self._startup_finished) - def __setup_view(self): + def _setup_view(self): self.set_hexpand(True) self.set_vexpand(True) if not App().api.has_account(): @@ -42,10 +42,14 @@ class StartupContainer(Container): self.add(self.builder.get_object("login")) self.builder.connect_signals(self) - def __register_signals(self): + def _register_signals(self): GObject.signal_new("briar_startup_completed", Gtk.Overlay, GObject.SignalFlags.RUN_LAST, GObject.TYPE_BOOLEAN, (GObject.TYPE_STRING,)) - def __startup_completed(self, succeded=True): - self.emit("briar_startup_completed", ("succeded",)) + def _startup_finished(self, succeeded): + if succeeded: + print("Startup succeeded") + self.emit("briar_startup_completed", (succeeded,)) + return + print("Startup failed") diff --git a/src/briar/gtk/window.py b/src/briar/gtk/window.py index f44cc8b..78362d6 100644 --- a/src/briar/gtk/window.py +++ b/src/briar/gtk/window.py @@ -23,7 +23,7 @@ class Window(Gtk.ApplicationWindow): return self.__container def __setup_content(self): - self.__setup_size((800, 600)) # TODO: do properly (constants, save) + self.__setup_size((600, 400)) # TODO: do properly (constants, save) self.__setup_grid() self.__setup_startup_container() @@ -47,9 +47,9 @@ class Window(Gtk.ApplicationWindow): self.__on_startup_completed) self.__grid.add(self.__container) - def __on_startup_completed(self): - self.__grid.remove_all() - self.__setup_main_container(self) + def __on_startup_completed(self, inst, obj): + self.__grid = Gtk.Grid() + self.__setup_main_container() def __setup_main_container(self): self.__container = MainContainer() -- GitLab