Skip to content
This repository has been archived by the owner on Sep 5, 2019. It is now read-only.

Commit

Permalink
Adds the ability to define an alias for dynamic applications
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Krienbühl committed Feb 20, 2015
1 parent a6392dc commit 763cb65
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
46 changes: 46 additions & 0 deletions onegov/server/application.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import re

from onegov.server import errors


class Application(object):
""" WSGI applications inheriting from this class can be served by
Expand All @@ -16,6 +18,9 @@ class Application(object):
#: expressions, but straight hostnames.
allowed_hosts = None

#: Use :meth:`alias` instead of manipulating this dictionary.
_aliases = None

def __call__(self, environ, start_respnose):
raise NotImplementedError

Expand All @@ -39,6 +44,8 @@ def configure_application(self, **configuration):
self.allowed_hosts = set(
configuration.get('allowed_hosts', []))

self._aliases = {}

def set_application_id(self, application_id):
""" Sets the application id before __call__ is called. That is, before
each request.
Expand Down Expand Up @@ -100,3 +107,42 @@ def is_allowed_hostname(self, hostname):
return True

return hostname in self.allowed_hosts

def alias(self, application_id, alias):
""" Adds an alias under which this application is available on the
server. The alias is *for* your current `application_id`.
The alias only works for wildcard applications - it has no effect
on static applications!
This is how it works:
An application running under `/sites/main` can be made available
under `/sites/blog` by running `self.alias('main', 'blog')`.
Aliases must be unique, so this method will fail with a
:class:`onegov.server.errors.ApplicationConflictError` if an alias
is already in use by another application running under the same path.
Note that an application opened through an alias will still have
the application_id set to the actual root, not the alias. So
`/sites/blog` in the example above would still lead to an
application_id of `main` instead of `blog`.
This is mainly meant to be used for dynamic DNS setups. Say you have
an application available through test.onegov.dev (pointing to
`/sites/test`). You now want to make this site available through
example.org. You can do this as follows:
self.allowed_hosts.add('example.org')
self.alias('example_org')
If your server has a catch-all rule that sends unknown domains to
`/sites/domain_without_dots`, then you can add domains dynamically
and the customer just points the DNS to your ip address.
"""
if alias in self._aliases:
raise errors.ApplicationConflictError(
"the alias '{}' is already in use".format(alias))

self._aliases[alias] = application_id
6 changes: 6 additions & 0 deletions onegov/server/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Request(BaseRequest):

@property
def hostnames(self):
""" Iterates through the hostnames of the request. """
for key in self.hostname_keys:
if key in self.environ:
hostname = urlparse(self.environ[key]).hostname
Expand Down Expand Up @@ -107,6 +108,11 @@ def __call__(self, environ, start_response):
if application_root in self.wildcard_applications:
base_path = '/'.join(path_fragments[:3])
application_id = ''.join(path_fragments[2:3])

# dealias the application id
if application_id in application._aliases:
application_id = application._aliases[application_id]

else:
base_path = application_root
application_id = ''.join(path_fragments[1:2])
Expand Down
35 changes: 35 additions & 0 deletions onegov/server/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,38 @@ def __call__(self, environ, start_response):

response = c.get('/static', headers={'HOST': 'example.org:8080'})
assert response.body == b'hello'


def test_aliases():

class EchoApplication(Application):

def __call__(self, environ, start_response):
response = Response()
response.text = u''.join((self.application_id))

return response(environ, start_response)

server = Server(Config({
'applications': [
{
'path': '/sites/*',
'application': EchoApplication,
}
]
}))

application = server.applications.get('/sites')

c = Client(server)

response = c.get('/sites/main')
assert response.body == b'main'

response = c.get('/sites/blog')
assert response.body == b'blog'

application.alias('main', 'blog')

response = c.get('/sites/blog')
assert response.body == b'main'

0 comments on commit 763cb65

Please sign in to comment.