diff --git a/sanic/app.py b/sanic/app.py index 3776c915..6644a24c 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -352,13 +352,13 @@ class Sanic: attach_to=middleware_or_request) # Static Files - def static(self, uri, file_or_directory, pattern=r'/?.+', + def static(self, uri, file_or_directory, host=None, pattern=r'/?.+', use_modified_since=True, use_content_range=False, stream_large_files=False, name='static'): """Register a root to serve files from. The input can either be a file or a directory. See """ - static_register(self, uri, file_or_directory, pattern, + static_register(self, uri, file_or_directory, host, pattern, use_modified_since, use_content_range, stream_large_files, name) diff --git a/sanic/blueprints.py b/sanic/blueprints.py index e8018326..741cbd2a 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -14,7 +14,6 @@ FutureStatic = namedtuple('Route', class Blueprint: - def __init__(self, name, url_prefix=None, host=None, version=None, @@ -92,7 +91,7 @@ class Blueprint: # Prepend the blueprint URI prefix if available uri = url_prefix + future.uri if url_prefix else future.uri app.static(uri, future.file_or_directory, - *future.args, **future.kwargs) + self.host, *future.args, **future.kwargs) # Event listeners for event, listeners in self.listeners.items(): @@ -115,6 +114,7 @@ class Blueprint: name) self.routes.append(route) return handler + return decorator def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None, @@ -165,6 +165,7 @@ class Blueprint: False, version, name) self.websocket_routes.append(route) return handler + return decorator def add_websocket_route(self, handler, uri, host=None, version=None, @@ -184,13 +185,16 @@ class Blueprint: :param event: Event to listen to. """ + def decorator(listener): self.listeners[event].append(listener) return listener + return decorator def middleware(self, *args, **kwargs): """Create a blueprint middleware from a decorated function.""" + def register_middleware(_middleware): future_middleware = FutureMiddleware(_middleware, args, kwargs) self.middlewares.append(future_middleware) @@ -206,10 +210,12 @@ class Blueprint: def exception(self, *args, **kwargs): """Create a blueprint exception from a decorated function.""" + def decorator(handler): exception = FutureException(handler, args, kwargs) self.exceptions.append(exception) return handler + return decorator def static(self, uri, file_or_directory, *args, **kwargs): diff --git a/sanic/static.py b/sanic/static.py index a9683b27..127b7f10 100644 --- a/sanic/static.py +++ b/sanic/static.py @@ -16,7 +16,7 @@ from sanic.handlers import ContentRangeHandler from sanic.response import file, file_stream, HTTPResponse -def register(app, uri, file_or_directory, pattern, +def register(app, uri, file_or_directory, host, pattern, use_modified_since, use_content_range, stream_large_files, name='static'): # TODO: Though sanic is not a file server, I feel like we should at least @@ -29,6 +29,7 @@ def register(app, uri, file_or_directory, pattern, :param app: Sanic :param file_or_directory: File or directory path to serve from :param uri: URL to serve from + :param host: vhost to serve from. 'None' for all :param pattern: regular expression used to match files in the URL :param use_modified_since: If true, send file modified time, and return not modified if the browser's matches the @@ -103,7 +104,7 @@ def register(app, uri, file_or_directory, pattern, if isinstance(stream_large_files, int): threshold = stream_large_files else: - threshold = 1024*1000 + threshold = 1024 * 1000 if not stats: stats = await stat(file_path) @@ -122,4 +123,4 @@ def register(app, uri, file_or_directory, pattern, if not name.startswith('_static_'): name = '_static_{}'.format(name) - app.route(uri, methods=['GET', 'HEAD'], name=name)(_handler) + app.route(uri, methods=['GET', 'HEAD'], name=name, host=host)(_handler) diff --git a/sanic/testing.py b/sanic/testing.py index de26d025..c3bdba65 100644 --- a/sanic/testing.py +++ b/sanic/testing.py @@ -13,7 +13,7 @@ class SanicTestClient: async def _local_request(self, method, uri, cookies=None, *args, **kwargs): import aiohttp - if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')): + if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://', '//')): url = uri else: url = 'http://{host}:{port}{uri}'.format( @@ -51,6 +51,7 @@ class SanicTestClient: def _collect_request(request): if results[0] is None: results[0] = request + self.app.request_middleware.appendleft(_collect_request) @self.app.listener('after_server_start') diff --git a/tests/test_static.py b/tests/test_static.py index 091d63a4..df55156b 100644 --- a/tests/test_static.py +++ b/tests/test_static.py @@ -3,7 +3,8 @@ import os import pytest -from sanic import Sanic + +from sanic import Sanic, exceptions @pytest.fixture(scope='module') @@ -161,3 +162,38 @@ def test_static_content_range_error(file_name, static_file_directory): assert 'Content-Range' in response.headers assert response.headers['Content-Range'] == "bytes */%s" % ( len(get_file_content(static_file_directory, file_name)),) + + +@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt', 'python.png']) +def test_static_file_vhost(static_file_directory, file_name): + app = Sanic('test_static') + + app.static( + '/testing.file', get_file_path(static_file_directory, file_name), host='vhost') + + # no vhost + request, response = app.test_client.get('/testing.file') + assert response.status == 404 + + # vhost + request, response = app.test_client.get('/testing.file', headers={"host": "vhost"}) + assert response.status == 200 + assert response.body == get_file_content(static_file_directory, file_name) + +@pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt', 'python.png']) +def test_bp_static_file_vhost(static_file_directory, file_name): + from sanic import Blueprint + + app = Sanic('test_static') + bp = Blueprint(name="bp", url_prefix="", host="vhost") + bp.static('/testing.file', get_file_path(static_file_directory, file_name)) + app.blueprint(bp) + + + # no vhost + request, response = app.test_client.get('/testing.file') + assert response.status == 404 + + request, response = app.test_client.get('/testing.file', headers={"host": "vhost"}) + assert response.status == 200 + assert response.body == get_file_content(static_file_directory, file_name) \ No newline at end of file