Resolved issue #949
This commit is contained in:
parent
1b0286916e
commit
3b0634bec8
|
@ -352,13 +352,13 @@ class Sanic:
|
||||||
attach_to=middleware_or_request)
|
attach_to=middleware_or_request)
|
||||||
|
|
||||||
# Static Files
|
# 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,
|
use_modified_since=True, use_content_range=False,
|
||||||
stream_large_files=False, name='static'):
|
stream_large_files=False, name='static'):
|
||||||
"""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, host, pattern,
|
||||||
use_modified_since, use_content_range,
|
use_modified_since, use_content_range,
|
||||||
stream_large_files, name)
|
stream_large_files, name)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ FutureStatic = namedtuple('Route',
|
||||||
|
|
||||||
|
|
||||||
class Blueprint:
|
class Blueprint:
|
||||||
|
|
||||||
def __init__(self, name,
|
def __init__(self, name,
|
||||||
url_prefix=None,
|
url_prefix=None,
|
||||||
host=None, version=None,
|
host=None, version=None,
|
||||||
|
@ -92,7 +91,7 @@ class Blueprint:
|
||||||
# Prepend the blueprint URI prefix if available
|
# Prepend the blueprint URI prefix if available
|
||||||
uri = url_prefix + future.uri if url_prefix else future.uri
|
uri = url_prefix + future.uri if url_prefix else future.uri
|
||||||
app.static(uri, future.file_or_directory,
|
app.static(uri, future.file_or_directory,
|
||||||
*future.args, **future.kwargs)
|
self.host, *future.args, **future.kwargs)
|
||||||
|
|
||||||
# Event listeners
|
# Event listeners
|
||||||
for event, listeners in self.listeners.items():
|
for event, listeners in self.listeners.items():
|
||||||
|
@ -115,6 +114,7 @@ class Blueprint:
|
||||||
name)
|
name)
|
||||||
self.routes.append(route)
|
self.routes.append(route)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
||||||
|
@ -165,6 +165,7 @@ class Blueprint:
|
||||||
False, version, name)
|
False, version, name)
|
||||||
self.websocket_routes.append(route)
|
self.websocket_routes.append(route)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_websocket_route(self, handler, uri, host=None, version=None,
|
def add_websocket_route(self, handler, uri, host=None, version=None,
|
||||||
|
@ -184,13 +185,16 @@ class Blueprint:
|
||||||
|
|
||||||
:param event: Event to listen to.
|
:param event: Event to listen to.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(listener):
|
def decorator(listener):
|
||||||
self.listeners[event].append(listener)
|
self.listeners[event].append(listener)
|
||||||
return listener
|
return listener
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def middleware(self, *args, **kwargs):
|
def middleware(self, *args, **kwargs):
|
||||||
"""Create a blueprint middleware from a decorated function."""
|
"""Create a blueprint middleware from a decorated function."""
|
||||||
|
|
||||||
def register_middleware(_middleware):
|
def register_middleware(_middleware):
|
||||||
future_middleware = FutureMiddleware(_middleware, args, kwargs)
|
future_middleware = FutureMiddleware(_middleware, args, kwargs)
|
||||||
self.middlewares.append(future_middleware)
|
self.middlewares.append(future_middleware)
|
||||||
|
@ -206,10 +210,12 @@ class Blueprint:
|
||||||
|
|
||||||
def exception(self, *args, **kwargs):
|
def exception(self, *args, **kwargs):
|
||||||
"""Create a blueprint exception from a decorated function."""
|
"""Create a blueprint exception from a decorated function."""
|
||||||
|
|
||||||
def decorator(handler):
|
def decorator(handler):
|
||||||
exception = FutureException(handler, args, kwargs)
|
exception = FutureException(handler, args, kwargs)
|
||||||
self.exceptions.append(exception)
|
self.exceptions.append(exception)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def static(self, uri, file_or_directory, *args, **kwargs):
|
def static(self, uri, file_or_directory, *args, **kwargs):
|
||||||
|
|
|
@ -16,7 +16,7 @@ from sanic.handlers import ContentRangeHandler
|
||||||
from sanic.response import file, file_stream, HTTPResponse
|
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,
|
use_modified_since, use_content_range,
|
||||||
stream_large_files, name='static'):
|
stream_large_files, name='static'):
|
||||||
# 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
|
||||||
|
@ -29,6 +29,7 @@ def register(app, uri, file_or_directory, pattern,
|
||||||
:param app: Sanic
|
:param app: Sanic
|
||||||
:param file_or_directory: File or directory path to serve from
|
:param file_or_directory: File or directory path to serve from
|
||||||
:param uri: URL 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 pattern: regular expression used to match files in the URL
|
||||||
:param use_modified_since: If true, send file modified time, and return
|
:param use_modified_since: If true, send file modified time, and return
|
||||||
not modified if the browser's matches the
|
not modified if the browser's matches the
|
||||||
|
@ -122,4 +123,4 @@ def register(app, uri, file_or_directory, pattern,
|
||||||
if not name.startswith('_static_'):
|
if not name.startswith('_static_'):
|
||||||
name = '_static_{}'.format(name)
|
name = '_static_{}'.format(name)
|
||||||
|
|
||||||
app.route(uri, methods=['GET', 'HEAD'], name=name)(_handler)
|
app.route(uri, methods=['GET', 'HEAD'], name=name, host=host)(_handler)
|
||||||
|
|
|
@ -13,7 +13,7 @@ class SanicTestClient:
|
||||||
|
|
||||||
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
|
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
|
||||||
import aiohttp
|
import aiohttp
|
||||||
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')):
|
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://', '//')):
|
||||||
url = uri
|
url = uri
|
||||||
else:
|
else:
|
||||||
url = 'http://{host}:{port}{uri}'.format(
|
url = 'http://{host}:{port}{uri}'.format(
|
||||||
|
@ -51,6 +51,7 @@ class SanicTestClient:
|
||||||
def _collect_request(request):
|
def _collect_request(request):
|
||||||
if results[0] is None:
|
if results[0] is None:
|
||||||
results[0] = request
|
results[0] = request
|
||||||
|
|
||||||
self.app.request_middleware.appendleft(_collect_request)
|
self.app.request_middleware.appendleft(_collect_request)
|
||||||
|
|
||||||
@self.app.listener('after_server_start')
|
@self.app.listener('after_server_start')
|
||||||
|
|
|
@ -3,7 +3,8 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
|
||||||
|
from sanic import Sanic, exceptions
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@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 'Content-Range' in response.headers
|
||||||
assert response.headers['Content-Range'] == "bytes */%s" % (
|
assert response.headers['Content-Range'] == "bytes */%s" % (
|
||||||
len(get_file_content(static_file_directory, file_name)),)
|
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)
|
Loading…
Reference in New Issue
Block a user