From e77b99c56a3dd4fa386ea49e2ccb4c3e881a45e8 Mon Sep 17 00:00:00 2001 From: kyle Date: Wed, 25 Apr 2018 16:46:21 -0400 Subject: [PATCH] Add support for custom context/application root This adds a new parameter, ARA_APPLICATION_ROOT, which does two things: 1) Maps to Flask's APPLICATION_ROOT [1] 2) Adds a route prefix by supplying the APPLICATION_ROOT value to SCRIPT_NAME through a custom WSGI middleware implementation. [1]: http://flask.pocoo.org/docs/0.12/config/ Story: 2001123 Co-Authored-By: kyle Co-Authored-By: David Moreau-Simard Change-Id: Icb58a3d64716263718b9cb5a2d4fe46a6d522502 --- ara/config/base.py | 5 ++++ ara/tests/unit/test_config.py | 1 + ara/webapp.py | 43 +++++++++++++++++++++++++++++++++++ doc/source/configuration.rst | 14 ++++++++++++ 4 files changed, 63 insertions(+) diff --git a/ara/config/base.py b/ara/config/base.py index 9051a578..427f074e 100644 --- a/ara/config/base.py +++ b/ara/config/base.py @@ -39,6 +39,11 @@ class BaseConfig(object): True, value_type='boolean' ) + self.APPLICATION_ROOT = ara_config( + 'application_root', + 'ARA_APPLICATION_ROOT', + '/' + ) self.SQLALCHEMY_DATABASE_URI = self.ARA_DATABASE self.SQLALCHEMY_TRACK_MODIFICATIONS = False self.SQLALCHEMY_ECHO = ara_config( diff --git a/ara/tests/unit/test_config.py b/ara/tests/unit/test_config.py index 0232c8ce..5f03d326 100644 --- a/ara/tests/unit/test_config.py +++ b/ara/tests/unit/test_config.py @@ -41,6 +41,7 @@ class TestConfig(TestAra): 'FREEZER_IGNORE_404_NOT_FOUND': True, 'ARA_DIR': os.path.expanduser('~/.ara'), 'SQLALCHEMY_DATABASE_URI': db, + 'APPLICATION_ROOT': '/', 'ARA_HOST': '127.0.0.1', 'ARA_AUTOCREATE_DATABASE': True, 'ARA_PORT': "9191", diff --git a/ara/webapp.py b/ara/webapp.py index 4ee5e2e5..f804b033 100644 --- a/ara/webapp.py +++ b/ara/webapp.py @@ -62,6 +62,7 @@ def create_app(): configure_app(app) configure_dirs(app) configure_logging(app) + configure_app_root(app) configure_blueprints(app) configure_static_route(app) configure_db(app) @@ -88,6 +89,14 @@ def configure_logging(app): setup_logging(app.config) +def configure_app_root(app): + log = logging.getLogger('ara.webapp.configure_app_root') + # Don't load the middleware needlessly if the root is actually '/' + if app.config['APPLICATION_ROOT'] != '/': + app.wsgi_app = AppRootMiddleware(app, app.wsgi_app) + log.debug('Application root loaded: %s' % app.config['APPLICATION_ROOT']) + + def configure_errorhandlers(app): @app.errorhandler(404) def page_not_found(error): @@ -309,3 +318,37 @@ def configure_cache(app): if not getattr(app, '_cache', None): app._cache = {} + + +class AppRootMiddleware(object): + """ + Middleware to manage route prefixes, for example when hosting ARA in a + subdirectory. + """ + def __init__(self, app, wsgi_app): + self.log = logging.getLogger('ara.webapp.AppRootMiddleware') + self.log.debug('Initializing AppRootMiddleware') + self.app = app + self.wsgi_app = wsgi_app + + def __call__(self, environ, start_response): + root = self.app.config['APPLICATION_ROOT'] + if environ['PATH_INFO'].startswith(root): + environ['PATH_INFO'] = environ['PATH_INFO'][len(root):] + environ['SCRIPT_NAME'] = root + self.log.debug('Returning with root %s' % root) + return self.wsgi_app(environ, start_response) + else: + self.log.debug('Returning 404 for %s' % environ['PATH_INFO']) + url = "{scheme}://{host}{root}".format( + scheme=environ['wsgi.url_scheme'], + host=environ['HTTP_HOST'], + root=root, + ) + msg = """ + This URL doesn't belong to an ARA application.
+
+ Did you mean to browse to {url} instead ? + """.format(url=url) + start_response('404', [('Content-Type', 'text/html')]) + return [msg.strip().encode()] diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index 39c181e1..2056dee7 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -117,6 +117,8 @@ Parameters and their defaults +-------------------------------+----------------------------+-------------------------------------------+ | ARA_PORT_ | port | 9191 | +-------------------------------+----------------------------+-------------------------------------------+ +| ARA_APPLICATION_ROOT_ | application_root | / | ++-------------------------------+----------------------------+-------------------------------------------+ | ARA_LOG_CONFIG_ | logconfig | None | +-------------------------------+----------------------------+-------------------------------------------+ | ARA_LOG_FILE_ | logfile | ~/.ara/ara.log | @@ -237,6 +239,18 @@ the ``ara-manage runserver`` command. It is equivalent to the ``-p`` or ``--port`` argument of the ``ara-manage runserver`` command. +ARA_APPLICATION_ROOT +~~~~~~~~~~~~~~~~~~~~ + +The path at which the web application should be loaded. + +The default behavior is to load the application at the root (``/``) of your +host. +Change this parameter if you'd like to host your application elsewhere. + +For example, ``/ara`` would make the application available under +``http://host/ara`` instead of ``http://host/``. + ARA_LOG_CONFIG ~~~~~~~~~~~~~~