From 9c72b557ec5e39ddc8dab426b7b02015e8f59f42 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Mon, 20 Feb 2017 16:36:48 -0800 Subject: [PATCH] allow default vhost --- docs/sanic/routing.md | 18 +++++++++++++----- sanic/router.py | 15 +++++++++------ tests/test_vhosts.py | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/docs/sanic/routing.md b/docs/sanic/routing.md index d6bedc6a..452f57ab 100644 --- a/docs/sanic/routing.md +++ b/docs/sanic/routing.md @@ -81,6 +81,19 @@ async def get_handler(request): ``` +There is also an optional `host` argument (which can be a list or a string). This restricts a route to the host or hosts provided. If there is a also a route with no host, it will be the default. + +```python +@app.route('/get', methods=['GET'], host='example.com') +async def get_handler(request): + return text('GET request - {}'.format(request.args)) + +# if the host header doesn't match example.com, this route will be used +@app.route('/get', methods=['GET']) +async def get_handler(request): + return text('GET request in default - {}'.format(request.args)) +``` + There are also shorthand method decorators: ```python @@ -168,8 +181,3 @@ url = app.url_for('post_handler', post_id=5, arg_one=['one', 'two'], arg_two=2, # http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor ``` - All valid parameters must be passed to `url_for` to build a URL. If a parameter is not supplied, or if a parameter does not match the specified type, a `URLBuildError` will be thrown. - - - - - diff --git a/sanic/router.py b/sanic/router.py index 4de95cb2..32b3a3d1 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -119,6 +119,9 @@ class Router: for host_ in host: self.add(uri, methods, handler, host_) return + else: + # default host + self.hosts.add('*') # Dict for faster lookups of if method allowed if methods: @@ -258,16 +261,16 @@ class Router: :param request: Request object :return: handler, arguments, keyword arguments """ - # Note - this means that if _any_ routes specify host, non-host routes - # will typically fail as they are looked up by host - # fix - check if host is in host-based routing table (perhaps) or - # better, get candidate of routes and then dispatch by host. - # This may have perf issues. + # No virtual hosts specified; default behavior if not self.hosts: return self._get(request.url, request.method, '') - else: + # virtual hosts specified; try to match route to the host header + try: return self._get(request.url, request.method, request.headers.get("Host", '')) + # try default hosts + except NotFound: + return self._get(request.url, request.method, '') @lru_cache(maxsize=ROUTER_CACHE_SIZE) def _get(self, url, method, host): diff --git a/tests/test_vhosts.py b/tests/test_vhosts.py index 53fdf53f..2b88bff3 100644 --- a/tests/test_vhosts.py +++ b/tests/test_vhosts.py @@ -36,3 +36,21 @@ def test_vhosts_with_list(): headers = {"Host": "world.com"} request, response = app.test_client.get('/', headers=headers) assert response.text == "Hello, world!" + +def test_vhosts_with_defaults(): + app = Sanic('test_vhosts') + + @app.route('/', host="hello.com") + async def handler(request): + return text("Hello, world!") + + @app.route('/') + async def handler(request): + return text("default") + + headers = {"Host": "hello.com"} + request, response = app.test_client.get('/', headers=headers) + assert response.text == "Hello, world!" + + request, response = app.test_client.get('/') + assert response.text == "default"