diff --git a/sanic/app.py b/sanic/app.py index 7af82111..f9eda2d4 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -386,13 +386,14 @@ class Sanic: def static(self, uri, file_or_directory, pattern=r'/?.+', use_modified_since=True, use_content_range=False, stream_large_files=False, name='static', host=None, - strict_slashes=None): + strict_slashes=None, content_type=None): """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, use_modified_since, use_content_range, - stream_large_files, name, host, strict_slashes) + stream_large_files, name, host, strict_slashes, + content_type) def blueprint(self, blueprint, **options): """Register a blueprint on the application. diff --git a/sanic/static.py b/sanic/static.py index f2d02ab0..07831390 100644 --- a/sanic/static.py +++ b/sanic/static.py @@ -19,7 +19,7 @@ from sanic.response import file, file_stream, HTTPResponse def register(app, uri, file_or_directory, pattern, use_modified_since, use_content_range, stream_large_files, name='static', host=None, - strict_slashes=None): + strict_slashes=None, content_type=None): # TODO: Though sanic is not a file server, I feel like we should at least # make a good effort here. Modified-since is nice, but we could # also look into etags, expires, and caching @@ -41,6 +41,7 @@ def register(app, uri, file_or_directory, pattern, If this is an integer, this represents the threshold size to switch to file_stream() :param name: user defined name used for url_for + :param content_type: user defined content type for header """ # If we're not trying to match a file directly, # serve from the folder @@ -95,10 +96,10 @@ def register(app, uri, file_or_directory, pattern, del headers['Content-Length'] for key, value in _range.headers.items(): headers[key] = value + headers['Content-Type'] = content_type \ + or guess_type(file_path)[0] or 'text/plain' if request.method == 'HEAD': - return HTTPResponse( - headers=headers, - content_type=guess_type(file_path)[0] or 'text/plain') + return HTTPResponse(headers=headers) else: if stream_large_files: if isinstance(stream_large_files, int): diff --git a/tests/static/test.html b/tests/static/test.html new file mode 100644 index 00000000..4ba71873 --- /dev/null +++ b/tests/static/test.html @@ -0,0 +1,26 @@ + +
++ ▄▄▄▄▄ + ▀▀▀██████▄▄▄ _______________ + ▄▄▄▄▄ █████████▄ / \ + ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! | + ▀▀█████▄▄ ▀██████▄██ | _________________/ + ▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/ + ▀▀▀▄ ▀▀███ ▀ ▄▄ + ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌ + ██▀▄▄▄██▀▄███▀ ▀▀████ ▄██ +▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀ +▌ ▐▀████▐███▒▒▒▒▒▐██▌ +▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀ + ▀▀█████████▀ + ▄▄██▀██████▀█ + ▄██▀ ▀▀▀ █ + ▄█ ▐▌ + ▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄ +▌ ▐ ▀▀▄▄▄▀ + ▀▀▄▄▀ + ++ + diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 4c321646..37756085 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -1,5 +1,6 @@ import asyncio import inspect +import os import pytest from sanic import Sanic @@ -13,6 +14,14 @@ from sanic.constants import HTTP_METHODS # GET # ------------------------------------------------------------ # +def get_file_path(static_file_directory, file_name): + return os.path.join(static_file_directory, file_name) + +def get_file_content(static_file_directory, file_name): + """The content of the static file to check""" + with open(get_file_path(static_file_directory, file_name), 'rb') as file: + return file.read() + @pytest.mark.parametrize('method', HTTP_METHODS) def test_versioned_routes_get(method): app = Sanic('test_shorhand_routes_get') @@ -348,6 +357,28 @@ def test_bp_static(): assert response.status == 200 assert response.body == current_file_contents +@pytest.mark.parametrize('file_name', ['test.html']) +def test_bp_static_content_type(file_name): + # This is done here, since no other test loads a file here + current_file = inspect.getfile(inspect.currentframe()) + current_directory = os.path.dirname(os.path.abspath(current_file)) + static_directory = os.path.join(current_directory, 'static') + + app = Sanic('test_static') + blueprint = Blueprint('test_static') + blueprint.static( + '/testing.file', + get_file_path(static_directory, file_name), + content_type='text/html; charset=utf-8' + ) + + app.blueprint(blueprint) + + request, response = app.test_client.get('/testing.file') + assert response.status == 200 + assert response.body == get_file_content(static_directory, file_name) + assert response.headers['Content-Type'] == 'text/html; charset=utf-8' + def test_bp_shorthand(): app = Sanic('test_shorhand_routes') blueprint = Blueprint('test_shorhand_routes') @@ -449,41 +480,41 @@ def test_bp_shorthand(): def test_bp_group(): app = Sanic('test_nested_bp_groups') - + deep_0 = Blueprint('deep_0', url_prefix='/deep') deep_1 = Blueprint('deep_1', url_prefix = '/deep1') @deep_0.route('/') def handler(request): return text('D0_OK') - + @deep_1.route('/bottom') def handler(request): return text('D1B_OK') mid_0 = Blueprint.group(deep_0, deep_1, url_prefix='/mid') mid_1 = Blueprint('mid_tier', url_prefix='/mid1') - + @mid_1.route('/') def handler(request): return text('M1_OK') top = Blueprint.group(mid_0, mid_1) - + app.blueprint(top) - + @app.route('/') def handler(request): return text('TOP_OK') - + request, response = app.test_client.get('/') assert response.text == 'TOP_OK' - + request, response = app.test_client.get('/mid1') assert response.text == 'M1_OK' - + request, response = app.test_client.get('/mid/deep') assert response.text == 'D0_OK' - + request, response = app.test_client.get('/mid/deep1/bottom') assert response.text == 'D1B_OK' diff --git a/tests/test_static.py b/tests/test_static.py index 276001cc..3335e248 100644 --- a/tests/test_static.py +++ b/tests/test_static.py @@ -36,6 +36,21 @@ def test_static_file(static_file_directory, file_name): assert response.body == get_file_content(static_file_directory, file_name) +@pytest.mark.parametrize('file_name', ['test.html']) +def test_static_file_content_type(static_file_directory, file_name): + app = Sanic('test_static') + app.static( + '/testing.file', + get_file_path(static_file_directory, file_name), + content_type='text/html; charset=utf-8' + ) + + request, response = app.test_client.get('/testing.file') + assert response.status == 200 + assert response.body == get_file_content(static_file_directory, file_name) + assert response.headers['Content-Type'] == 'text/html; charset=utf-8' + + @pytest.mark.parametrize('file_name', ['test.file', 'decode me.txt']) @pytest.mark.parametrize('base_uri', ['/static', '', '/dir']) def test_static_directory(file_name, base_uri, static_file_directory):