Version 0.0.5
This commit is contained in:
Shawn Niederriter 2017-04-11 21:47:08 +00:00
commit a0730aeb44
8 changed files with 81 additions and 36 deletions

View File

@ -1,5 +1,6 @@
aiofiles aiofiles
aiohttp==1.3.5 aiohttp==1.3.5
chardet<=2.3.0
beautifulsoup4 beautifulsoup4
coverage coverage
httptools httptools

View File

@ -1,6 +1,6 @@
from sanic.app import Sanic from sanic.app import Sanic
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
__version__ = '0.4.1' __version__ = '0.5.0'
__all__ = ['Sanic', 'Blueprint'] __all__ = ['Sanic', 'Blueprint']

View File

@ -28,10 +28,13 @@ if __name__ == "__main__":
raise ValueError("Module is not a Sanic app, it is a {}. " raise ValueError("Module is not a Sanic app, it is a {}. "
"Perhaps you meant {}.app?" "Perhaps you meant {}.app?"
.format(type(app).__name__, args.module)) .format(type(app).__name__, args.module))
if args.cert is not None or args.key is not None:
ssl = {'cert': args.cert, 'key': args.key}
else:
ssl = None
app.run(host=args.host, port=args.port, app.run(host=args.host, port=args.port,
workers=args.workers, debug=args.debug, workers=args.workers, debug=args.debug, ssl=ssl)
cert=args.cert, key=args.key)
except ImportError: except ImportError:
log.error("No module named {} found.\n" log.error("No module named {} found.\n"
" Example File: project/sanic_server.py -> app\n" " Example File: project/sanic_server.py -> app\n"

View File

@ -7,7 +7,7 @@ from functools import partial
from inspect import isawaitable, stack, getmodulename from inspect import isawaitable, stack, getmodulename
from traceback import format_exc from traceback import format_exc
from urllib.parse import urlencode, urlunparse from urllib.parse import urlencode, urlunparse
from ssl import create_default_context from ssl import create_default_context, Purpose
from sanic.config import Config from sanic.config import Config
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
@ -26,7 +26,7 @@ from sanic.websocket import WebSocketProtocol, ConnectionClosed
class Sanic: class Sanic:
def __init__(self, name=None, router=None, error_handler=None, def __init__(self, name=None, router=None, error_handler=None,
load_env=True): load_env=True, request_class=None):
# Only set up a default log handler if the # Only set up a default log handler if the
# end-user application didn't set anything up. # end-user application didn't set anything up.
if not logging.root.handlers and log.level == logging.NOTSET: if not logging.root.handlers and log.level == logging.NOTSET:
@ -44,6 +44,7 @@ class Sanic:
self.name = name self.name = name
self.router = router or Router() self.router = router or Router()
self.request_class = request_class
self.error_handler = error_handler or ErrorHandler() self.error_handler = error_handler or ErrorHandler()
self.config = Config(load_env=load_env) self.config = Config(load_env=load_env)
self.request_middleware = deque() self.request_middleware = deque()
@ -444,17 +445,7 @@ class Sanic:
# -------------------------------------------- # # -------------------------------------------- #
request.app = self request.app = self
response = await self._run_request_middleware(request)
response = False
# The if improves speed. I don't know why
if self.request_middleware:
for middleware in self.request_middleware:
response = middleware(request)
if isawaitable(response):
response = await response
if response:
break
# No middleware results # No middleware results
if not response: if not response:
# -------------------------------------------- # # -------------------------------------------- #
@ -472,20 +463,6 @@ class Sanic:
response = handler(request, *args, **kwargs) response = handler(request, *args, **kwargs)
if isawaitable(response): if isawaitable(response):
response = await response response = await response
# -------------------------------------------- #
# Response Middleware
# -------------------------------------------- #
if self.response_middleware:
for middleware in self.response_middleware:
_response = middleware(request, response)
if isawaitable(_response):
_response = await _response
if _response:
response = _response
break
except Exception as e: except Exception as e:
# -------------------------------------------- # # -------------------------------------------- #
# Response Generation Failed # Response Generation Failed
@ -503,6 +480,17 @@ class Sanic:
else: else:
response = HTTPResponse( response = HTTPResponse(
"An error occurred while handling an error") "An error occurred while handling an error")
finally:
# -------------------------------------------- #
# Response Middleware
# -------------------------------------------- #
try:
response = await self._run_response_middleware(request,
response)
except:
log.exception(
'Exception occured in one of response middleware handlers'
)
# pass the response to the correct callback # pass the response to the correct callback
if isinstance(response, StreamingHTTPResponse): if isinstance(response, StreamingHTTPResponse):
@ -615,6 +603,28 @@ class Sanic:
return await serve(**server_settings) return await serve(**server_settings)
async def _run_request_middleware(self, request):
# The if improves speed. I don't know why
if self.request_middleware:
for middleware in self.request_middleware:
response = middleware(request)
if isawaitable(response):
response = await response
if response:
return response
return None
async def _run_response_middleware(self, request, response):
if self.response_middleware:
for middleware in self.response_middleware:
_response = middleware(request, response)
if isawaitable(_response):
_response = await _response
if _response:
response = _response
break
return response
def _helper(self, host="127.0.0.1", port=8000, debug=False, def _helper(self, host="127.0.0.1", port=8000, debug=False,
before_start=None, after_start=None, before_stop=None, before_start=None, after_start=None, before_stop=None,
after_stop=None, ssl=None, sock=None, workers=1, loop=None, after_stop=None, ssl=None, sock=None, workers=1, loop=None,
@ -626,9 +636,9 @@ class Sanic:
# try common aliaseses # try common aliaseses
cert = ssl.get('cert') or ssl.get('certificate') cert = ssl.get('cert') or ssl.get('certificate')
key = ssl.get('key') or ssl.get('keyfile') key = ssl.get('key') or ssl.get('keyfile')
if not cert and key: if cert is None or key is None:
raise ValueError("SSLContext or certificate and key required.") raise ValueError("SSLContext or certificate and key required.")
context = create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) context = create_default_context(purpose=Purpose.CLIENT_AUTH)
context.load_cert_chain(cert, keyfile=key) context.load_cert_chain(cert, keyfile=key)
ssl = context ssl = context
if stop_event is not None: if stop_event is not None:
@ -659,6 +669,7 @@ class Sanic:
server_settings = { server_settings = {
'protocol': protocol, 'protocol': protocol,
'request_class': self.request_class,
'host': host, 'host': host,
'port': port, 'port': port,
'sock': sock, 'sock': sock,

View File

@ -129,7 +129,7 @@ class StreamingHTTPResponse(BaseHTTPResponse):
data = self._encode_body(data) data = self._encode_body(data)
self.transport.write( self.transport.write(
b"%b\r\n%b\r\n" % (str(len(data)).encode(), data)) b"%x\r\n%b\r\n" % (len(data), data))
async def stream( async def stream(
self, version="1.1", keep_alive=False, keep_alive_timeout=None): self, version="1.1", keep_alive=False, keep_alive_timeout=None):

View File

@ -64,12 +64,13 @@ class HttpProtocol(asyncio.Protocol):
'parser', 'request', 'url', 'headers', 'parser', 'request', 'url', 'headers',
# request config # request config
'request_handler', 'request_timeout', 'request_max_size', 'request_handler', 'request_timeout', 'request_max_size',
'request_class',
# connection management # connection management
'_total_request_size', '_timeout_handler', '_last_communication_time') '_total_request_size', '_timeout_handler', '_last_communication_time')
def __init__(self, *, loop, request_handler, error_handler, def __init__(self, *, loop, request_handler, error_handler,
signal=Signal(), connections=set(), request_timeout=60, signal=Signal(), connections=set(), request_timeout=60,
request_max_size=None): request_max_size=None, request_class=None):
self.loop = loop self.loop = loop
self.transport = None self.transport = None
self.request = None self.request = None
@ -82,6 +83,7 @@ class HttpProtocol(asyncio.Protocol):
self.error_handler = error_handler self.error_handler = error_handler
self.request_timeout = request_timeout self.request_timeout = request_timeout
self.request_max_size = request_max_size self.request_max_size = request_max_size
self.request_class = request_class or Request
self._total_request_size = 0 self._total_request_size = 0
self._timeout_handler = None self._timeout_handler = None
self._last_request_time = None self._last_request_time = None
@ -151,7 +153,7 @@ class HttpProtocol(asyncio.Protocol):
self.headers.append((name.decode().casefold(), value.decode())) self.headers.append((name.decode().casefold(), value.decode()))
def on_headers_complete(self): def on_headers_complete(self):
self.request = Request( self.request = self.request_class(
url_bytes=self.url, url_bytes=self.url,
headers=CIDict(self.headers), headers=CIDict(self.headers),
version=self.parser.get_http_version(), version=self.parser.get_http_version(),
@ -320,7 +322,7 @@ def serve(host, port, request_handler, error_handler, before_start=None,
request_timeout=60, ssl=None, sock=None, request_max_size=None, request_timeout=60, ssl=None, sock=None, request_max_size=None,
reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100,
register_sys_signals=True, run_async=False, connections=None, register_sys_signals=True, run_async=False, connections=None,
signal=Signal()): signal=Signal(), request_class=None):
"""Start asynchronous HTTP Server on an individual process. """Start asynchronous HTTP Server on an individual process.
:param host: Address to host on :param host: Address to host on
@ -345,6 +347,7 @@ def serve(host, port, request_handler, error_handler, before_start=None,
:param reuse_port: `True` for multiple workers :param reuse_port: `True` for multiple workers
:param loop: asyncio compatible event loop :param loop: asyncio compatible event loop
:param protocol: subclass of asyncio protocol class :param protocol: subclass of asyncio protocol class
:param request_class: Request class to use
:return: Nothing :return: Nothing
""" """
if not run_async: if not run_async:
@ -366,6 +369,7 @@ def serve(host, port, request_handler, error_handler, before_start=None,
error_handler=error_handler, error_handler=error_handler,
request_timeout=request_timeout, request_timeout=request_timeout,
request_max_size=request_max_size, request_max_size=request_max_size,
request_class=request_class,
) )
server_coroutine = loop.create_server( server_coroutine = loop.create_server(

View File

@ -1,3 +1,5 @@
import traceback
from sanic.log import log from sanic.log import log
HOST = '127.0.0.1' HOST = '127.0.0.1'
@ -50,6 +52,8 @@ class SanicTestClient:
**request_kwargs) **request_kwargs)
results[-1] = response results[-1] = response
except Exception as e: except Exception as e:
log.error(
'Exception:\n{}'.format(traceback.format_exc()))
exceptions.append(e) exceptions.append(e)
self.app.stop() self.app.stop()

View File

@ -2,6 +2,7 @@ from json import loads as json_loads, dumps as json_dumps
from sanic import Sanic from sanic import Sanic
from sanic.request import Request from sanic.request import Request
from sanic.response import json, text, HTTPResponse from sanic.response import json, text, HTTPResponse
from sanic.exceptions import NotFound
# ------------------------------------------------------------ # # ------------------------------------------------------------ #
@ -53,6 +54,27 @@ def test_middleware_response():
assert isinstance(results[2], HTTPResponse) assert isinstance(results[2], HTTPResponse)
def test_middleware_response_exception():
app = Sanic('test_middleware_response_exception')
result = {'status_code': None}
@app.middleware('response')
async def process_response(reqest, response):
result['status_code'] = response.status
return response
@app.exception(NotFound)
async def error_handler(request, exception):
return text('OK', exception.status_code)
@app.route('/')
async def handler(request):
return text('FAIL')
request, response = app.test_client.get('/page_not_found')
assert response.text == 'OK'
assert result['status_code'] == 404
def test_middleware_override_request(): def test_middleware_override_request():
app = Sanic('test_middleware_override_request') app = Sanic('test_middleware_override_request')