Add content_type flag to Sanic.static (#1267)
* Add content_type flag to Sanic.static Fixes #1266 * Fix flake8 error in travis Add line to document `content_type` arg * Fix content_type for file streams Update tests herp derp * Remove content_type as an arg to HTTPResponse `response.HTTPResponse` will default to `headers['Content-Type']` instead of `content_type` https://github.com/channelcat/sanic/pull/1267#discussion_r204190913
This commit is contained in:
parent
377c9890a3
commit
b238be54a4
|
@ -386,13 +386,14 @@ class Sanic:
|
||||||
def static(self, uri, file_or_directory, pattern=r'/?.+',
|
def static(self, uri, file_or_directory, pattern=r'/?.+',
|
||||||
use_modified_since=True, use_content_range=False,
|
use_modified_since=True, use_content_range=False,
|
||||||
stream_large_files=False, name='static', host=None,
|
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
|
"""Register a root to serve files from. The input can either be a
|
||||||
file or a directory. See
|
file or a directory. See
|
||||||
"""
|
"""
|
||||||
static_register(self, uri, file_or_directory, pattern,
|
static_register(self, uri, file_or_directory, pattern,
|
||||||
use_modified_since, use_content_range,
|
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):
|
def blueprint(self, blueprint, **options):
|
||||||
"""Register a blueprint on the application.
|
"""Register a blueprint on the application.
|
||||||
|
|
|
@ -19,7 +19,7 @@ from sanic.response import file, file_stream, HTTPResponse
|
||||||
def register(app, uri, file_or_directory, pattern,
|
def register(app, uri, file_or_directory, pattern,
|
||||||
use_modified_since, use_content_range,
|
use_modified_since, use_content_range,
|
||||||
stream_large_files, name='static', host=None,
|
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
|
# 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
|
# make a good effort here. Modified-since is nice, but we could
|
||||||
# also look into etags, expires, and caching
|
# 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
|
If this is an integer, this represents the
|
||||||
threshold size to switch to file_stream()
|
threshold size to switch to file_stream()
|
||||||
:param name: user defined name used for url_for
|
: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,
|
# If we're not trying to match a file directly,
|
||||||
# serve from the folder
|
# serve from the folder
|
||||||
|
@ -95,10 +96,10 @@ def register(app, uri, file_or_directory, pattern,
|
||||||
del headers['Content-Length']
|
del headers['Content-Length']
|
||||||
for key, value in _range.headers.items():
|
for key, value in _range.headers.items():
|
||||||
headers[key] = value
|
headers[key] = value
|
||||||
|
headers['Content-Type'] = content_type \
|
||||||
|
or guess_type(file_path)[0] or 'text/plain'
|
||||||
if request.method == 'HEAD':
|
if request.method == 'HEAD':
|
||||||
return HTTPResponse(
|
return HTTPResponse(headers=headers)
|
||||||
headers=headers,
|
|
||||||
content_type=guess_type(file_path)[0] or 'text/plain')
|
|
||||||
else:
|
else:
|
||||||
if stream_large_files:
|
if stream_large_files:
|
||||||
if isinstance(stream_large_files, int):
|
if isinstance(stream_large_files, int):
|
||||||
|
|
26
tests/static/test.html
Normal file
26
tests/static/test.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
▄▄▄▄▄
|
||||||
|
▀▀▀██████▄▄▄ _______________
|
||||||
|
▄▄▄▄▄ █████████▄ / \
|
||||||
|
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
|
||||||
|
▀▀█████▄▄ ▀██████▄██ | _________________/
|
||||||
|
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
|
||||||
|
▀▀▀▄ ▀▀███ ▀ ▄▄
|
||||||
|
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
|
||||||
|
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
|
||||||
|
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
|
||||||
|
▌ ▐▀████▐███▒▒▒▒▒▐██▌
|
||||||
|
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
|
||||||
|
▀▀█████████▀
|
||||||
|
▄▄██▀██████▀█
|
||||||
|
▄██▀ ▀▀▀ █
|
||||||
|
▄█ ▐▌
|
||||||
|
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
|
||||||
|
▌ ▐ ▀▀▄▄▄▀
|
||||||
|
▀▀▄▄▀
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,5 +1,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
@ -13,6 +14,14 @@ from sanic.constants import HTTP_METHODS
|
||||||
# GET
|
# 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)
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
def test_versioned_routes_get(method):
|
def test_versioned_routes_get(method):
|
||||||
app = Sanic('test_shorhand_routes_get')
|
app = Sanic('test_shorhand_routes_get')
|
||||||
|
@ -348,6 +357,28 @@ def test_bp_static():
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.body == current_file_contents
|
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():
|
def test_bp_shorthand():
|
||||||
app = Sanic('test_shorhand_routes')
|
app = Sanic('test_shorhand_routes')
|
||||||
blueprint = Blueprint('test_shorhand_routes')
|
blueprint = Blueprint('test_shorhand_routes')
|
||||||
|
@ -449,41 +480,41 @@ def test_bp_shorthand():
|
||||||
|
|
||||||
def test_bp_group():
|
def test_bp_group():
|
||||||
app = Sanic('test_nested_bp_groups')
|
app = Sanic('test_nested_bp_groups')
|
||||||
|
|
||||||
deep_0 = Blueprint('deep_0', url_prefix='/deep')
|
deep_0 = Blueprint('deep_0', url_prefix='/deep')
|
||||||
deep_1 = Blueprint('deep_1', url_prefix = '/deep1')
|
deep_1 = Blueprint('deep_1', url_prefix = '/deep1')
|
||||||
|
|
||||||
@deep_0.route('/')
|
@deep_0.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('D0_OK')
|
return text('D0_OK')
|
||||||
|
|
||||||
@deep_1.route('/bottom')
|
@deep_1.route('/bottom')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('D1B_OK')
|
return text('D1B_OK')
|
||||||
|
|
||||||
mid_0 = Blueprint.group(deep_0, deep_1, url_prefix='/mid')
|
mid_0 = Blueprint.group(deep_0, deep_1, url_prefix='/mid')
|
||||||
mid_1 = Blueprint('mid_tier', url_prefix='/mid1')
|
mid_1 = Blueprint('mid_tier', url_prefix='/mid1')
|
||||||
|
|
||||||
@mid_1.route('/')
|
@mid_1.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('M1_OK')
|
return text('M1_OK')
|
||||||
|
|
||||||
top = Blueprint.group(mid_0, mid_1)
|
top = Blueprint.group(mid_0, mid_1)
|
||||||
|
|
||||||
app.blueprint(top)
|
app.blueprint(top)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def handler(request):
|
def handler(request):
|
||||||
return text('TOP_OK')
|
return text('TOP_OK')
|
||||||
|
|
||||||
request, response = app.test_client.get('/')
|
request, response = app.test_client.get('/')
|
||||||
assert response.text == 'TOP_OK'
|
assert response.text == 'TOP_OK'
|
||||||
|
|
||||||
request, response = app.test_client.get('/mid1')
|
request, response = app.test_client.get('/mid1')
|
||||||
assert response.text == 'M1_OK'
|
assert response.text == 'M1_OK'
|
||||||
|
|
||||||
request, response = app.test_client.get('/mid/deep')
|
request, response = app.test_client.get('/mid/deep')
|
||||||
assert response.text == 'D0_OK'
|
assert response.text == 'D0_OK'
|
||||||
|
|
||||||
request, response = app.test_client.get('/mid/deep1/bottom')
|
request, response = app.test_client.get('/mid/deep1/bottom')
|
||||||
assert response.text == 'D1B_OK'
|
assert response.text == 'D1B_OK'
|
||||||
|
|
|
@ -36,6 +36,21 @@ def test_static_file(static_file_directory, file_name):
|
||||||
assert response.body == get_file_content(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('file_name', ['test.file', 'decode me.txt'])
|
||||||
@pytest.mark.parametrize('base_uri', ['/static', '', '/dir'])
|
@pytest.mark.parametrize('base_uri', ['/static', '', '/dir'])
|
||||||
def test_static_directory(file_name, base_uri, static_file_directory):
|
def test_static_directory(file_name, base_uri, static_file_directory):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user