sanic/sanic/config.py

140 lines
4.4 KiB
Python
Raw Normal View History

from os import environ
from typing import Any, Union
2019-01-02 00:35:25 +00:00
# NOTE(tomaszdrozdz): remove in version: 21.3
# We replace from_envvar(), from_object(), from_pyfile() config object methods
# with one simpler update_config() method.
# We also replace "loading module from file code" in from_pyfile()
# in a favour of load_module_from_file_location().
# Please see pull request: 1903
# and issue: 1895
from .deprecated import from_envvar, from_object, from_pyfile # noqa
from .utils import load_module_from_file_location, str_to_bool
2018-10-14 01:55:33 +01:00
SANIC_PREFIX = "SANIC_"
BASE_LOGO = """
Sanic
Build Fast. Run Fast.
"""
2017-03-29 04:57:58 +01:00
DEFAULT_CONFIG = {
"REQUEST_MAX_SIZE": 100000000, # 100 megabytes
"REQUEST_BUFFER_QUEUE_SIZE": 100,
"REQUEST_TIMEOUT": 60, # 60 seconds
"RESPONSE_TIMEOUT": 60, # 60 seconds
"KEEP_ALIVE": True,
"KEEP_ALIVE_TIMEOUT": 5, # 5 seconds
"WEBSOCKET_MAX_SIZE": 2 ** 20, # 1 megabyte
"WEBSOCKET_MAX_QUEUE": 32,
"WEBSOCKET_READ_LIMIT": 2 ** 16,
"WEBSOCKET_WRITE_LIMIT": 2 ** 16,
"WEBSOCKET_PING_TIMEOUT": 20,
"WEBSOCKET_PING_INTERVAL": 20,
"GRACEFUL_SHUTDOWN_TIMEOUT": 15.0, # 15 sec
"ACCESS_LOG": True,
Forwarded headers and otherwise improved proxy handling (#1638) * Added support for HTTP Forwarded header and combined parsing of other proxy headers. - Accessible via request.forwarded that tries parse_forwarded and then parse_xforwarded - parse_forwarded uses the Forwarded header, if config.FORWARDED_SECRET is provided and a matching header field is found - parse_xforwarded uses X-Real-IP and X-Forwarded-* much alike the existing implementation - This commit does not change existing request properties that still use the old code and won't make use of Forwarded headers. * Use req.forwarded in req properties server_name, server_port, scheme and remote_addr. X-Scheme handling moved to parse_xforwarded. * Cleanup and fix req.server_port; no longer reports socket port if any forwards headers are used. * Update docstrings to incidate that forwarded header is used first. * Remove testing function. * Fix tests and linting. - One test removed due to change of semantics - no socket port will be used if any forwarded headers are in effect. - Other tests augmented with X-Forwarded-For, to allow the header being tested take effect (shouldn't affect old implementation). * Try to workaround buggy tools complaining about incorrect ordering of imports. * Cleanup forwarded processing, add comments. secret is now also returned. * Added tests, fixed quoted string handling, cleanup. * Further tests for full coverage. * Try'n make linter happy. * Add support for multiple Forwarded headers. Unify parse_forwarded parameters with parse_xforwarded. * Implement multiple headers support for X-Forwarded-For. - Previously only the first header was used, so this BUGFIX may affect functionality. * Bugfix for request.server_name: strip port and other parts. - request.server_name docs claim that it returns the hostname only (no port). - config.SERVER_NAME may be full URL, so strip scheme, port and path - HTTP Host and consequently forwarded Host may include port number, so strip that also for forwarded hosts (previously only done for HTTP Host). - Possible performance benefit of limiting to one split. * Fallback to app.url_for and let it handle SERVER_NAME if defined (until a proper solution is implemented). * Revise previous commit. Only fallback for full URL SERVER_NAMEs; allows host to be defined and proxied information still being used. * Heil lintnazi. * Modify testcase not to use underscores in URLs. Use hyphens which the spec allows for. * Forwarded and Host header parsing improved. - request.forwarded lowercases hosts, separates host:port into their own fields and lowercases addresses - forwarded.parse_host helper function added and used for parsing all host-style headers (IPv6 cannot be simply split(":")). - more tests fixed not to use underscores in hosts as those are no longer accepted and lead to the field being rejected * Fixed typo in docstring. * Added IPv6 address tests for Host header. * Fix regex. * Further tests and stricter forwarded handling. * Fix merge commit * Linter * Linter * Linter * Add to avoid re-using the variable. Make a few raw strings non-raw. * Remove unnecessary or * Updated docs (work in progress). * Enable REAL_IP_HEADER parsing irregardless of PROXIES_COUNT setting. - Also cleanup and added comments * New defaults for PROXIES_COUNT and REAL_IP_HEADER, updated tests. * Remove support for PROXIES_COUNT=-1. * Linter errors. - This is getting ridiculous: cannot fit an URL on one line, linter requires splitting the string literal! * Add support for by=_proxySecret, updated docs, updated tests. * Forwarded headers' semantics tuning. - Forwarded host is now preserved in original format - request.host now returns a forwarded host if available, else the Host header - Forwarded options are preserved in original order, and later keys override earlier ones - Forwarded path is automatically URL-unquoted - Forwarded 'by' and 'for' are omitted if their value is unknown - Tests modified accordingly - Cleanup and improved documentation * Add ASGI test. * Linter * Linter #2
2019-09-02 14:50:56 +01:00
"FORWARDED_SECRET": None,
"REAL_IP_HEADER": None,
"PROXIES_COUNT": None,
"FORWARDED_FOR_HEADER": "X-Forwarded-For",
"FALLBACK_ERROR_FORMAT": "html",
"REGISTER": True,
}
class Config(dict):
def __init__(self, defaults=None, load_env=True, keep_alive=None):
2018-12-30 21:56:02 +00:00
defaults = defaults or {}
super().__init__({**DEFAULT_CONFIG, **defaults})
self.LOGO = BASE_LOGO
if keep_alive is not None:
self.KEEP_ALIVE = keep_alive
if load_env:
2017-09-05 10:05:31 +01:00
prefix = SANIC_PREFIX if load_env is True else load_env
2017-09-05 09:58:48 +01:00
self.load_environment_vars(prefix=prefix)
def __getattr__(self, attr):
2016-12-16 17:46:07 +00:00
try:
return self[attr]
except KeyError as ke:
raise AttributeError(f"Config has no '{ke.args[0]}'")
def __setattr__(self, attr, value):
self[attr] = value
# NOTE(tomaszdrozdz): remove in version: 21.3
# We replace from_envvar(), from_object(), from_pyfile() config object
# methods with one simpler update_config() method.
# We also replace "loading module from file code" in from_pyfile()
# in a favour of load_module_from_file_location().
# Please see pull request: 1903
# and issue: 1895
from_envvar = from_envvar
from_pyfile = from_pyfile
from_object = from_object
def load_environment_vars(self, prefix=SANIC_PREFIX):
2017-04-17 05:39:18 +01:00
"""
Looks for prefixed environment variables and applies
2017-04-17 05:39:18 +01:00
them to the configuration if present.
"""
for k, v in environ.items():
if k.startswith(prefix):
_, config_key = k.split(prefix, 1)
2017-06-27 05:26:34 +01:00
try:
self[config_key] = int(v)
except ValueError:
try:
2017-06-27 04:58:31 +01:00
self[config_key] = float(v)
2017-06-27 05:26:34 +01:00
except ValueError:
try:
self[config_key] = str_to_bool(v)
except ValueError:
self[config_key] = v
def update_config(self, config: Union[bytes, str, dict, Any]):
"""Update app.config.
Note:: only upper case settings are considered.
You can upload app config by providing path to py file
holding settings.
# /some/py/file
A = 1
B = 2
config.update_config("${some}/py/file")
Yes you can put environment variable here, but they must be provided
in format: ${some_env_var}, and mark that $some_env_var is treated
as plain string.
You can upload app config by providing dict holding settings.
d = {"A": 1, "B": 2}
config.update_config(d)
You can upload app config by providing any object holding settings,
but in such case config.__dict__ will be used as dict holding settings.
class C:
A = 1
B = 2
config.update_config(C)"""
if isinstance(config, (bytes, str)):
config = load_module_from_file_location(location=config)
if not isinstance(config, dict):
config = config.__dict__
config = dict(filter(lambda i: i[0].isupper(), config.items()))
self.update(config)