diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..367956b6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing + +Thank you for your interest! Sanic is always looking for contributors. If you +don't feel comfortable contributing code, adding docstrings to the source files +is very appreciated. + +## Installation + +To develop on sanic (and mainly to just run the tests) it is highly recommend to +install from sources. + +So assume you have already cloned the repo and are in the working directory with +a virtual environment already set up, then run: + +```bash +python setup.py develop && pip install -r requirements-dev.txt +``` + +## Running tests + +To run the tests for sanic it is recommended to use tox like so: + +```bash +tox +``` + +See it's that simple! + +## Pull requests! + +So the pull request approval rules are pretty simple: +1. All pull requests must pass unit tests +2. All pull requests must be reviewed and approved by at least +one current collaborator on the project +3. All pull requests must pass flake8 checks +4. If you decide to remove/change anything from any common interface +a deprecation message should accompany it. +5. If you implement a new feature you should have at least one unit +test to accompany it. + +## Documentation + +Sanic's documentation is built +using [sphinx](http://www.sphinx-doc.org/en/1.5.1/). Guides are written in +Markdown and can be found in the `docs` folder, while the module reference is +automatically generated using `sphinx-apidoc`. + +To generate the documentation from scratch: + +```bash +sphinx-apidoc -fo docs/_api/ sanic +sphinx-build -b html docs docs/_build +``` + +The HTML documentation will be created in the `docs/_build` folder. + +## Warning + +One of the main goals of Sanic is speed. Code that lowers the performance of +Sanic without significant gains in usability, security, or features may not be +merged. Please don't let this intimidate you! If you have any concerns about an +idea, open an issue for discussion and help. diff --git a/docs/sanic/config.md b/docs/sanic/config.md index 3ed40fda..a93d2cf1 100644 --- a/docs/sanic/config.md +++ b/docs/sanic/config.md @@ -83,3 +83,4 @@ Out of the box there are just a few predefined values which can be overwritten w | ----------------- | --------- | --------------------------------- | | REQUEST_MAX_SIZE | 100000000 | How big a request may be (bytes) | | REQUEST_TIMEOUT | 60 | How long a request can take (sec) | + | KEEP_ALIVE | True | Disables keep-alive when False | diff --git a/docs/sanic/contributing.md b/docs/sanic/contributing.md index dbc64a02..95922693 100644 --- a/docs/sanic/contributing.md +++ b/docs/sanic/contributing.md @@ -4,10 +4,39 @@ Thank you for your interest! Sanic is always looking for contributors. If you don't feel comfortable contributing code, adding docstrings to the source files is very appreciated. +## Installation + +To develop on sanic (and mainly to just run the tests) it is highly recommend to +install from sources. + +So assume you have already cloned the repo and are in the working directory with +a virtual environment already set up, then run: + +```bash +python setup.py develop && pip install -r requirements-dev.txt +``` + ## Running tests -* `python -m pip install pytest` -* `python -m pytest tests` +To run the tests for sanic it is recommended to use tox like so: + +```bash +tox +``` + +See it's that simple! + +## Pull requests! + +So the pull request approval rules are pretty simple: +1. All pull requests must pass unit tests +* All pull requests must be reviewed and approved by at least +one current collaborator on the project +* All pull requests must pass flake8 checks +* If you decide to remove/change anything from any common interface +a deprecation message should accompany it. +* If you implement a new feature you should have at least one unit +test to accompany it. ## Documentation diff --git a/examples/dask_distributed.py b/examples/dask_distributed.py new file mode 100644 index 00000000..ef3fe423 --- /dev/null +++ b/examples/dask_distributed.py @@ -0,0 +1,41 @@ +from sanic import Sanic +from sanic import response + +from tornado.platform.asyncio import BaseAsyncIOLoop, to_asyncio_future +from distributed import LocalCluster, Client + + +app = Sanic(__name__) + + +def square(x): + return x**2 + + +@app.listener('after_server_start') +async def setup(app, loop): + # configure tornado use asyncio's loop + ioloop = BaseAsyncIOLoop(loop) + + # init distributed client + app.client = Client('tcp://localhost:8786', loop=ioloop, start=False) + await to_asyncio_future(app.client._start()) + + +@app.listener('before_server_stop') +async def stop(app, loop): + await to_asyncio_future(app.client._shutdown()) + + +@app.route('/') +async def test(request, value): + future = app.client.submit(square, value) + result = await to_asyncio_future(future._result()) + return response.text(f'The square of {value} is {result}') + + +if __name__ == '__main__': + # Distributed cluster should run somewhere else + with LocalCluster(scheduler_port=8786, nanny=False, n_workers=2, + threads_per_worker=1) as cluster: + app.run(host="0.0.0.0", port=8000) diff --git a/examples/exception_monitoring.py b/examples/exception_monitoring.py index 37d5c89d..76d16d90 100644 --- a/examples/exception_monitoring.py +++ b/examples/exception_monitoring.py @@ -1,9 +1,7 @@ """ Example intercepting uncaught exceptions using Sanic's error handler framework. - This may be useful for developers wishing to use Sentry, Airbrake, etc. or a custom system to log and monitor unexpected errors in production. - First we create our own class inheriting from Handler in sanic.exceptions, and pass in an instance of it when we create our Sanic instance. Inside this class' default handler, we can do anything including sending exceptions to @@ -39,7 +37,7 @@ server's error_handler to an instance of our CustomHandler """ from sanic import Sanic -from sanic.response import json +from sanic import response app = Sanic(__name__) @@ -52,7 +50,7 @@ async def test(request): # Here, something occurs which causes an unexpected exception # This exception will flow to our custom handler. 1 / 0 - return json({"test": True}) + return response.json({"test": True}) -app.run(host="0.0.0.0", port=8000, debug=True) +app.run(host="0.0.0.0", port=8000, debug=True) \ No newline at end of file diff --git a/examples/jinja_example.py b/examples/jinja_example.py index 1f9bb1ba..cf517030 100644 --- a/examples/jinja_example.py +++ b/examples/jinja_example.py @@ -1,18 +1,27 @@ -## To use this example: -# curl -d '{"name": "John Doe"}' localhost:8000 +# Render templates in a Flask like way from a "template" directory in the project from sanic import Sanic -from sanic.response import html -from jinja2 import Template - -template = Template('Hello {{ name }}!') +from sanic import response +from jinja2 import Evironment, PackageLoader, select_autoescape app = Sanic(__name__) +# Load the template environment with async support +template_env = Environment( + loader=jinja2.PackageLoader('yourapplication', 'templates'), + autoescape=jinja2.select_autoescape(['html', 'xml']), + enable_async=True +) + +# Load the template from file +template = template_env.get_template("example_template.html") + + @app.route('/') async def test(request): data = request.json - return html(template.render(**data)) + rendered_template = await template.render_async(**data) + return response.html(rendered_template) -app.run(host="0.0.0.0", port=8000) +app.run(host="0.0.0.0", port=8080, debug=True) \ No newline at end of file diff --git a/examples/modify_header_example.py b/examples/modify_header_example.py new file mode 100644 index 00000000..bb5efe8e --- /dev/null +++ b/examples/modify_header_example.py @@ -0,0 +1,26 @@ +""" +Modify header or status in response +""" + +from sanic import Sanic +from sanic import response + +app = Sanic(__name__) + +@app.route('/') +def handle_request(request): + return response.json( + {'message': 'Hello world!'}, + headers={'X-Served-By': 'sanic'}, + status=200 + ) + +@app.route('/unauthorized') +def handle_request(request): + return response.json( + {'message': 'You are not authorized'}, + headers={'X-Served-By': 'sanic'}, + status=404 + ) + +app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/override_logging.py b/examples/override_logging.py index 117d63bf..e4d529e8 100644 --- a/examples/override_logging.py +++ b/examples/override_logging.py @@ -1,6 +1,5 @@ from sanic import Sanic -from sanic.response import text -import json +from sanic import response import logging logging_format = "[%(asctime)s] %(process)d-%(levelname)s " @@ -18,6 +17,6 @@ sanic = Sanic() @sanic.route("/") def test(request): log.info("received request; responding with 'hey'") - return text("hey") + return response.text("hey") sanic.run(host="0.0.0.0", port=8000) diff --git a/examples/plotly_example/plotlyjs_example.py b/examples/plotly_example/plotlyjs_example.py new file mode 100644 index 00000000..f536067c --- /dev/null +++ b/examples/plotly_example/plotlyjs_example.py @@ -0,0 +1,85 @@ +from sanic import Sanic + +from sanic_session import InMemorySessionInterface +from sanic_jinja2 import SanicJinja2 + +import json +import plotly + +import pandas as pd +import numpy as np + +app = Sanic(__name__) + +jinja = SanicJinja2(app) +session = InMemorySessionInterface(cookie_name=app.name, prefix=app.name) + +@app.middleware('request') +async def print_on_request(request): + print(request.headers) + await session.open(request) + +@app.middleware('response') +async def print_on_response(request, response): + await session.save(request, response) + + + +@app.route('/') +async def index(request): + rng = pd.date_range('1/1/2011', periods=7500, freq='H') + ts = pd.Series(np.random.randn(len(rng)), index=rng) + + graphs = [ + dict( + data=[ + dict( + x=[1, 2, 3], + y=[10, 20, 30], + type='scatter' + ), + ], + layout=dict( + title='first graph' + ) + ), + + dict( + data=[ + dict( + x=[1, 3, 5], + y=[10, 50, 30], + type='bar' + ), + ], + layout=dict( + title='second graph' + ) + ), + + dict( + data=[ + dict( + x=ts.index, # Can use the pandas data structures directly + y=ts + ) + ] + ) + ] + + # Add "ids" to each of the graphs to pass up to the client + # for templating + ids = ['graph-{}'.format(i) for i, _ in enumerate(graphs)] + + # Convert the figures to JSON + # PlotlyJSONEncoder appropriately converts pandas, datetime, etc + # objects to their JSON equivalents + graphJSON = json.dumps(graphs, cls=plotly.utils.PlotlyJSONEncoder) + + return jinja.render('index.html', request, + ids=ids, + graphJSON=graphJSON) + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000, debug=True) \ No newline at end of file diff --git a/examples/plotly_example/requirements.txt b/examples/plotly_example/requirements.txt new file mode 100644 index 00000000..91875907 --- /dev/null +++ b/examples/plotly_example/requirements.txt @@ -0,0 +1,5 @@ +pandas==0.19.2 +plotly==2.0.7 +sanic==0.5.0 +sanic-jinja2==0.5.1 +sanic-session==0.1.3 \ No newline at end of file diff --git a/examples/plotly_example/templates/index.html b/examples/plotly_example/templates/index.html new file mode 100644 index 00000000..e69de29b diff --git a/examples/request_timeout.py b/examples/request_timeout.py index 261f423a..fb2822ee 100644 --- a/examples/request_timeout.py +++ b/examples/request_timeout.py @@ -1,6 +1,6 @@ -from sanic import Sanic import asyncio -from sanic.response import text +from sanic import Sanic +from sanic import response from sanic.config import Config from sanic.exceptions import RequestTimeout @@ -11,11 +11,11 @@ app = Sanic(__name__) @app.route('/') async def test(request): await asyncio.sleep(3) - return text('Hello, world!') + return response.text('Hello, world!') @app.exception(RequestTimeout) def timeout(request, exception): - return text('RequestTimeout from error_handler.', 408) + return response.text('RequestTimeout from error_handler.', 408) -app.run(host='0.0.0.0', port=8000) +app.run(host='0.0.0.0', port=8000) \ No newline at end of file diff --git a/examples/sanic_motor.py b/examples/sanic_motor.py index 495875a4..c7d2b60f 100644 --- a/examples/sanic_motor.py +++ b/examples/sanic_motor.py @@ -5,7 +5,7 @@ motor==1.1 sanic==0.2.0 """ from sanic import Sanic -from sanic.response import json +from sanic import response app = Sanic('motor_mongodb') @@ -25,7 +25,7 @@ async def get(request): for doc in docs: doc['id'] = str(doc['_id']) del doc['_id'] - return json(docs) + return response.json(docs) @app.route('/post', methods=['POST']) @@ -34,8 +34,8 @@ async def new(request): print(doc) db = get_db() object_id = await db.test_col.save(doc) - return json({'object_id': str(object_id)}) + return response.json({'object_id': str(object_id)}) if __name__ == "__main__": - app.run(host='127.0.0.1', port=8000) + app.run(host='0.0.0.0', port=8000, debug=True) diff --git a/examples/simple_server.py b/examples/simple_server.py index a803feb8..948090c4 100644 --- a/examples/simple_server.py +++ b/examples/simple_server.py @@ -1,12 +1,12 @@ from sanic import Sanic -from sanic.response import json +from sanic import response app = Sanic(__name__) @app.route("/") async def test(request): - return json({"test": True}) + return response.json({"test": True}) if __name__ == '__main__': diff --git a/examples/try_everything.py b/examples/try_everything.py index da3cc515..d46b832e 100644 --- a/examples/try_everything.py +++ b/examples/try_everything.py @@ -2,7 +2,7 @@ import os from sanic import Sanic from sanic.log import log -from sanic.response import json, text, file +from sanic import response from sanic.exceptions import ServerError app = Sanic(__name__) @@ -10,17 +10,17 @@ app = Sanic(__name__) @app.route("/") async def test_async(request): - return json({"test": True}) + return response.json({"test": True}) @app.route("/sync", methods=['GET', 'POST']) def test_sync(request): - return json({"test": True}) + return response.json({"test": True}) @app.route("/dynamic//") def test_params(request, name, id): - return text("yeehaww {} {}".format(name, id)) + return response.text("yeehaww {} {}".format(name, id)) @app.route("/exception") @@ -31,11 +31,11 @@ def exception(request): async def test_await(request): import asyncio await asyncio.sleep(5) - return text("I'm feeling sleepy") + return response.text("I'm feeling sleepy") @app.route("/file") async def test_file(request): - return await file(os.path.abspath("setup.py")) + return await response.file(os.path.abspath("setup.py")) # ----------------------------------------------- # @@ -44,7 +44,7 @@ async def test_file(request): @app.exception(ServerError) async def test(request, exception): - return json({"exception": "{}".format(exception), "status": exception.status_code}, status=exception.status_code) + return response.json({"exception": "{}".format(exception), "status": exception.status_code}, status=exception.status_code) # ----------------------------------------------- # @@ -53,17 +53,17 @@ async def test(request, exception): @app.route("/json") def post_json(request): - return json({"received": True, "message": request.json}) + return response.json({"received": True, "message": request.json}) @app.route("/form") def post_json(request): - return json({"received": True, "form_data": request.form, "test": request.form.get('test')}) + return response.json({"received": True, "form_data": request.form, "test": request.form.get('test')}) @app.route("/query_string") def query_string(request): - return json({"parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string}) + return response.json({"parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string}) # ----------------------------------------------- # diff --git a/examples/url_for_example.py b/examples/url_for_example.py new file mode 100644 index 00000000..c26debf4 --- /dev/null +++ b/examples/url_for_example.py @@ -0,0 +1,18 @@ +from sanic import Sanic +from sanic import response + +app = Sanic(__name__) + +@app.route('/') +async def index(request): + # generate a URL for the endpoint `post_handler` + url = app.url_for('post_handler', post_id=5) + # the URL is `/posts/5`, redirect to it + return response.redirect(url) + +@app.route('/posts/') +async def post_handler(request, post_id): + return response.text('Post - {}'.format(post_id)) + +if __name__ == '__main__': + app.run(host="0.0.0.0", port=8000, debug=True) \ No newline at end of file diff --git a/examples/vhosts.py b/examples/vhosts.py index 810dc513..50c9b6f6 100644 --- a/examples/vhosts.py +++ b/examples/vhosts.py @@ -1,4 +1,4 @@ -from sanic.response import text +from sanic import response from sanic import Sanic from sanic.blueprints import Blueprint @@ -15,25 +15,25 @@ bp = Blueprint("bp", host="bp.example.com") "somethingelse.com", "therestofyourdomains.com"]) async def hello(request): - return text("Some defaults") + return response.text("Some defaults") @app.route('/', host="example.com") async def hello(request): - return text("Answer") + return response.text("Answer") @app.route('/', host="sub.example.com") async def hello(request): - return text("42") + return response.text("42") @bp.route("/question") async def hello(request): - return text("What is the meaning of life?") + return response.text("What is the meaning of life?") @bp.route("/answer") async def hello(request): - return text("42") + return response.text("42") app.register_blueprint(bp) if __name__ == '__main__': - app.run(host="0.0.0.0", port=8000) + app.run(host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/sanic/app.py b/sanic/app.py index c19d1427..c0505ab0 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -17,7 +17,7 @@ from sanic.handlers import ErrorHandler from sanic.log import log from sanic.response import HTTPResponse, StreamingHTTPResponse from sanic.router import Router -from sanic.server import serve, serve_multiple, HttpProtocol +from sanic.server import serve, serve_multiple, HttpProtocol, Signal from sanic.static import register as static_register from sanic.testing import SanicTestClient from sanic.views import CompositionView @@ -293,7 +293,7 @@ class Sanic: attach_to=middleware_or_request) # Static Files - def static(self, uri, file_or_directory, pattern='.+', + def static(self, uri, file_or_directory, pattern=r'/?.+', use_modified_since=True, use_content_range=False): """Register a root to serve files from. The input can either be a file or a directory. See @@ -687,11 +687,13 @@ class Sanic: 'port': port, 'sock': sock, 'ssl': ssl, + 'signal': Signal(), '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, + 'keep_alive': self.config.KEEP_ALIVE, 'loop': loop, 'register_sys_signals': register_sys_signals, 'backlog': backlog, diff --git a/sanic/config.py b/sanic/config.py index a1c140c1..c12037fe 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -116,7 +116,7 @@ if type(_addr) is str and not os.path.exists(_addr): class Config(dict): - def __init__(self, defaults=None, load_env=True): + def __init__(self, defaults=None, load_env=True, keep_alive=True): super().__init__(defaults or {}) self.LOGO = """ ▄▄▄▄▄ @@ -141,6 +141,7 @@ class Config(dict): """ self.REQUEST_MAX_SIZE = 100000000 # 100 megababies self.REQUEST_TIMEOUT = 60 # 60 seconds + self.KEEP_ALIVE = keep_alive if load_env: self.load_environment_vars() @@ -208,11 +209,11 @@ class Config(dict): self[key] = getattr(obj, key) def load_environment_vars(self): + """ + Looks for any SANIC_ prefixed environment variables and applies + them to the configuration if present. + """ for k, v in os.environ.items(): - """ - Looks for any SANIC_ prefixed environment variables and applies - them to the configuration if present. - """ if k.startswith(SANIC_PREFIX): _, config_key = k.split(SANIC_PREFIX, 1) self[config_key] = v diff --git a/sanic/request.py b/sanic/request.py index 4a15c22f..31b6a08f 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -78,9 +78,10 @@ class Request(dict): :return: token related to request """ auth_header = self.headers.get('Authorization') - if auth_header is not None: - return auth_header.split()[1] - return auth_header + if 'Token ' in auth_header: + return auth_header.partition('Token ')[-1] + else: + return auth_header @property def form(self): diff --git a/sanic/response.py b/sanic/response.py index bebb8071..0fc3c575 100644 --- a/sanic/response.py +++ b/sanic/response.py @@ -210,7 +210,7 @@ class HTTPResponse(BaseHTTPResponse): # Speeds up response rate 6% over pulling from all status = COMMON_STATUS_CODES.get(self.status) if not status: - status = ALL_STATUS_CODES.get(self.status) + status = ALL_STATUS_CODES.get(self.status, b'UNKNOWN RESPONSE') return (b'HTTP/%b %d %b\r\n' b'Connection: %b\r\n' diff --git a/sanic/router.py b/sanic/router.py index f7877f15..39872469 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -16,6 +16,7 @@ REGEX_TYPES = { 'int': (int, r'\d+'), 'number': (float, r'[0-9\\.]+'), 'alpha': (str, r'[A-Za-z]+'), + 'path': (str, r'[^/].*?'), } ROUTER_CACHE_SIZE = 1024 @@ -71,7 +72,8 @@ class Router: self.routes_always_check = [] self.hosts = set() - def parse_parameter_string(self, parameter_string): + @classmethod + def parse_parameter_string(cls, parameter_string): """Parse a parameter string into its constituent name, type, and pattern @@ -161,10 +163,10 @@ class Router: parameters.append(parameter) # Mark the whole route as unhashable if it has the hash key in it - if re.search('(^|[^^]){1}/', pattern): + if re.search(r'(^|[^^]){1}/', pattern): properties['unhashable'] = True # Mark the route as unhashable if it matches the hash key - elif re.search(pattern, '/'): + elif re.search(r'/', pattern): properties['unhashable'] = True return '({})'.format(pattern) diff --git a/sanic/server.py b/sanic/server.py index 40f36e2b..d0d13037 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -73,7 +73,8 @@ class HttpProtocol(asyncio.Protocol): def __init__(self, *, loop, request_handler, error_handler, signal=Signal(), connections=set(), request_timeout=60, - request_max_size=None, request_class=None, has_log=True): + request_max_size=None, request_class=None, has_log=True, + keep_alive=True): self.loop = loop self.transport = None self.request = None @@ -92,10 +93,13 @@ class HttpProtocol(asyncio.Protocol): self._timeout_handler = None self._last_request_time = None self._request_handler_task = None + self._keep_alive = keep_alive @property def keep_alive(self): - return self.parser.should_keep_alive() and not self.signal.stopped + return (self._keep_alive + and not self.signal.stopped + and self.parser.should_keep_alive()) # -------------------------------------------- # # Connection @@ -357,7 +361,8 @@ def serve(host, port, request_handler, error_handler, before_start=None, request_timeout=60, ssl=None, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_async=False, connections=None, - signal=Signal(), request_class=None, has_log=True): + signal=Signal(), request_class=None, has_log=True, keep_alive=True): + signal=Signal(), request_class=None, keep_alive=True): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on @@ -406,7 +411,8 @@ def serve(host, port, request_handler, error_handler, before_start=None, request_timeout=request_timeout, request_max_size=request_max_size, request_class=request_class, - has_log=has_log + has_log=has_log, + keep_alive=keep_alive, ) server_coroutine = loop.create_server( diff --git a/sanic/static.py b/sanic/static.py index adbdd0ea..64166d46 100644 --- a/sanic/static.py +++ b/sanic/static.py @@ -48,14 +48,18 @@ def register(app, uri, file_or_directory, pattern, # Merge served directory and requested file if provided # Strip all / that in the beginning of the URL to help prevent python # from herping a derp and treating the uri as an absolute path - file_path = file_or_directory + root_path = file_path = file_or_directory if file_uri: file_path = path.join( file_or_directory, sub('^[/]*', '', file_uri)) # URL decode the path sent by the browser otherwise we won't be able to # match filenames which got encoded (filenames with spaces etc) - file_path = unquote(file_path) + file_path = path.abspath(unquote(file_path)) + if not file_path.startswith(path.abspath(unquote(root_path))): + raise FileNotFound('File not found', + path=file_or_directory, + relative_url=file_uri) try: headers = {} # Check if the client has been sent this file before diff --git a/sanic/worker.py b/sanic/worker.py index 7a8303d8..b2f5af17 100644 --- a/sanic/worker.py +++ b/sanic/worker.py @@ -3,6 +3,7 @@ import sys import signal import asyncio import logging + try: import ssl except ImportError: @@ -50,8 +51,8 @@ class GunicornWorker(base.Worker): debug=is_debug, protocol=protocol, ssl=self.ssl_context, - run_async=True - ) + run_async=True) + self._server_settings['signal'] = self.signal self._server_settings.pop('sock') trigger_events(self._server_settings.get('before_start', []), self.loop) @@ -97,7 +98,6 @@ class GunicornWorker(base.Worker): self.servers.append(await serve( sock=sock, connections=self.connections, - signal=self.signal, **self._server_settings )) diff --git a/tests/test_requests.py b/tests/test_requests.py index 7b453fc1..c0824850 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -141,6 +141,16 @@ def test_token(): return text('OK') # uuid4 generated token. + token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' + headers = { + 'content-type': 'application/json', + 'Authorization': '{}'.format(token) + } + + request, response = app.test_client.get('/', headers=headers) + + assert request.token == token + token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' headers = { 'content-type': 'application/json', @@ -151,6 +161,18 @@ def test_token(): assert request.token == token + token = 'a1d895e0-553a-421a-8e22-5ff8ecb48cbf' + headers = { + 'content-type': 'application/json', + 'Authorization': 'Bearer Token {}'.format(token) + } + + request, response = app.test_client.get('/', headers=headers) + + assert request.token == token + + + # ------------------------------------------------------------ # # POST # ------------------------------------------------------------ # diff --git a/tests/test_routes.py b/tests/test_routes.py index 3506db66..b3e19355 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -238,6 +238,30 @@ def test_dynamic_route_regex(): assert response.status == 200 +def test_dynamic_route_path(): + app = Sanic('test_dynamic_route_path') + + @app.route('//info') + async def handler(request, path): + return text('OK') + + request, response = app.test_client.get('/path/1/info') + assert response.status == 200 + + request, response = app.test_client.get('/info') + assert response.status == 404 + + @app.route('/') + async def handler1(request, path): + return text('OK') + + request, response = app.test_client.get('/info') + assert response.status == 200 + + request, response = app.test_client.get('/whatever/you/set') + assert response.status == 200 + + def test_dynamic_route_unhashable(): app = Sanic('test_dynamic_route_unhashable')