Last active
December 22, 2016 20:31
-
-
Save antoine-lizee/05e24078df1e3a0485bb8dafcfd0741a to your computer and use it in GitHub Desktop.
Implement exception-based catchall host routing with flask & tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from werkzeug.routing import Rule as BaseRule | |
class Rule(BaseRule): | |
except_hosts = [] | |
def match(self, path): | |
""" Monkey patched version of the default Werkzeug behavior to achieve host matching with catchall. | |
By default, if host matching is enabled, the match is only done if the host is always provided, which is | |
unpractical. This modified version matches normally if host matching is not enabled. If host matching is | |
enabled, if it fails, and if the host is not part of a blacklist, it will try matching again without host | |
matching ('catch all'). | |
""" | |
host, route = path.split('|') | |
result = super().match(path) | |
if result is None and host and host not in self.except_hosts: | |
result = super().match('|' + route) | |
return result | |
def configure_app_host_routing(app, except_hosts): | |
app.url_rule_class = type('LocalRule', (Rule, ), {'except_hosts': except_hosts}) | |
app.url_map.host_matching = True | |
### EXAMPLE | |
from flask import Flask | |
app = Flask('test_app') | |
configure_app_host_routing(app, ['specificdomain.com']) | |
# add host-specific endpoint | |
@app.route('/<argument>', host='specificdomain.com') | |
def specific_host_endpoint(): | |
return | |
# add normal endpoint, could be a blueprint too. | |
@app.route('/test/<id>') | |
def test(): | |
return | |
### TEST | |
import unittest | |
from werkzeug.exceptions import NotFound | |
class TestHostRouting(unittest.TestCase): | |
def main_test(self): | |
m = app.url_map | |
# host specific endpoint is routed correctly for specificdomain.com | |
self.assertEqual( | |
('specific_host_endpoint', {'argument': 'yo'}), | |
m.bind('specificdomain.com').match('/yo'), | |
) | |
with self.assertRaises(NotFound): | |
m.bind('specificdomain.com').match('/yo/') | |
# host specific endpoint does not work for other domains | |
with self.assertRaises(NotFound): | |
m.bind('domain.com').match('/yo') | |
with self.assertRaises(NotFound): | |
m.bind('anything.specificdomain.com').match('/yo') | |
# normal endpoint works as expected | |
self.assertEqual(('test', {'id': '1'}), m.bind('domain.com').match('/test/1')) | |
self.assertEqual(('test', {'id': '1'}), m.bind('anything.domain.com').match('/test/1')) | |
# normal endpoint doesn't work for specific host | |
with self.assertRaises(NotFound): | |
m.bind('specificdomain.com').match('/test/1') | |
TestHostRouting('main_test').main_test() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment