Reverse proxy wrapper now supports configuration of the http headers to utilize for determining prefix and scheme

Introduced two new configuration settings, server.reverseProxy.prefixHeader and server.reverseProxy.schemeHeader to define the headers to evaluate for prefix and scheme to use respectively, also moved server.baseUrl and server.scheme to server.reverseProxy.fallbackPrefix and server.reverseProxy.fallbackScheme.

Also fixed SockJS URI as generated in index.jinja to include BASE_URL.

 Should do what PR #507 intended, but with less code duplication.
This commit is contained in:
Gina Häußge 2014-08-07 13:34:58 +02:00
parent 02212bde21
commit 17d3d9deb7
4 changed files with 76 additions and 20 deletions

View file

@ -153,7 +153,13 @@ class Server():
except AttributeError, e:
logger.exception("Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName)
app.wsgi_app = util.ReverseProxied(app.wsgi_app)
app.wsgi_app = util.ReverseProxied(
app.wsgi_app,
settings().get(["server", "reverseProxy", "prefixHeader"]),
settings().get(["server", "reverseProxy", "schemeHeader"]),
settings().get(["server", "reverseProxy", "prefixFallback"]),
settings().get(["server", "reverseProxy", "prefixScheme"])
)
app.secret_key = "k3PuVYgtxNm8DXKKTw2nWmFQQun9qceV"
loginManager = LoginManager()

View file

@ -72,29 +72,43 @@ class ReverseProxied(object):
:param app: the WSGI application
:param header_script_name: the HTTP header in the wsgi environment from which to determine the prefix
:param header_scheme: the HTTP header in the wsgi environment from which to determine the scheme
:param base_url: the prefix to use as fallback if headers are not set
"""
def __init__(self, app, header_script_name="HTTP_X_SCRIPT_NAME", header_scheme="HTTP_X_SCHEME"):
def __init__(self, app, header_prefix="x-script-name", header_scheme="x-scheme", base_url="", scheme=""):
self.app = app
self._header_script_name = header_script_name
self._header_scheme = header_scheme
# headers for prefix & scheme, converted to conform to WSGI format
to_wsgi_format = lambda header: "HTTP_" + header.upper().replace("-", "_")
self._header_prefix = to_wsgi_format(header_prefix)
self._header_scheme = to_wsgi_format(header_scheme)
# fallback prefix & scheme from config
self._fallback_prefix = base_url
self._fallback_scheme = scheme
def __call__(self, environ, start_response):
script_name = environ.get(self._header_script_name, '')
if not script_name:
script_name = settings().get(["server", "baseUrl"])
# determine prefix
prefix = environ.get(self._header_prefix, "")
if not prefix:
prefix = self._fallback_prefix
if script_name:
environ['SCRIPT_NAME'] = script_name
path_info = environ['PATH_INFO']
if path_info.startswith(script_name):
environ['PATH_INFO'] = path_info[len(script_name):]
# rewrite SCRIPT_NAME and if necessary also PATH_INFO based on prefix
if prefix:
environ["SCRIPT_NAME"] = prefix
path_info = environ["PATH_INFO"]
if path_info.startswith(prefix):
environ["PATH_INFO"] = path_info[len(prefix):]
scheme = environ.get(self._header_scheme, '')
# determine scheme
scheme = environ.get(self._header_scheme, "")
if not scheme:
scheme = settings().get(["server", "scheme"])
scheme = self._fallback_scheme
# rewrite wsgi.url_scheme based on scheme
if scheme:
environ['wsgi.url_scheme'] = scheme
environ["wsgi.url_scheme"] = scheme
# call wrapped app with rewritten environment
return self.app(environ, start_response)

View file

@ -41,8 +41,12 @@ default_settings = {
"host": "0.0.0.0",
"port": 5000,
"firstRun": True,
"baseUrl": "",
"scheme": "",
"reverseProxy": {
"prefixHeader": "X-Script-Name",
"schemeHeader": "X-Scheme",
"prefixFallback": "",
"schemeFallback": ""
},
"uploads": {
"maxSize": 1 * 1024 * 1024 * 1024, # 1GB
"nameSuffix": ".name",
@ -198,7 +202,7 @@ class Settings(object):
if os.path.exists(self._configfile) and os.path.isfile(self._configfile):
with open(self._configfile, "r") as f:
self._config = yaml.safe_load(f)
# chamged from else to handle cases where the file exists, but is empty / 0 bytes
# changed from else to handle cases where the file exists, but is empty / 0 bytes
if not self._config:
self._config = {}
@ -209,6 +213,36 @@ class Settings(object):
if not self._config:
return
dirty = False
for migrate in (self._migrate_event_config, self._migrate_reverse_proxy_config):
dirty = migrate() or dirty
if dirty:
self.save(force=True)
def _migrate_reverse_proxy_config(self):
if "server" in self._config.keys() and ("baseUrl" in self._config["server"] or "scheme" in self._config["server"]):
prefix = ""
if "baseUrl" in self._config["server"]:
prefix = self._config["server"]["baseUrl"]
del self._config["server"]["baseUrl"]
scheme = ""
if "scheme" in self._config["server"]:
scheme = self._config["server"]["scheme"]
del self._config["server"]["scheme"]
if not "reverseProxy" in self._config["server"] or not isinstance(self._config["server"]["reverseProxy"], dict):
self._config["server"]["reverseProxy"] = dict()
if prefix:
self._config["server"]["reverseProxy"]["prefixFallback"] = prefix
if scheme:
self._config["server"]["reverseProxy"]["schemeFallback"] = scheme
self._logger.info("Migrated reverse proxy configuration to new structure")
return True
else:
return False
def _migrate_event_config(self):
if "events" in self._config.keys() and ("gcodeCommandTrigger" in self._config["events"] or "systemCommandTrigger" in self._config["events"]):
self._logger.info("Migrating config (event subscriptions)...")
@ -290,8 +324,10 @@ class Settings(object):
newEvents["subscriptions"].append(newTrigger)
self._config["events"] = newEvents
self.save(force=True)
self._logger.info("Migrated %d event subscriptions to new format and structure" % len(newEvents["subscriptions"]))
return True
else:
return False
def save(self, force=False):
if not self._dirty and not force:

View file

@ -37,7 +37,7 @@
var CONFIG_GCODE_SIZE_THRESHOLD = {{ gcodeThreshold }};
var CONFIG_GCODE_MOBILE_SIZE_THRESHOLD = {{ gcodeMobileThreshold }};
var SOCKJS_URI = window.location.protocol.slice(0, -1) + "://" + (window.document ? window.document.domain : window.location.hostname) + ":" + window.location.port + "/sockjs";
var SOCKJS_URI = window.location.protocol.slice(0, -1) + "://" + (window.document ? window.document.domain : window.location.hostname) + ":" + window.location.port + BASEURL + "sockjs";
var SOCKJS_DEBUG = {% if debug -%} true; {% else %} false; {%- endif %}
var UI_API_KEY = "{{ uiApiKey }}";