Merge branch 'fix/multsite'
This commit is contained in:
commit
990688f6e0
64
dispatch.py
64
dispatch.py
@ -1,64 +0,0 @@
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
try:
|
||||
from urlparse import urlparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from werkzeug.wrappers import Request
|
||||
from werkzeug.exceptions import ImATeapot
|
||||
|
||||
from isso import make_app, wsgi
|
||||
from isso.core import Config
|
||||
|
||||
logger = logging.getLogger("isso")
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
"""
|
||||
A dispatcher to support different websites. Dispatches based on
|
||||
HTTP-Host. If HTTP-Host is not provided, display an error message.
|
||||
"""
|
||||
|
||||
def __init__(self, *confs):
|
||||
|
||||
self.isso = {}
|
||||
|
||||
for conf in map(Config.load, confs):
|
||||
|
||||
app = make_app(conf)
|
||||
|
||||
for origin in conf.getiter("general", "host"):
|
||||
self.isso[origin.rstrip("/")] = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
|
||||
if Request(environ).url.endswith((".js", ".css")):
|
||||
return self.isso.values()[0](environ, start_response)
|
||||
|
||||
if "HTTP_X_ORIGIN" in environ and "HTTP_ORIGIN" not in environ:
|
||||
environ["HTTP_ORIGIN"] = environ["HTTP_X_ORIGIN"]
|
||||
|
||||
origin = environ.get("HTTP_ORIGIN", wsgi.host(environ))
|
||||
|
||||
try:
|
||||
# logger.info("dispatch %s", origin)
|
||||
return self.isso[origin](environ, start_response)
|
||||
except KeyError:
|
||||
# logger.info("unable to dispatch %s", origin)
|
||||
resp = ImATeapot("unable to dispatch %s" % origin)
|
||||
return resp(environ, start_response)
|
||||
|
||||
|
||||
if "ISSO_SETTINGS" not in os.environ:
|
||||
logger.fatal('no such environment variable: ISSO_SETTINGS')
|
||||
else:
|
||||
confs = os.environ["ISSO_SETTINGS"].split(";")
|
||||
for path in confs:
|
||||
if not os.path.isfile(path):
|
||||
logger.fatal("%s: no such file", path)
|
||||
break
|
||||
else:
|
||||
application = Dispatcher(*confs)
|
@ -38,6 +38,7 @@ session key and hostname. Here are the default values for this section:
|
||||
|
||||
[general]
|
||||
dbpath = /tmp/isso.db
|
||||
name =
|
||||
host = http://localhost:8080/
|
||||
max-age = 15m
|
||||
session-key = ... ; python: binascii.b2a_hex(os.urandom(24))
|
||||
@ -47,6 +48,10 @@ dbpath
|
||||
file location to the SQLite3 database, highly recommended to change this
|
||||
location to a non-temporary location!
|
||||
|
||||
name
|
||||
required to dispatch :ref:`multiple websites <configure-multiple-sites>`,
|
||||
not used otherwise.
|
||||
|
||||
host
|
||||
URL to your website. When you start Isso, it will probe your website with
|
||||
a simple ``GET /`` request to see if it can reach the webserver. If this
|
||||
|
@ -42,25 +42,28 @@ websites on different domains.
|
||||
|
||||
The following example uses `gunicorn <http://gunicorn.org/>`_ as WSGI server (
|
||||
you can use uWSGI as well). Let's say you maintain two websites, like
|
||||
foo.example and other.foo:
|
||||
foo.example and other.bar:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ cat /etc/isso.d/foo.example.cfg
|
||||
[general]
|
||||
name = foo
|
||||
host = http://foo.example/
|
||||
dbpath = /var/lib/isso/foo.example.db
|
||||
|
||||
$ cat /etc/isso.d/other.foo.cfg
|
||||
$ cat /etc/isso.d/other.bar.cfg
|
||||
[general]
|
||||
host = http://other.foo/
|
||||
dbpath = /var/lib/isso/other.foo.db
|
||||
name = bar
|
||||
host = http://other.bar/
|
||||
dbpath = /var/lib/isso/other.bar.db
|
||||
|
||||
Then you run Isso using gunicorn:
|
||||
Then you run Isso with gunicorn (separate multiple configuration files by
|
||||
semicolon):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.foo.cfg"
|
||||
$ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.bar.cfg"
|
||||
$ gunicorn isso.dispatch -b localhost:8080
|
||||
|
||||
In your webserver configuration, proxy Isso as usual:
|
||||
@ -73,18 +76,18 @@ In your webserver configuration, proxy Isso as usual:
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
To verify the setup, run:
|
||||
When you now visit http://comments.example/, you will see your different Isso
|
||||
configuration separated by /`name`.
|
||||
|
||||
.. code-block:: sh
|
||||
.. code-block:: text
|
||||
|
||||
$ curl -vH "Origin: http://foo.example" http://comments.example/
|
||||
...
|
||||
$ curl -vH "Origin: http://other.foo" http://comments.example/
|
||||
...
|
||||
$ curl http://comments.example/
|
||||
/foo
|
||||
/bar
|
||||
|
||||
In case of a 418 (I'm a teapot), the setup is *not* correctly configured.
|
||||
Just embed the JavaScript including the new relative path, e.g.
|
||||
*http://comments.example/foo/js/embed.min.js*. Make sure, you don't mix the
|
||||
URLs on both sites as it will most likely cause CORS-related errors.
|
||||
|
@ -7,6 +7,9 @@
|
||||
# location to a non-temporary location
|
||||
dbpath = /tmp/comments.db
|
||||
|
||||
# required to dispatch multiple websites, not used otherwise.
|
||||
name =
|
||||
|
||||
# URL to your website. When you start Isso, it will probe your website with a
|
||||
# simple GET / request to see if it can reach the webserver. If this fails, Isso
|
||||
# may not be able check if a web page exists, thus fails to accept new comments.
|
||||
|
@ -105,6 +105,7 @@ class Config:
|
||||
|
||||
default = [
|
||||
"[general]",
|
||||
"name = ",
|
||||
"dbpath = /tmp/isso.db", "session-key = %r" % binascii.b2a_hex(os.urandom(24)),
|
||||
"host = http://localhost:8080/", "max-age = 15m",
|
||||
"notify = ",
|
||||
|
61
isso/dispatch.py
Normal file
61
isso/dispatch.py
Normal file
@ -0,0 +1,61 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
try:
|
||||
from urlparse import urlparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from werkzeug.wsgi import DispatcherMiddleware
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
from isso import make_app, wsgi
|
||||
from isso.core import Config
|
||||
|
||||
logger = logging.getLogger("isso")
|
||||
|
||||
|
||||
class Dispatcher(DispatcherMiddleware):
|
||||
"""
|
||||
A dispatcher to support different websites. Dispatches based on
|
||||
a relative URI, e.g. /foo.example and /other.bar.
|
||||
"""
|
||||
|
||||
def __init__(self, *confs):
|
||||
|
||||
self.isso = {}
|
||||
|
||||
for i, conf in enumerate(map(Config.load, confs)):
|
||||
|
||||
if not conf.get("general", "name"):
|
||||
logger.warn("unable to dispatch %r, no 'name' set", confs[i])
|
||||
continue
|
||||
|
||||
self.isso["/" + conf.get("general", "name")] = make_app(conf)
|
||||
|
||||
super(Dispatcher, self).__init__(self.default, mounts=self.isso)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
|
||||
# clear X-Script-Name as the PATH_INFO is already adjusted
|
||||
environ.pop('HTTP_X_SCRIPT_NAME', None)
|
||||
|
||||
return super(Dispatcher, self).__call__(environ, start_response)
|
||||
|
||||
def default(self, environ, start_response):
|
||||
resp = Response("\n".join(self.isso.keys()), 404, content_type="text/plain")
|
||||
return resp(environ, start_response)
|
||||
|
||||
|
||||
if "ISSO_SETTINGS" not in os.environ:
|
||||
logger.fatal('no such environment variable: ISSO_SETTINGS')
|
||||
else:
|
||||
confs = os.environ["ISSO_SETTINGS"].split(";")
|
||||
for path in confs:
|
||||
if not os.path.isfile(path):
|
||||
logger.fatal("%s: no such file", path)
|
||||
break
|
||||
else:
|
||||
application = wsgi.SubURI(Dispatcher(*confs))
|
@ -87,10 +87,6 @@ define(["q"], function(Q) {
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
if (method === "GET") {
|
||||
xhr.setRequestHeader("X-Origin", window.location.origin);
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
onload();
|
||||
|
@ -69,7 +69,7 @@ class CORSMiddleware(object):
|
||||
def add_cors_headers(status, headers, exc_info=None):
|
||||
headers = Headers(headers)
|
||||
headers.add("Access-Control-Allow-Origin", self.origin(environ))
|
||||
headers.add("Access-Control-Allow-Headers", "Origin, Content-Type, X-Origin")
|
||||
headers.add("Access-Control-Allow-Headers", "Origin, Content-Type")
|
||||
headers.add("Access-Control-Allow-Credentials", "true")
|
||||
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
|
||||
headers.add("Access-Control-Expose-Headers", "X-Set-Cookie")
|
||||
|
@ -26,7 +26,7 @@ def test_simple_CORS():
|
||||
rv = client.get("/", headers={"ORIGIN": "https://example.tld"})
|
||||
|
||||
assert rv.headers["Access-Control-Allow-Origin"] == "https://example.tld"
|
||||
assert rv.headers["Access-Control-Allow-Headers"] == "Origin, Content-Type, X-Origin"
|
||||
assert rv.headers["Access-Control-Allow-Headers"] == "Origin, Content-Type"
|
||||
assert rv.headers["Access-Control-Allow-Credentials"] == "true"
|
||||
assert rv.headers["Access-Control-Allow-Methods"] == "GET, POST, PUT, DELETE"
|
||||
assert rv.headers["Access-Control-Expose-Headers"] == "X-Set-Cookie"
|
||||
|
Loading…
Reference in New Issue
Block a user