2017-01-28 01:34:21 +00:00
|
|
|
import logging
|
2017-02-09 01:37:32 +00:00
|
|
|
import re
|
2017-01-31 01:04:51 +00:00
|
|
|
import warnings
|
2016-10-18 09:22:49 +01:00
|
|
|
from asyncio import get_event_loop
|
2016-10-24 10:09:07 +01:00
|
|
|
from collections import deque
|
2016-10-21 12:11:18 +01:00
|
|
|
from functools import partial
|
2016-11-19 01:06:16 +00:00
|
|
|
from inspect import isawaitable, stack, getmodulename
|
2016-10-15 20:59:00 +01:00
|
|
|
from traceback import format_exc
|
2017-02-02 17:21:14 +00:00
|
|
|
from urllib.parse import urlencode, urlunparse
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
from .config import Config
|
2017-01-28 04:05:46 +00:00
|
|
|
from .constants import HTTP_METHODS
|
2017-02-09 01:59:34 +00:00
|
|
|
from .handlers import ErrorHandler
|
|
|
|
from .exceptions import ServerError, URLBuildError
|
2016-12-23 05:00:57 +00:00
|
|
|
from .log import log
|
2016-10-15 20:59:00 +01:00
|
|
|
from .response import HTTPResponse
|
|
|
|
from .router import Router
|
2017-01-28 01:34:21 +00:00
|
|
|
from .server import serve, serve_multiple, HttpProtocol
|
2016-10-24 09:21:06 +01:00
|
|
|
from .static import register as static_register
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Sanic:
|
2016-12-23 05:00:57 +00:00
|
|
|
def __init__(self, name=None, router=None,
|
2017-01-17 23:38:20 +00:00
|
|
|
error_handler=None):
|
2017-01-20 22:09:27 +00:00
|
|
|
# Only set up a default log handler if the
|
|
|
|
# end-user application didn't set anything up.
|
|
|
|
if not logging.root.handlers and log.level == logging.NOTSET:
|
|
|
|
formatter = logging.Formatter(
|
|
|
|
"%(asctime)s: %(levelname)s: %(message)s")
|
|
|
|
handler = logging.StreamHandler()
|
|
|
|
handler.setFormatter(formatter)
|
|
|
|
log.addHandler(handler)
|
|
|
|
log.setLevel(logging.INFO)
|
2016-11-19 01:06:16 +00:00
|
|
|
if name is None:
|
|
|
|
frame_records = stack()[1]
|
|
|
|
name = getmodulename(frame_records[1])
|
2016-10-15 20:59:00 +01:00
|
|
|
self.name = name
|
|
|
|
self.router = router or Router()
|
2017-01-31 01:04:51 +00:00
|
|
|
self.error_handler = error_handler or ErrorHandler()
|
2016-10-15 20:59:00 +01:00
|
|
|
self.config = Config()
|
2016-10-24 10:09:07 +01:00
|
|
|
self.request_middleware = deque()
|
|
|
|
self.response_middleware = deque()
|
2016-10-16 05:48:49 +01:00
|
|
|
self.blueprints = {}
|
|
|
|
self._blueprint_order = []
|
2016-10-21 12:11:18 +01:00
|
|
|
self.debug = None
|
2017-01-07 23:46:43 +00:00
|
|
|
self.sock = None
|
|
|
|
self.processes = None
|
2016-10-15 20:59:00 +01:00
|
|
|
|
2016-10-24 09:21:06 +01:00
|
|
|
# Register alternative method names
|
|
|
|
self.go_fast = self.run
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
# Registration
|
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
|
|
|
|
# Decorator
|
2017-01-19 14:56:51 +00:00
|
|
|
def route(self, uri, methods=frozenset({'GET'}), host=None):
|
2016-10-15 20:59:00 +01:00
|
|
|
"""
|
|
|
|
Decorates a function to be registered as a route
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
:param uri: path of the URL
|
|
|
|
:param methods: list or tuple of methods allowed
|
2017-01-31 01:04:51 +00:00
|
|
|
:param host:
|
2016-10-15 20:59:00 +01:00
|
|
|
:return: decorated function
|
|
|
|
"""
|
|
|
|
|
2016-10-24 09:21:06 +01:00
|
|
|
# Fix case where the user did not prefix the URL with a /
|
|
|
|
# and will probably get confused as to why it's not working
|
|
|
|
if not uri.startswith('/'):
|
|
|
|
uri = '/' + uri
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
def response(handler):
|
2017-01-08 23:48:12 +00:00
|
|
|
self.router.add(uri=uri, methods=methods, handler=handler,
|
|
|
|
host=host)
|
2016-10-15 20:59:00 +01:00
|
|
|
return handler
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
2017-01-20 07:47:07 +00:00
|
|
|
# Shorthand method decorators
|
|
|
|
def get(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"GET"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
|
|
|
def post(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"POST"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
|
|
|
def put(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"PUT"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
|
|
|
def head(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"HEAD"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
|
|
|
def options(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"OPTIONS"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
|
|
|
def patch(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"PATCH"}), host=host)
|
2017-01-20 07:47:07 +00:00
|
|
|
|
2017-01-28 04:05:46 +00:00
|
|
|
def delete(self, uri, host=None):
|
2017-01-31 01:04:51 +00:00
|
|
|
return self.route(uri, methods=frozenset({"DELETE"}), host=host)
|
2017-01-28 04:05:46 +00:00
|
|
|
|
2017-01-28 03:00:33 +00:00
|
|
|
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None):
|
2016-11-25 07:10:25 +00:00
|
|
|
"""
|
2016-11-25 07:29:25 +00:00
|
|
|
A helper method to register class instance or
|
|
|
|
functions as a handler to the application url
|
|
|
|
routes.
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2016-11-25 07:10:25 +00:00
|
|
|
:param handler: function or class instance
|
|
|
|
:param uri: path of the URL
|
2017-01-28 04:05:46 +00:00
|
|
|
:param methods: list or tuple of methods allowed, these are overridden
|
|
|
|
if using a HTTPMethodView
|
2017-01-31 01:04:51 +00:00
|
|
|
:param host:
|
2016-11-25 07:10:25 +00:00
|
|
|
:return: function or class instance
|
|
|
|
"""
|
2017-01-28 04:05:46 +00:00
|
|
|
# Handle HTTPMethodView differently
|
|
|
|
if hasattr(handler, 'view_class'):
|
|
|
|
methods = frozenset(HTTP_METHODS)
|
2017-01-08 23:48:12 +00:00
|
|
|
self.route(uri=uri, methods=methods, host=host)(handler)
|
2016-11-25 07:10:25 +00:00
|
|
|
return handler
|
|
|
|
|
2017-01-08 23:48:12 +00:00
|
|
|
def remove_route(self, uri, clean_cache=True, host=None):
|
|
|
|
self.router.remove(uri, clean_cache, host)
|
2016-12-30 09:36:57 +00:00
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
# Decorator
|
|
|
|
def exception(self, *exceptions):
|
|
|
|
"""
|
2016-10-18 10:50:28 +01:00
|
|
|
Decorates a function to be registered as a handler for exceptions
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2017-01-31 01:04:51 +00:00
|
|
|
:param exceptions: exceptions
|
2016-10-15 20:59:00 +01:00
|
|
|
:return: decorated function
|
|
|
|
"""
|
|
|
|
|
|
|
|
def response(handler):
|
|
|
|
for exception in exceptions:
|
|
|
|
self.error_handler.add(exception, handler)
|
|
|
|
return handler
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
# Decorator
|
|
|
|
def middleware(self, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Decorates and registers middleware to be called before a request
|
|
|
|
can either be called as @app.middleware or @app.middleware('request')
|
|
|
|
"""
|
|
|
|
attach_to = 'request'
|
|
|
|
|
|
|
|
def register_middleware(middleware):
|
|
|
|
if attach_to == 'request':
|
|
|
|
self.request_middleware.append(middleware)
|
|
|
|
if attach_to == 'response':
|
2016-10-24 10:09:07 +01:00
|
|
|
self.response_middleware.appendleft(middleware)
|
2016-10-15 20:59:00 +01:00
|
|
|
return middleware
|
|
|
|
|
|
|
|
# Detect which way this was called, @middleware or @middleware('AT')
|
|
|
|
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
|
|
|
|
return register_middleware(args[0])
|
|
|
|
else:
|
|
|
|
attach_to = args[0]
|
|
|
|
return register_middleware
|
|
|
|
|
2016-10-24 09:21:06 +01:00
|
|
|
# Static Files
|
2016-10-25 10:45:28 +01:00
|
|
|
def static(self, uri, file_or_directory, pattern='.+',
|
2017-01-31 01:04:51 +00:00
|
|
|
use_modified_since=True, use_content_range=False):
|
2016-10-24 09:21:06 +01:00
|
|
|
"""
|
|
|
|
Registers a root to serve files from. The input can either be a file
|
|
|
|
or a directory. See
|
|
|
|
"""
|
2016-10-25 10:45:28 +01:00
|
|
|
static_register(self, uri, file_or_directory, pattern,
|
2017-01-31 01:04:51 +00:00
|
|
|
use_modified_since, use_content_range)
|
2016-10-24 09:21:06 +01:00
|
|
|
|
|
|
|
def blueprint(self, blueprint, **options):
|
2016-10-15 20:38:12 +01:00
|
|
|
"""
|
|
|
|
Registers a blueprint on the application.
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2016-10-15 20:38:12 +01:00
|
|
|
:param blueprint: Blueprint object
|
|
|
|
:param options: option dictionary with blueprint defaults
|
|
|
|
:return: Nothing
|
|
|
|
"""
|
|
|
|
if blueprint.name in self.blueprints:
|
|
|
|
assert self.blueprints[blueprint.name] is blueprint, \
|
2016-10-16 05:48:49 +01:00
|
|
|
'A blueprint with the name "%s" is already registered. ' \
|
|
|
|
'Blueprint names must be unique.' % \
|
|
|
|
(blueprint.name,)
|
2016-10-15 20:38:12 +01:00
|
|
|
else:
|
|
|
|
self.blueprints[blueprint.name] = blueprint
|
|
|
|
self._blueprint_order.append(blueprint)
|
|
|
|
blueprint.register(self, options)
|
|
|
|
|
2016-10-24 09:21:06 +01:00
|
|
|
def register_blueprint(self, *args, **kwargs):
|
|
|
|
# TODO: deprecate 1.0
|
2017-01-28 01:59:08 +00:00
|
|
|
if self.debug:
|
|
|
|
warnings.simplefilter('default')
|
|
|
|
warnings.warn("Use of register_blueprint will be deprecated in "
|
|
|
|
"version 1.0. Please use the blueprint method"
|
|
|
|
" instead",
|
|
|
|
DeprecationWarning)
|
2016-10-24 09:21:06 +01:00
|
|
|
return self.blueprint(*args, **kwargs)
|
|
|
|
|
2017-02-02 17:21:14 +00:00
|
|
|
def url_for(self, view_name: str, **kwargs):
|
2017-02-02 17:52:48 +00:00
|
|
|
"""Builds a URL based on a view name and the values provided.
|
|
|
|
|
|
|
|
In order to build a URL, all request parameters must be supplied as
|
|
|
|
keyword arguments, and each parameter must pass the test for the
|
|
|
|
specified parameter type. If these conditions are not met, a
|
|
|
|
`URLBuildError` will be thrown.
|
|
|
|
|
|
|
|
Keyword arguments that are not request parameters will be included in
|
|
|
|
the output URL's query string.
|
|
|
|
|
|
|
|
:param view_name: A string referencing the view name
|
|
|
|
:param **kwargs: keys and values that are used to build request
|
|
|
|
parameters and query string arguments.
|
|
|
|
|
|
|
|
:return: the built URL
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
URLBuildError
|
|
|
|
"""
|
|
|
|
# find the route by the supplied view name
|
2017-02-02 17:21:14 +00:00
|
|
|
uri, route = self.router.find_route_by_view_name(view_name)
|
|
|
|
|
|
|
|
if not uri or not route:
|
|
|
|
raise URLBuildError(
|
|
|
|
'Endpoint with name `{}` was not found'.format(
|
|
|
|
view_name))
|
|
|
|
|
|
|
|
out = uri
|
2017-02-02 17:52:48 +00:00
|
|
|
|
|
|
|
# find all the parameters we will need to build in the URL
|
2017-02-02 17:21:14 +00:00
|
|
|
matched_params = re.findall(
|
|
|
|
self.router.parameter_pattern, uri)
|
|
|
|
|
2017-02-09 08:44:23 +00:00
|
|
|
# _method is only a placeholder now, don't know how to support it
|
|
|
|
kwargs.pop('_method', None)
|
|
|
|
anchor = kwargs.pop('_anchor', '')
|
|
|
|
# _external need SERVER_NAME in config or pass _server arg
|
|
|
|
external = kwargs.pop('_external', False)
|
|
|
|
scheme = kwargs.pop('_scheme', '')
|
|
|
|
if scheme and not external:
|
|
|
|
raise ValueError('When specifying _scheme, _external must be True')
|
|
|
|
|
|
|
|
netloc = kwargs.pop('_server', self.config.get('SERVER_NAME', ''))
|
2017-02-02 17:21:14 +00:00
|
|
|
for match in matched_params:
|
|
|
|
name, _type, pattern = self.router.parse_parameter_string(
|
|
|
|
match)
|
2017-02-02 17:52:48 +00:00
|
|
|
# we only want to match against each individual parameter
|
2017-02-02 17:21:14 +00:00
|
|
|
specific_pattern = '^{}$'.format(pattern)
|
|
|
|
supplied_param = None
|
2017-02-02 17:52:48 +00:00
|
|
|
|
2017-02-02 17:21:14 +00:00
|
|
|
if kwargs.get(name):
|
|
|
|
supplied_param = kwargs.get(name)
|
|
|
|
del kwargs[name]
|
|
|
|
else:
|
|
|
|
raise URLBuildError(
|
|
|
|
'Required parameter `{}` was not passed to url_for'.format(
|
|
|
|
name))
|
|
|
|
|
|
|
|
supplied_param = str(supplied_param)
|
2017-02-02 17:52:48 +00:00
|
|
|
# determine if the parameter supplied by the caller passes the test
|
|
|
|
# in the URL
|
2017-02-02 17:21:14 +00:00
|
|
|
passes_pattern = re.match(specific_pattern, supplied_param)
|
|
|
|
|
|
|
|
if not passes_pattern:
|
|
|
|
if _type != str:
|
|
|
|
msg = (
|
|
|
|
'Value "{}" for parameter `{}` does not '
|
|
|
|
'match pattern for type `{}`: {}'.format(
|
|
|
|
supplied_param, name, _type.__name__, pattern))
|
|
|
|
else:
|
|
|
|
msg = (
|
|
|
|
'Value "{}" for parameter `{}` '
|
|
|
|
'does not satisfy pattern {}'.format(
|
|
|
|
supplied_param, name, pattern))
|
|
|
|
raise URLBuildError(msg)
|
|
|
|
|
2017-02-02 17:52:48 +00:00
|
|
|
# replace the parameter in the URL with the supplied value
|
2017-02-02 17:21:14 +00:00
|
|
|
replacement_regex = '(<{}.*?>)'.format(name)
|
|
|
|
|
|
|
|
out = re.sub(
|
|
|
|
replacement_regex, supplied_param, out)
|
|
|
|
|
|
|
|
# parse the remainder of the keyword arguments into a querystring
|
|
|
|
if kwargs:
|
2017-02-09 08:44:23 +00:00
|
|
|
query_string = urlencode(kwargs, doseq=True)
|
|
|
|
# scheme://netloc/path;parameters?query#fragment
|
|
|
|
out = urlunparse((scheme, netloc, out, '', query_string, anchor))
|
2017-02-02 17:21:14 +00:00
|
|
|
|
|
|
|
return out
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
# Request Handling
|
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
|
2016-10-21 12:11:18 +01:00
|
|
|
def converted_response_type(self, response):
|
|
|
|
pass
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
async def handle_request(self, request, response_callback):
|
|
|
|
"""
|
2016-10-16 14:01:59 +01:00
|
|
|
Takes a request from the HTTP Server and returns a response object to
|
|
|
|
be sent back The HTTP Server only expects a response object, so
|
|
|
|
exception handling must be done here
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
:param request: HTTP Request object
|
2016-10-16 14:01:59 +01:00
|
|
|
:param response_callback: Response function to be called with the
|
2016-12-25 09:43:45 +00:00
|
|
|
response as the only argument
|
2016-10-15 20:59:00 +01:00
|
|
|
:return: Nothing
|
|
|
|
"""
|
|
|
|
try:
|
2016-10-21 12:11:18 +01:00
|
|
|
# -------------------------------------------- #
|
|
|
|
# Request Middleware
|
|
|
|
# -------------------------------------------- #
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
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
|
|
|
|
if not response:
|
2016-10-21 12:11:18 +01:00
|
|
|
# -------------------------------------------- #
|
|
|
|
# Execute Handler
|
|
|
|
# -------------------------------------------- #
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
# Fetch handler from router
|
|
|
|
handler, args, kwargs = self.router.get(request)
|
|
|
|
if handler is None:
|
2016-10-16 14:01:59 +01:00
|
|
|
raise ServerError(
|
|
|
|
("'None' was returned while requesting a "
|
|
|
|
"handler from the router"))
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
# Run response handler
|
|
|
|
response = handler(request, *args, **kwargs)
|
|
|
|
if isawaitable(response):
|
|
|
|
response = await response
|
|
|
|
|
2016-12-23 14:59:04 +00:00
|
|
|
# -------------------------------------------- #
|
|
|
|
# Response Middleware
|
|
|
|
# -------------------------------------------- #
|
2016-10-21 12:11:18 +01:00
|
|
|
|
2016-12-23 14:59:04 +00:00
|
|
|
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
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
except Exception as e:
|
2016-10-21 12:11:18 +01:00
|
|
|
# -------------------------------------------- #
|
|
|
|
# Response Generation Failed
|
|
|
|
# -------------------------------------------- #
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
try:
|
|
|
|
response = self.error_handler.response(request, e)
|
|
|
|
if isawaitable(response):
|
|
|
|
response = await response
|
|
|
|
except Exception as e:
|
|
|
|
if self.debug:
|
2016-10-16 14:01:59 +01:00
|
|
|
response = HTTPResponse(
|
|
|
|
"Error while handling error: {}\nStack: {}".format(
|
|
|
|
e, format_exc()))
|
2016-10-15 20:59:00 +01:00
|
|
|
else:
|
2016-10-16 14:01:59 +01:00
|
|
|
response = HTTPResponse(
|
2017-01-19 09:04:16 +00:00
|
|
|
"An error occurred while handling an error")
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
response_callback(response)
|
|
|
|
|
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
# Execution
|
|
|
|
# -------------------------------------------------------------------- #
|
|
|
|
|
2016-10-21 12:11:18 +01:00
|
|
|
def run(self, host="127.0.0.1", port=8000, debug=False, before_start=None,
|
2017-01-14 12:16:59 +00:00
|
|
|
after_start=None, before_stop=None, after_stop=None, ssl=None,
|
2017-01-25 21:11:04 +00:00
|
|
|
sock=None, workers=1, loop=None, protocol=HttpProtocol,
|
|
|
|
backlog=100, stop_event=None, register_sys_signals=True):
|
2016-10-15 20:59:00 +01:00
|
|
|
"""
|
2016-10-16 14:01:59 +01:00
|
|
|
Runs the HTTP Server and listens until keyboard interrupt or term
|
|
|
|
signal. On termination, drains connections before closing.
|
2016-12-25 09:43:45 +00:00
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
:param host: Address to host on
|
|
|
|
:param port: Port to host on
|
|
|
|
:param debug: Enables debug output (slows server)
|
2016-12-28 09:03:12 +00:00
|
|
|
:param before_start: Functions to be executed before the server starts
|
2016-12-25 09:43:45 +00:00
|
|
|
accepting connections
|
2016-12-28 09:03:12 +00:00
|
|
|
:param after_start: Functions to be executed after the server starts
|
2016-12-25 09:43:45 +00:00
|
|
|
accepting connections
|
2016-12-28 09:03:12 +00:00
|
|
|
:param before_stop: Functions to be executed when a stop signal is
|
2016-12-25 09:43:45 +00:00
|
|
|
received before it is respected
|
2016-12-28 09:03:12 +00:00
|
|
|
:param after_stop: Functions to be executed when all requests are
|
2016-12-25 09:43:45 +00:00
|
|
|
complete
|
2017-01-14 12:16:59 +00:00
|
|
|
:param ssl: SSLContext for SSL encryption of worker(s)
|
2016-10-18 09:22:49 +01:00
|
|
|
:param sock: Socket for the server to accept connections from
|
|
|
|
:param workers: Number of processes
|
2016-12-25 09:43:45 +00:00
|
|
|
received before it is respected
|
2017-01-31 01:04:51 +00:00
|
|
|
:param loop:
|
|
|
|
:param backlog:
|
|
|
|
:param stop_event:
|
|
|
|
:param register_sys_signals:
|
2016-12-29 04:11:27 +00:00
|
|
|
:param protocol: Subclass of asyncio protocol class
|
2016-10-15 20:59:00 +01:00
|
|
|
:return: Nothing
|
|
|
|
"""
|
2017-01-29 22:01:00 +00:00
|
|
|
server_settings = self._helper(
|
|
|
|
host=host, port=port, debug=debug, before_start=before_start,
|
|
|
|
after_start=after_start, before_stop=before_stop,
|
|
|
|
after_stop=after_stop, ssl=ssl, sock=sock, workers=workers,
|
|
|
|
loop=loop, protocol=protocol, backlog=backlog,
|
|
|
|
stop_event=stop_event, register_sys_signals=register_sys_signals)
|
2016-10-15 20:59:00 +01:00
|
|
|
try:
|
2016-10-18 09:22:49 +01:00
|
|
|
if workers == 1:
|
|
|
|
serve(**server_settings)
|
|
|
|
else:
|
2017-01-28 01:34:21 +00:00
|
|
|
serve_multiple(server_settings, workers, stop_event)
|
2016-10-16 14:01:59 +01:00
|
|
|
except Exception as e:
|
|
|
|
log.exception(
|
2016-11-19 07:16:20 +00:00
|
|
|
'Experienced exception while trying to serve')
|
2016-10-18 09:22:49 +01:00
|
|
|
log.info("Server Stopped")
|
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
def stop(self):
|
2017-01-28 01:34:21 +00:00
|
|
|
"""This kills the Sanic"""
|
2016-10-18 09:22:49 +01:00
|
|
|
get_event_loop().stop()
|
|
|
|
|
2017-01-24 03:58:37 +00:00
|
|
|
async def create_server(self, host="127.0.0.1", port=8000, debug=False,
|
|
|
|
before_start=None, after_start=None,
|
|
|
|
before_stop=None, after_stop=None, ssl=None,
|
|
|
|
sock=None, loop=None, protocol=HttpProtocol,
|
|
|
|
backlog=100, stop_event=None):
|
2017-01-26 16:36:00 +00:00
|
|
|
"""
|
2017-01-24 03:58:37 +00:00
|
|
|
Asynchronous version of `run`.
|
2017-01-26 16:36:00 +00:00
|
|
|
"""
|
2017-01-29 22:01:00 +00:00
|
|
|
server_settings = self._helper(
|
|
|
|
host=host, port=port, debug=debug, before_start=before_start,
|
|
|
|
after_start=after_start, before_stop=before_stop,
|
|
|
|
after_stop=after_stop, ssl=ssl, sock=sock, loop=loop,
|
|
|
|
protocol=protocol, backlog=backlog, stop_event=stop_event,
|
2017-01-31 01:04:51 +00:00
|
|
|
run_async=True)
|
2017-01-28 22:54:15 +00:00
|
|
|
|
|
|
|
# Serve
|
|
|
|
proto = "http"
|
|
|
|
if ssl is not None:
|
|
|
|
proto = "https"
|
|
|
|
log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port))
|
|
|
|
|
|
|
|
return await serve(**server_settings)
|
|
|
|
|
|
|
|
def _helper(self, host="127.0.0.1", port=8000, debug=False,
|
|
|
|
before_start=None, after_start=None, before_stop=None,
|
|
|
|
after_stop=None, ssl=None, sock=None, workers=1, loop=None,
|
|
|
|
protocol=HttpProtocol, backlog=100, stop_event=None,
|
2017-01-29 22:01:00 +00:00
|
|
|
register_sys_signals=True, run_async=False):
|
2017-01-28 22:54:15 +00:00
|
|
|
"""
|
2017-01-29 22:01:00 +00:00
|
|
|
Helper function used by `run` and `create_server`.
|
2017-01-28 22:54:15 +00:00
|
|
|
"""
|
|
|
|
|
2017-01-26 23:03:25 +00:00
|
|
|
if loop is not None:
|
2017-02-04 07:54:37 +00:00
|
|
|
if debug:
|
2017-01-28 01:59:08 +00:00
|
|
|
warnings.simplefilter('default')
|
|
|
|
warnings.warn("Passing a loop will be deprecated in version"
|
|
|
|
" 0.4.0 https://github.com/channelcat/sanic/"
|
|
|
|
"pull/335 has more information.",
|
|
|
|
DeprecationWarning)
|
|
|
|
|
2017-02-04 07:54:37 +00:00
|
|
|
self.error_handler.debug = debug
|
|
|
|
self.debug = debug
|
|
|
|
self.loop = loop = get_event_loop()
|
|
|
|
|
2017-01-24 03:58:37 +00:00
|
|
|
server_settings = {
|
|
|
|
'protocol': protocol,
|
|
|
|
'host': host,
|
|
|
|
'port': port,
|
|
|
|
'sock': sock,
|
|
|
|
'ssl': ssl,
|
|
|
|
'debug': debug,
|
|
|
|
'request_handler': self.handle_request,
|
|
|
|
'error_handler': self.error_handler,
|
|
|
|
'request_timeout': self.config.REQUEST_TIMEOUT,
|
|
|
|
'request_max_size': self.config.REQUEST_MAX_SIZE,
|
|
|
|
'loop': loop,
|
2017-01-28 22:54:15 +00:00
|
|
|
'register_sys_signals': register_sys_signals,
|
2017-01-24 03:58:37 +00:00
|
|
|
'backlog': backlog
|
|
|
|
}
|
|
|
|
|
|
|
|
# -------------------------------------------- #
|
|
|
|
# Register start/stop events
|
|
|
|
# -------------------------------------------- #
|
|
|
|
|
|
|
|
for event_name, settings_name, args, reverse in (
|
|
|
|
("before_server_start", "before_start", before_start, False),
|
|
|
|
("after_server_start", "after_start", after_start, False),
|
|
|
|
("before_server_stop", "before_stop", before_stop, True),
|
2017-01-28 22:54:15 +00:00
|
|
|
("after_server_stop", "after_stop", after_stop, True),
|
2017-01-31 01:04:51 +00:00
|
|
|
):
|
2017-01-24 03:58:37 +00:00
|
|
|
listeners = []
|
|
|
|
for blueprint in self.blueprints.values():
|
|
|
|
listeners += blueprint.listeners[event_name]
|
|
|
|
if args:
|
|
|
|
if callable(args):
|
|
|
|
args = [args]
|
|
|
|
listeners += args
|
|
|
|
if reverse:
|
|
|
|
listeners.reverse()
|
|
|
|
# Prepend sanic to the arguments when listeners are triggered
|
|
|
|
listeners = [partial(listener, self) for listener in listeners]
|
|
|
|
server_settings[settings_name] = listeners
|
|
|
|
|
2017-01-28 22:54:15 +00:00
|
|
|
if debug:
|
|
|
|
log.setLevel(logging.DEBUG)
|
2016-12-23 10:42:00 +00:00
|
|
|
if self.config.LOGO is not None:
|
|
|
|
log.debug(self.config.LOGO)
|
2017-01-24 03:58:37 +00:00
|
|
|
|
2017-01-29 22:01:00 +00:00
|
|
|
if run_async:
|
|
|
|
server_settings['run_async'] = True
|
2017-01-24 03:58:37 +00:00
|
|
|
|
|
|
|
# Serve
|
|
|
|
proto = "http"
|
|
|
|
if ssl is not None:
|
|
|
|
proto = "https"
|
2017-01-26 23:11:53 +00:00
|
|
|
log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port))
|
2017-01-24 03:58:37 +00:00
|
|
|
|
2017-01-28 22:54:15 +00:00
|
|
|
return server_settings
|