diff --git a/MANIFEST.in b/MANIFEST.in index 7682c1b6..e52d6670 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,7 @@ include README.rst +include MANIFEST.in +include LICENSE +include setup.py recursive-exclude * __pycache__ recursive-exclude * *.py[co] diff --git a/docs/sanic/config.md b/docs/sanic/config.md index 01ee7fc3..5d0dc95a 100644 --- a/docs/sanic/config.md +++ b/docs/sanic/config.md @@ -85,8 +85,35 @@ DB_USER = 'appuser' Out of the box there are just a few predefined values which can be overwritten when creating the application. - | Variable | Default | Description | - | ----------------- | --------- | --------------------------------- | - | 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 | + | Variable | Default | Description | + | ------------------ | --------- | --------------------------------------------- | + | REQUEST_MAX_SIZE | 100000000 | How big a request may be (bytes) | + | REQUEST_TIMEOUT | 60 | How long a request can take to arrive (sec) | + | RESPONSE_TIMEOUT | 60 | How long a response can take to process (sec) | + | KEEP_ALIVE | True | Disables keep-alive when False | + | KEEP_ALIVE_TIMEOUT | 5 | How long to hold a TCP connection open (sec) | + +### The different Timeout variables: + +A request timeout measures the duration of time between the instant when a new open TCP connection is passed to the Sanic backend server, and the instant when the whole HTTP request is received. If the time taken exceeds the `REQUEST_TIMEOUT` value (in seconds), this is considered a Client Error so Sanic generates a HTTP 408 response and sends that to the client. Adjust this value higher if your clients routinely pass very large request payloads or upload requests very slowly. + +A response timeout measures the duration of time between the instant the Sanic server passes the HTTP request to the Sanic App, and the instant a HTTP response is sent to the client. If the time taken exceeds the `RESPONSE_TIMEOUT` value (in seconds), this is considered a Server Error so Sanic generates a HTTP 503 response and sets that to the client. Adjust this value higher if your application is likely to have long-running process that delay the generation of a response. + +### What is Keep Alive? And what does the Keep Alive Timeout value do? + +Keep-Alive is a HTTP feature indroduced in HTTP 1.1. When sending a HTTP request, the client (usually a web browser application) can set a Keep-Alive header to indicate for the http server (Sanic) to not close the TCP connection after it has send the response. This allows the client to reuse the existing TCP connection to send subsequent HTTP requests, and ensures more efficient network traffic for both the client and the server. + +The `KEEP_ALIVE` config variable is set to `True` in Sanic by default. If you don't need this feature in your application, set it to `False` to cause all client connections to close immediately after a response is sent, regardless of the Keep-Alive header on the request. + +The amount of time the server holds the TCP connection open is decided by the server itself. In Sanic, that value is configured using the `KEEP_ALIVE_TIMEOUT` value. By default, it is set to 5 seconds, this is the same default setting as the Apache HTTP server and is a good balance between allowing enough time for the client to send a new request, and not holding open too many connections at once. Do not exceed 75 seconds unless you know your clients are using a browser which supports TCP connections held open for that long. + +For reference: +``` +Apache httpd server default keepalive timeout = 5 seconds +Nginx server default keepalive timeout = 75 seconds +Nginx performance tuning guidelines uses keepalive = 15 seconds +IE (5-9) client hard keepalive limit = 60 seconds +Firefox client hard keepalive limit = 115 seconds +Opera 11 client hard keepalive limit = 120 seconds +Chrome 13+ client keepalive limit > 300+ seconds +``` diff --git a/docs/sanic/extensions.md b/docs/sanic/extensions.md index ad9b8156..03feb90c 100644 --- a/docs/sanic/extensions.md +++ b/docs/sanic/extensions.md @@ -1,7 +1,7 @@ # Extensions A list of Sanic extensions created by the community. - +- [Sanic-Plugins-Framework](https://github.com/ashleysommer/sanicpluginsframework): Library for easily creating and using Sanic plugins. - [Sessions](https://github.com/subyraman/sanic_session): Support for sessions. Allows using redis, memcache or an in memory store. - [CORS](https://github.com/ashleysommer/sanic-cors): A port of flask-cors. diff --git a/docs/sanic/request_data.md b/docs/sanic/request_data.md index e778faf6..4f6bc970 100644 --- a/docs/sanic/request_data.md +++ b/docs/sanic/request_data.md @@ -75,6 +75,10 @@ The following variables are accessible as properties on `Request` objects: - `ip` (str) - IP address of the requester. +- `port` (str) - Port address of the requester. + +- `socket` (tuple) - (IP, port) of the requester. + - `app` - a reference to the Sanic application object that is handling this request. This is useful when inside blueprints or other handlers in modules that do not have access to the global `app` object. ```python diff --git a/examples/pytest_xdist.py b/examples/pytest_xdist.py new file mode 100644 index 00000000..06730016 --- /dev/null +++ b/examples/pytest_xdist.py @@ -0,0 +1,49 @@ +"""pytest-xdist example for sanic server + +Install testing tools: + + $ pip install pytest pytest-xdist + +Run with xdist params: + + $ pytest examples/pytest_xdist.py -n 8 # 8 workers +""" +import re +from sanic import Sanic +from sanic.response import text +from sanic.testing import PORT as PORT_BASE, SanicTestClient +import pytest + + +@pytest.fixture(scope="session") +def test_port(worker_id): + m = re.search(r'[0-9]+', worker_id) + if m: + num_id = m.group(0) + else: + num_id = 0 + port = PORT_BASE + int(num_id) + return port + + +@pytest.fixture(scope="session") +def app(): + app = Sanic() + + @app.route('/') + async def index(request): + return text('OK') + + return app + + +@pytest.fixture(scope="session") +def client(app, test_port): + return SanicTestClient(app, test_port) + + +@pytest.mark.parametrize('run_id', range(100)) +def test_index(client, run_id): + request, response = client._sanic_endpoint_test('get', '/') + assert response.status == 200 + assert response.text == 'OK' diff --git a/sanic/app.py b/sanic/app.py index 8f1e0b90..05d99c08 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -28,7 +28,8 @@ class Sanic: def __init__(self, name=None, router=None, error_handler=None, load_env=True, request_class=None, - strict_slashes=False, log_config=None): + strict_slashes=False, log_config=None, + configure_logging=True): # Get name from previous stack frame if name is None: @@ -36,7 +37,8 @@ class Sanic: name = getmodulename(frame_records[1]) # logging - logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) + if configure_logging: + logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) self.name = name self.router = router or Router() @@ -47,6 +49,7 @@ class Sanic: self.response_middleware = deque() self.blueprints = {} self._blueprint_order = [] + self.configure_logging = configure_logging self.debug = None self.sock = None self.strict_slashes = strict_slashes @@ -54,7 +57,7 @@ class Sanic: self.is_running = False self.is_request_stream = False self.websocket_enabled = False - self.websocket_tasks = [] + self.websocket_tasks = set() # Register alternative method names self.go_fast = self.run @@ -259,7 +262,7 @@ class Sanic: # its future is kept in self.websocket_tasks in case it # needs to be cancelled due to the server being stopped fut = ensure_future(handler(request, ws, *args, **kwargs)) - self.websocket_tasks.append(fut) + self.websocket_tasks.add(fut) try: await fut except (CancelledError, ConnectionClosed): @@ -345,13 +348,14 @@ class Sanic: # Static Files def static(self, uri, file_or_directory, pattern=r'/?.+', 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): """Register a root to serve files from. The input can either be a file or a directory. See """ static_register(self, uri, file_or_directory, pattern, use_modified_since, use_content_range, - stream_large_files, name, host) + stream_large_files, name, host, strict_slashes) def blueprint(self, blueprint, **options): """Register a blueprint on the application. @@ -574,9 +578,9 @@ class Sanic: try: response = await self._run_response_middleware(request, response) - except: + except BaseException: error_logger.exception( - 'Exception occured in one of response middleware handlers' + 'Exception occurred in one of response middleware handlers' ) # pass the response to the correct callback @@ -642,7 +646,7 @@ class Sanic: serve(**server_settings) else: serve_multiple(server_settings, workers) - except: + except BaseException: error_logger.exception( 'Experienced exception while trying to serve') raise @@ -793,7 +797,7 @@ class Sanic: listeners = [partial(listener, self) for listener in listeners] server_settings[settings_name] = listeners - if debug: + if self.configure_logging and debug: logger.setLevel(logging.DEBUG) if self.config.LOGO is not None: logger.debug(self.config.LOGO) diff --git a/sanic/blueprints.py b/sanic/blueprints.py index e8018326..f9159168 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -221,8 +221,12 @@ class Blueprint: name = kwargs.pop('name', 'static') if not name.startswith(self.name + '.'): name = '{}.{}'.format(self.name, name) - kwargs.update(name=name) + + strict_slashes = kwargs.get('strict_slashes') + if strict_slashes is None and self.strict_slashes is not None: + kwargs.update(strict_slashes=self.strict_slashes) + static = FutureStatic(uri, file_or_directory, args, kwargs) self.statics.append(static) diff --git a/sanic/config.py b/sanic/config.py index 741da019..922a9874 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -33,12 +33,6 @@ class Config(dict): self.REQUEST_TIMEOUT = 60 # 60 seconds self.RESPONSE_TIMEOUT = 60 # 60 seconds self.KEEP_ALIVE = keep_alive - # Apache httpd server default keepalive timeout = 5 seconds - # Nginx server default keepalive timeout = 75 seconds - # Nginx performance tuning guidelines uses keepalive = 15 seconds - # IE client hard keepalive limit = 60 seconds - # Firefox client hard keepalive limit = 115 seconds - self.KEEP_ALIVE_TIMEOUT = 5 # 5 seconds self.WEBSOCKET_MAX_SIZE = 2 ** 20 # 1 megabytes self.WEBSOCKET_MAX_QUEUE = 32 diff --git a/sanic/exceptions.py b/sanic/exceptions.py index e2d808f7..aa1e0d4d 100644 --- a/sanic/exceptions.py +++ b/sanic/exceptions.py @@ -1,4 +1,4 @@ -from sanic.response import ALL_STATUS_CODES, COMMON_STATUS_CODES +from sanic.response import STATUS_CODES TRACEBACK_STYLE = '''