|
|
|
@ -7,6 +7,7 @@ import urllib.request |
|
|
|
|
import time |
|
|
|
|
import glob |
|
|
|
|
|
|
|
|
|
import flask_socketio |
|
|
|
|
import click |
|
|
|
|
|
|
|
|
|
from asl_rulebook2.webapp import app, globvars |
|
|
|
@ -22,16 +23,16 @@ def main( bind_addr, data_dir, force_init_delay, flask_debug ): |
|
|
|
|
"""Run the webapp server.""" |
|
|
|
|
|
|
|
|
|
# initialize |
|
|
|
|
port = None |
|
|
|
|
flask_port = None |
|
|
|
|
if bind_addr: |
|
|
|
|
words = bind_addr.split( ":" ) |
|
|
|
|
host = words[0] |
|
|
|
|
flask_host = words[0] |
|
|
|
|
if len(words) > 1: |
|
|
|
|
port = words[1] |
|
|
|
|
flask_port = words[1] |
|
|
|
|
else: |
|
|
|
|
host = app.config.get( "FLASK_HOST", "localhost" ) |
|
|
|
|
if not port: |
|
|
|
|
port = app.config.get( "FLASK_PORT_NO" ) |
|
|
|
|
flask_host = app.config.get( "FLASK_HOST", "localhost" ) |
|
|
|
|
if not flask_port: |
|
|
|
|
flask_port = app.config.get( "FLASK_PORT_NO" ) |
|
|
|
|
if not flask_debug: |
|
|
|
|
flask_debug = app.config.get( "FLASK_DEBUG", False ) |
|
|
|
|
|
|
|
|
@ -42,9 +43,9 @@ def main( bind_addr, data_dir, force_init_delay, flask_debug ): |
|
|
|
|
app.config["DATA_DIR"] = data_dir |
|
|
|
|
|
|
|
|
|
# validate the configuration |
|
|
|
|
if not host: |
|
|
|
|
if not flask_host: |
|
|
|
|
raise RuntimeError( "The server host was not set." ) |
|
|
|
|
if not port: |
|
|
|
|
if not flask_port: |
|
|
|
|
raise RuntimeError( "The server port was not set." ) |
|
|
|
|
|
|
|
|
|
# monitor extra files for changes |
|
|
|
@ -75,36 +76,47 @@ def main( bind_addr, data_dir, force_init_delay, flask_debug ): |
|
|
|
|
if force_init_delay > 0: |
|
|
|
|
def _start_server(): |
|
|
|
|
time.sleep( force_init_delay ) |
|
|
|
|
url = "http://{}:{}".format( host, port ) |
|
|
|
|
url = "http://{}:{}".format( flask_host, flask_port ) |
|
|
|
|
_ = urllib.request.urlopen( url ) |
|
|
|
|
threading.Thread( target=_start_server, daemon=True ).start() |
|
|
|
|
|
|
|
|
|
# check if the user needs to prepare their data files |
|
|
|
|
if not app.config.get( "DATA_DIR" ): |
|
|
|
|
# yup - initialize the socketio server |
|
|
|
|
init_prepare_socketio( app ) |
|
|
|
|
|
|
|
|
|
# run the server |
|
|
|
|
app.run( host=host, port=port, debug=flask_debug, |
|
|
|
|
extra_files = extra_files |
|
|
|
|
) |
|
|
|
|
run_server( flask_host, flask_port, flask_debug, extra_files ) |
|
|
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
|
|
|
|
|
|
def init_prepare_socketio( flask_app ): |
|
|
|
|
"""Initialize the socketio server needed to prepare the data files.""" |
|
|
|
|
# NOTE: We only set this up if it's needed (i.e. because there is no data directory, |
|
|
|
|
# and the user needs to prepare their data files), rather than always having it running |
|
|
|
|
# on the off-chance that the user might need it :-/ |
|
|
|
|
# NOTE: socketio doesn't really work well with threads, and it's tricky to get it to |
|
|
|
|
# send events to the client if we're using e.g. eventlet: |
|
|
|
|
# https://stackoverflow.com/questions/43801884/how-to-run-python-socketio-in-thread |
|
|
|
|
# https://python-socketio.readthedocs.io/en/latest/server.html#standard-threads |
|
|
|
|
# Using native threads is less-performant, but it's not an issue for us, and it works :-/ |
|
|
|
|
import socketio |
|
|
|
|
sio = socketio.Server( async_mode="threading" ) |
|
|
|
|
flask_app.wsgi_app = socketio.WSGIApp( sio, flask_app.wsgi_app ) |
|
|
|
|
def run_server( host, port, debug, extra_files=None ): |
|
|
|
|
"""Run the webapp server.""" |
|
|
|
|
|
|
|
|
|
# NOTE: flask-socketio + eventlet handles concurrency differently to the Flask development server, |
|
|
|
|
# and we need to remain responsive, otherwise pings from the socketio client will timeout, and it will |
|
|
|
|
# disconnect (and show a big warning in the UI that the server has gone away). To avoid this, |
|
|
|
|
# we relinquish the CPU regularly, but just in case, we increase the ping timeout (and allow the user |
|
|
|
|
# to increase it even further, if necessary). This should only be an issue when preparing the data files, |
|
|
|
|
# since the main program doesn't use socketio. |
|
|
|
|
# NOTE: Setting the timeout high shouldn't be a problem, since if the server really does go away, |
|
|
|
|
# the connection will be dropped, and the front-end Javascript will detect that immediately. |
|
|
|
|
ping_timeout = app.config.get( "SOCKETIO_PING_TIMEOUT", 30 ) |
|
|
|
|
|
|
|
|
|
# run the server |
|
|
|
|
sio = flask_socketio.SocketIO( app, |
|
|
|
|
async_mode = "eventlet", |
|
|
|
|
ping_timeout = ping_timeout |
|
|
|
|
) |
|
|
|
|
globvars.socketio_server = sio |
|
|
|
|
args = { |
|
|
|
|
"debug": debug, |
|
|
|
|
"log_output": False |
|
|
|
|
} |
|
|
|
|
if extra_files: |
|
|
|
|
args.update( { |
|
|
|
|
"use_reloader": True, |
|
|
|
|
"reloader_options": { "extra_files": extra_files }, |
|
|
|
|
} ) |
|
|
|
|
sio.run( app, |
|
|
|
|
host=host, port=port, |
|
|
|
|
**args |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
# --------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|