From b672dae62417b7d87b110e75274eeae50e433cdd Mon Sep 17 00:00:00 2001 From: Martin Zimmermann Date: Tue, 29 Oct 2013 12:22:13 +0100 Subject: [PATCH] allow multiple hosts, e.g. HTTP and HTTPS sites --- docs/CONFIGURATION.rst | 11 +++++++++++ isso/__init__.py | 42 ++++++++++++++++++++++++++++-------------- isso/core.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/docs/CONFIGURATION.rst b/docs/CONFIGURATION.rst index 76b462e..e531220 100644 --- a/docs/CONFIGURATION.rst +++ b/docs/CONFIGURATION.rst @@ -47,6 +47,17 @@ host fails, Isso may not be able check if a web page exists, thus fails to accept new comments. + You can supply more than one host: + + .. code-block:: ini + + [general] + host = + http://localhost/ + https://localhost/ + + This is useful, when your website is available on HTTP and HTTPS. + session-key private session key to validate client cookies. If you restart the application several times per hour for whatever reason, use a fixed diff --git a/isso/__init__.py b/isso/__init__.py index e8253ce..c71a761 100644 --- a/isso/__init__.py +++ b/isso/__init__.py @@ -40,10 +40,8 @@ from argparse import ArgumentParser try: import httplib - import urlparse except ImportError: import http.client as httplib - import urllib.parse as urlparse import misaka from itsdangerous import URLSafeTimedSerializer @@ -128,12 +126,24 @@ class Isso(object): return e def wsgi_app(self, environ, start_response): + response = self.dispatch(Request(environ), start_response) - if hasattr(response, 'headers'): - response.headers["Access-Control-Allow-Origin"] = self.conf.get('general', 'host').rstrip('/') - response.headers["Access-Control-Allow-Headers"] = "Origin, Content-Type" - response.headers["Access-Control-Allow-Credentials"] = "true" - response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE" + + # add CORS header + if hasattr(response, 'headers') and 'HTTP_ORIGN' in environ: + for host in self.conf.getiter('general', 'host'): + if environ["HTTP_ORIGIN"] == host.rstrip("/"): + origin = host.rstrip("/") + break + else: + origin = host.rstrip("/") + + hdrs = response.headers + hdrs["Access-Control-Allow-Origin"] = origin + hdrs["Access-Control-Allow-Headers"] = "Origin, Content-Type" + hdrs["Access-Control-Allow-Credentials"] = "true" + hdrs["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE" + return response(environ, start_response) def __call__(self, environ, start_response): @@ -153,12 +163,17 @@ def make_app(conf=None): isso = App(conf) - try: - host, port, ssl = parse.host(conf.get("general", "host")) - con = httplib.HTTPSConnection if ssl else httplib.HTTPConnection - con(host, port, timeout=5).request('GET', '/') - logger.info("connected to HTTP server") - except (httplib.HTTPException, socket.error): + for line in conf.getiter("general", "host"): + try: + host, port, ssl = parse.host(line) + con = httplib.HTTPSConnection if ssl else httplib.HTTPConnection + con(host, port, timeout=5).request('GET', '/') + except (httplib.HTTPException, socket.error): + continue + else: + logger.info("connected to HTTP server") + break + else: logger.warn("unable to connect to HTTP server") app = ProxyFix(wsgi.SubURI(SharedDataMiddleware(isso.wsgi_app, { @@ -195,7 +210,6 @@ def main(): run_simple(conf.get('server', 'host'), conf.getint('server', 'port'), make_app(conf), threaded=True, use_reloader=conf.getboolean('server', 'reload')) - try: import uwsgi except ImportError: diff --git a/isso/core.py b/isso/core.py index e1636e1..3e5a8f4 100644 --- a/isso/core.py +++ b/isso/core.py @@ -28,11 +28,36 @@ else: from isso import notify from isso.utils import parse +from isso.compat import text_type as str logger = logging.getLogger("isso") class IssoParser(ConfigParser): + """ + Extended :class:`ConfigParser` to parse human-readable timedeltas + into seconds and handles multiple values per key. + + >>> import io + >>> parser = IssoParser(allow_no_value=True) + >>> parser.read_file(io.StringIO(u''' + ... [foo] + ... bar = 1h + ... baz = 12 + ... bla = + ... spam + ... ham + ... asd = fgh + ... ''')) + >>> parser.getint("foo", "bar") + 3600 + >>> parser.getint("foo", "baz") + 12 + >>> list(parser.getiter("foo", "bla")) # doctest: +IGNORE_UNICODE + ['spam', 'ham'] + >>> list(parser.getiter("foo", "asd")) # doctest: +IGNORE_UNICODE + ['fgh'] + """ @classmethod def _total_seconds(cls, td): @@ -49,6 +74,11 @@ class IssoParser(ConfigParser): except AttributeError: return int(IssoParser._total_seconds(delta)) + def getiter(self, section, key): + for item in map(str.strip, self.get(section, key).split('\n')): + if item: + yield item + class Config: