tigrcorn-config 0.3.16.dev13__tar.gz → 0.3.17.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/PKG-INFO +2 -2
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/pyproject.toml +2 -2
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/governance_surface.py +3 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/__init__.py +16 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/helpers.py +28 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/listeners.py +122 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/mapping.py +60 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/namespace.py +215 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/public_api.py +280 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/public_helpers.py +148 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/sources.py +47 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/__init__.py +32 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/app.py +50 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/http.py +74 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/observability.py +26 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/scheduler.py +11 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/security.py +37 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/server.py +195 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/transports.py +120 -0
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/types.py +9 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/negative_surface.py +2 -2
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/PKG-INFO +2 -2
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/SOURCES.txt +18 -3
- tigrcorn_config-0.3.17.dev1/src/tigrcorn_config.egg-info/requires.txt +4 -0
- tigrcorn_config-0.3.16.dev13/src/tigrcorn_config/load.py +0 -666
- tigrcorn_config-0.3.16.dev13/src/tigrcorn_config/model.py +0 -471
- tigrcorn_config-0.3.16.dev13/src/tigrcorn_config.egg-info/requires.txt +0 -4
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/LICENSE +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/README.md +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/setup.cfg +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/__init__.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/audit.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/defaults.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/env.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/files.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/merge.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/normalize.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/observability_surface.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/origin_surface.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/policy_surface.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/profiles.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/py.typed +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/quic_surface.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/validate.py +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/dependency_links.txt +0 -0
- {tigrcorn_config-0.3.16.dev13 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrcorn-config
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17.dev1
|
|
4
4
|
Summary: Typed configuration models, profiles, environment loading, and validation for the Tigrcorn ASGI/HTTP server stack.
|
|
5
5
|
Author-email: Jacob Stewart <jacob@swarmauri.com>
|
|
6
6
|
License: Apache License
|
|
@@ -186,7 +186,7 @@ Classifier: Typing :: Typed
|
|
|
186
186
|
Requires-Python: <3.15,>=3.10
|
|
187
187
|
Description-Content-Type: text/markdown
|
|
188
188
|
License-File: LICENSE
|
|
189
|
-
Requires-Dist: tigrcorn-core==0.3.
|
|
189
|
+
Requires-Dist: tigrcorn-core==0.3.17.dev1
|
|
190
190
|
Provides-Extra: yaml
|
|
191
191
|
Requires-Dist: PyYAML>=6.0; extra == "yaml"
|
|
192
192
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tigrcorn-config"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.17.dev1"
|
|
8
8
|
description = "Typed configuration models, profiles, environment loading, and validation for the Tigrcorn ASGI/HTTP server stack."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<3.15"
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
29
|
"Typing :: Typed",
|
|
30
30
|
]
|
|
31
|
-
dependencies = ["tigrcorn-core==0.3.
|
|
31
|
+
dependencies = ["tigrcorn-core==0.3.17.dev1"]
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
34
|
yaml = ["PyYAML>=6.0"]
|
|
@@ -223,8 +223,11 @@ APPROVED_LEGACY_UNITTEST_FILES = (
|
|
|
223
223
|
'tests/test_stream_probe_fixtures.py',
|
|
224
224
|
'tests/test_websocket_uix_demo.py',
|
|
225
225
|
'tests/test_webtransport_bidi_stream_context.py',
|
|
226
|
+
'tests/test_webtransport_feature_demo_app.py',
|
|
226
227
|
'tests/test_webtransport_feature_coverage.py',
|
|
227
228
|
'tests/test_webtransport_mtls_demo.py',
|
|
229
|
+
'tests/test_webtransport_mtls_demo_cleanup.py',
|
|
230
|
+
'tests/test_webtransport_mtls_demo_runtime.py',
|
|
228
231
|
'tests/test_webtransport_operator_surface.py',
|
|
229
232
|
'tests/test_aioquic_performance_matrix.py',
|
|
230
233
|
'tests/test_ws_wss_probe_fixture.py',
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .mapping import config_from_mapping, config_from_source, config_to_dict
|
|
4
|
+
from .namespace import namespace_to_overrides
|
|
5
|
+
from .public_api import build_config
|
|
6
|
+
from .sources import build_config_from_namespace, build_config_from_sources
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"build_config",
|
|
10
|
+
"build_config_from_namespace",
|
|
11
|
+
"build_config_from_sources",
|
|
12
|
+
"config_from_mapping",
|
|
13
|
+
"config_from_source",
|
|
14
|
+
"config_to_dict",
|
|
15
|
+
"namespace_to_overrides",
|
|
16
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def listify(value: Any) -> list[Any]:
|
|
7
|
+
if value is None:
|
|
8
|
+
return []
|
|
9
|
+
if isinstance(value, list):
|
|
10
|
+
result: list[Any] = []
|
|
11
|
+
for item in value:
|
|
12
|
+
if isinstance(item, str) and "," in item:
|
|
13
|
+
result.extend(part.strip() for part in item.split(",") if part.strip())
|
|
14
|
+
else:
|
|
15
|
+
result.append(item)
|
|
16
|
+
return result
|
|
17
|
+
if isinstance(value, str):
|
|
18
|
+
return [part.strip() for part in value.split(",") if part.strip()]
|
|
19
|
+
return [value]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def mapping_get(source: Mapping[str, Any], *path: str) -> Any:
|
|
23
|
+
cursor: Any = source
|
|
24
|
+
for segment in path:
|
|
25
|
+
if not isinstance(cursor, Mapping):
|
|
26
|
+
return None
|
|
27
|
+
cursor = cursor.get(segment)
|
|
28
|
+
return cursor
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from argparse import Namespace
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from tigrcorn_core.constants import DEFAULT_HOST, DEFAULT_PORT
|
|
7
|
+
|
|
8
|
+
from .helpers import listify
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_bind(value: str, *, kind: str) -> dict[str, Any]:
|
|
12
|
+
if value.startswith("fd://"):
|
|
13
|
+
return {"kind": kind, "fd": int(value.removeprefix("fd://"))}
|
|
14
|
+
if value.startswith("unix:"):
|
|
15
|
+
return {"kind": "unix", "path": value.split(":", 1)[1]}
|
|
16
|
+
if value.startswith("udp://"):
|
|
17
|
+
kind = "udp"
|
|
18
|
+
value = value.removeprefix("udp://")
|
|
19
|
+
elif value.startswith("tcp://"):
|
|
20
|
+
kind = "tcp"
|
|
21
|
+
value = value.removeprefix("tcp://")
|
|
22
|
+
elif value.startswith("quic://"):
|
|
23
|
+
kind = "udp"
|
|
24
|
+
value = value.removeprefix("quic://")
|
|
25
|
+
if value.startswith("[") and "]:" in value:
|
|
26
|
+
host, port = value.rsplit(":", 1)
|
|
27
|
+
host = host[1:-1]
|
|
28
|
+
elif ":" in value:
|
|
29
|
+
host, port = value.rsplit(":", 1)
|
|
30
|
+
else:
|
|
31
|
+
host, port = DEFAULT_HOST, value
|
|
32
|
+
return {"kind": kind, "host": host, "port": int(port)}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def listener_overrides_from_namespace(ns: Namespace) -> list[dict[str, Any]] | None:
|
|
36
|
+
listeners: list[dict[str, Any]] = []
|
|
37
|
+
for item in list(ns.bind or []):
|
|
38
|
+
listeners.append(parse_bind(item, kind="tcp"))
|
|
39
|
+
for item in list(ns.quic_bind or []):
|
|
40
|
+
listener = parse_bind(item, kind="udp")
|
|
41
|
+
listener["quic_bind"] = item
|
|
42
|
+
listeners.append(listener)
|
|
43
|
+
for item in list(ns.insecure_bind or []):
|
|
44
|
+
listener = parse_bind(item, kind="tcp")
|
|
45
|
+
listener["insecure_bind"] = item
|
|
46
|
+
listeners.append(listener)
|
|
47
|
+
for item in list(ns.fd or []):
|
|
48
|
+
listeners.append({"kind": ns.transport or "tcp", "fd": int(item)})
|
|
49
|
+
for item in list(ns.endpoint or []):
|
|
50
|
+
listeners.append({"kind": ns.transport or "tcp", "endpoint": item})
|
|
51
|
+
|
|
52
|
+
if not listeners:
|
|
53
|
+
if ns.uds:
|
|
54
|
+
kind = "pipe" if ns.transport == "pipe" else "unix"
|
|
55
|
+
listeners.append({"kind": kind, "path": ns.uds})
|
|
56
|
+
else:
|
|
57
|
+
listeners.append({"kind": ns.transport or "tcp", "host": ns.host or DEFAULT_HOST, "port": ns.port or DEFAULT_PORT})
|
|
58
|
+
|
|
59
|
+
for listener in listeners:
|
|
60
|
+
_apply_common_listener_fields(ns, listener)
|
|
61
|
+
return listeners
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _apply_common_listener_fields(ns: Namespace, listener: dict[str, Any]) -> None:
|
|
65
|
+
for attr in ("backlog", "reuse_port", "reuse_address"):
|
|
66
|
+
value = getattr(ns, attr, None)
|
|
67
|
+
if value is not None:
|
|
68
|
+
listener[attr] = value
|
|
69
|
+
if ns.pipe_mode is not None and listener.get("kind") == "pipe":
|
|
70
|
+
listener["pipe_mode"] = ns.pipe_mode
|
|
71
|
+
if ns.user is not None and listener.get("kind") == "unix":
|
|
72
|
+
listener["user"] = ns.user
|
|
73
|
+
if ns.group is not None and listener.get("kind") == "unix":
|
|
74
|
+
listener["group"] = ns.group
|
|
75
|
+
if ns.umask is not None and listener.get("kind") == "unix":
|
|
76
|
+
listener["umask"] = ns.umask
|
|
77
|
+
if ns.http_versions:
|
|
78
|
+
listener["http_versions"] = list(ns.http_versions)
|
|
79
|
+
if ns.protocols:
|
|
80
|
+
listener["protocols"] = list(ns.protocols)
|
|
81
|
+
if ns.disable_websocket is not None:
|
|
82
|
+
listener["websocket"] = not ns.disable_websocket
|
|
83
|
+
_apply_listener_tls_fields(ns, listener)
|
|
84
|
+
_apply_listener_quic_fields(ns, listener)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _apply_listener_tls_fields(ns: Namespace, listener: dict[str, Any]) -> None:
|
|
88
|
+
if listener.get("insecure_bind"):
|
|
89
|
+
return
|
|
90
|
+
mapping = {
|
|
91
|
+
"ssl_certfile": "ssl_certfile",
|
|
92
|
+
"ssl_keyfile": "ssl_keyfile",
|
|
93
|
+
"ssl_keyfile_password": "ssl_keyfile_password",
|
|
94
|
+
"ssl_ca_certs": "ssl_ca_certs",
|
|
95
|
+
"ssl_require_client_cert": "ssl_require_client_cert",
|
|
96
|
+
"ssl_ocsp_mode": "ocsp_mode",
|
|
97
|
+
"ssl_ocsp_soft_fail": "ocsp_soft_fail",
|
|
98
|
+
"ssl_ocsp_cache_size": "ocsp_cache_size",
|
|
99
|
+
"ssl_ocsp_max_age": "ocsp_max_age",
|
|
100
|
+
"ssl_crl_mode": "crl_mode",
|
|
101
|
+
"ssl_crl": "ssl_crl",
|
|
102
|
+
}
|
|
103
|
+
for source, dest in mapping.items():
|
|
104
|
+
value = getattr(ns, source, None)
|
|
105
|
+
if value is not None:
|
|
106
|
+
listener[dest] = value
|
|
107
|
+
if ns.ssl_alpn:
|
|
108
|
+
listener["alpn_protocols"] = listify(ns.ssl_alpn)
|
|
109
|
+
if getattr(ns, "ssl_revocation_fetch", None) is not None:
|
|
110
|
+
value = ns.ssl_revocation_fetch
|
|
111
|
+
listener["revocation_fetch"] = value == "on" if isinstance(value, str) else bool(value)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _apply_listener_quic_fields(ns: Namespace, listener: dict[str, Any]) -> None:
|
|
115
|
+
if listener.get("kind") != "udp":
|
|
116
|
+
return
|
|
117
|
+
if ns.quic_require_retry is not None:
|
|
118
|
+
listener["quic_require_retry"] = ns.quic_require_retry
|
|
119
|
+
if ns.quic_max_datagram_size is not None:
|
|
120
|
+
listener["max_datagram_size"] = ns.quic_max_datagram_size
|
|
121
|
+
if ns.quic_secret is not None:
|
|
122
|
+
listener["quic_secret"] = ns.quic_secret.encode("utf-8") if isinstance(ns.quic_secret, str) else ns.quic_secret
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
from tigrcorn_config.defaults import default_config
|
|
8
|
+
from tigrcorn_config.files import load_config_source
|
|
9
|
+
from tigrcorn_config.merge import merge_config_dicts
|
|
10
|
+
from tigrcorn_config.model import ListenerConfig, ServerConfig
|
|
11
|
+
from tigrcorn_config.normalize import normalize_config
|
|
12
|
+
from tigrcorn_config.profiles import resolve_effective_profile_mapping, resolve_requested_profile
|
|
13
|
+
from tigrcorn_config.validate import validate_config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _dataclass_to_dict(value: Any) -> Any:
|
|
17
|
+
if dataclasses.is_dataclass(value):
|
|
18
|
+
return {field.name: _dataclass_to_dict(getattr(value, field.name)) for field in dataclasses.fields(value)}
|
|
19
|
+
if isinstance(value, list):
|
|
20
|
+
return [_dataclass_to_dict(item) for item in value]
|
|
21
|
+
if isinstance(value, tuple):
|
|
22
|
+
return [_dataclass_to_dict(item) for item in value]
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def config_to_dict(config: ServerConfig) -> dict[str, Any]:
|
|
27
|
+
return _dataclass_to_dict(config)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _apply_mapping(target: Any, data: Mapping[str, Any]) -> None:
|
|
31
|
+
for key, value in data.items():
|
|
32
|
+
if not hasattr(target, key):
|
|
33
|
+
continue
|
|
34
|
+
current = getattr(target, key)
|
|
35
|
+
if isinstance(current, list) and key == "listeners" and isinstance(value, list):
|
|
36
|
+
listeners: list[ListenerConfig] = []
|
|
37
|
+
for entry in value:
|
|
38
|
+
if isinstance(entry, Mapping):
|
|
39
|
+
listener = ListenerConfig()
|
|
40
|
+
_apply_mapping(listener, entry)
|
|
41
|
+
listeners.append(listener)
|
|
42
|
+
setattr(target, key, listeners)
|
|
43
|
+
elif dataclasses.is_dataclass(current) and isinstance(value, Mapping):
|
|
44
|
+
_apply_mapping(current, value)
|
|
45
|
+
else:
|
|
46
|
+
setattr(target, key, value)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def config_from_mapping(data: Mapping[str, Any]) -> ServerConfig:
|
|
50
|
+
profile_name = resolve_requested_profile(data)
|
|
51
|
+
effective_mapping = merge_config_dicts(resolve_effective_profile_mapping(profile_name), data)
|
|
52
|
+
config = default_config()
|
|
53
|
+
_apply_mapping(config, effective_mapping)
|
|
54
|
+
normalize_config(config)
|
|
55
|
+
validate_config(config)
|
|
56
|
+
return config
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def config_from_source(source: str | Path | Mapping[str, Any] | Any | None) -> ServerConfig:
|
|
60
|
+
return config_from_mapping(load_config_source(source))
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from argparse import Namespace
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .helpers import listify
|
|
7
|
+
from .listeners import listener_overrides_from_namespace
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def namespace_to_overrides(ns: Namespace) -> dict[str, Any]:
|
|
11
|
+
overrides: dict[str, Any] = {}
|
|
12
|
+
blocks = {
|
|
13
|
+
"app": {},
|
|
14
|
+
"process": {},
|
|
15
|
+
"tls": {},
|
|
16
|
+
"proxy": {},
|
|
17
|
+
"http": {},
|
|
18
|
+
"websocket": {},
|
|
19
|
+
"static": {},
|
|
20
|
+
"quic": {},
|
|
21
|
+
"webtransport": {},
|
|
22
|
+
"logging": {},
|
|
23
|
+
"metrics": {},
|
|
24
|
+
"scheduler": {},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_populate_app_block(ns, blocks["app"])
|
|
28
|
+
logging_explicit_fields = _populate_mapped_blocks(ns, blocks)
|
|
29
|
+
_populate_special_blocks(ns, blocks, logging_explicit_fields)
|
|
30
|
+
|
|
31
|
+
listeners = listener_overrides_from_namespace(ns)
|
|
32
|
+
if listeners:
|
|
33
|
+
overrides["listeners"] = listeners
|
|
34
|
+
if logging_explicit_fields:
|
|
35
|
+
blocks["logging"]["explicit_fields"] = sorted(set(logging_explicit_fields))
|
|
36
|
+
|
|
37
|
+
for name, block in blocks.items():
|
|
38
|
+
if block:
|
|
39
|
+
overrides[name] = block
|
|
40
|
+
return overrides
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _populate_app_block(ns: Namespace, app_block: dict[str, Any]) -> None:
|
|
44
|
+
if ns.app is not None:
|
|
45
|
+
app_block["target"] = ns.app
|
|
46
|
+
for key, dest in (
|
|
47
|
+
("factory", "factory"),
|
|
48
|
+
("app_interface", "interface"),
|
|
49
|
+
("app_dir", "app_dir"),
|
|
50
|
+
("lifespan", "lifespan"),
|
|
51
|
+
("reload", "reload"),
|
|
52
|
+
("config", "config_file"),
|
|
53
|
+
("env_prefix", "env_prefix"),
|
|
54
|
+
("env_file", "env_file"),
|
|
55
|
+
):
|
|
56
|
+
value = getattr(ns, key, None)
|
|
57
|
+
if value is not None:
|
|
58
|
+
app_block[dest] = value
|
|
59
|
+
if ns.reload_dir:
|
|
60
|
+
app_block["reload_dirs"] = list(ns.reload_dir)
|
|
61
|
+
if ns.reload_include:
|
|
62
|
+
app_block["reload_include"] = list(ns.reload_include)
|
|
63
|
+
if ns.reload_exclude:
|
|
64
|
+
app_block["reload_exclude"] = list(ns.reload_exclude)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _populate_mapped_blocks(ns: Namespace, blocks: dict[str, dict[str, Any]]) -> list[str]:
|
|
68
|
+
logging_explicit_fields: list[str] = []
|
|
69
|
+
for key, (block_name, dest) in _NAMESPACE_MAPPING.items():
|
|
70
|
+
value = getattr(ns, key, None)
|
|
71
|
+
if value is not None:
|
|
72
|
+
block = blocks[block_name]
|
|
73
|
+
block[dest] = value
|
|
74
|
+
if block_name == "logging" and dest in _LOGGING_EXPLICIT_DESTS:
|
|
75
|
+
logging_explicit_fields.append(dest)
|
|
76
|
+
return logging_explicit_fields
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _populate_special_blocks(
|
|
80
|
+
ns: Namespace,
|
|
81
|
+
blocks: dict[str, dict[str, Any]],
|
|
82
|
+
logging_explicit_fields: list[str],
|
|
83
|
+
) -> None:
|
|
84
|
+
if ns.ssl_alpn:
|
|
85
|
+
blocks["tls"]["alpn_protocols"] = listify(ns.ssl_alpn)
|
|
86
|
+
if ns.log_config is not None:
|
|
87
|
+
logging_explicit_fields.append("log_config")
|
|
88
|
+
if getattr(ns, "ssl_revocation_fetch", None) is not None:
|
|
89
|
+
value = ns.ssl_revocation_fetch
|
|
90
|
+
blocks["tls"]["revocation_fetch"] = value == "on" if isinstance(value, str) else bool(value)
|
|
91
|
+
if ns.forwarded_allow_ips:
|
|
92
|
+
blocks["proxy"]["forwarded_allow_ips"] = listify(ns.forwarded_allow_ips)
|
|
93
|
+
if ns.server_header is not None:
|
|
94
|
+
blocks["proxy"]["server_header"] = ns.server_header
|
|
95
|
+
blocks["proxy"]["include_server_header"] = True
|
|
96
|
+
if ns.no_server_header:
|
|
97
|
+
blocks["proxy"]["include_server_header"] = False
|
|
98
|
+
blocks["proxy"]["server_header"] = ""
|
|
99
|
+
if ns.date_header is not None:
|
|
100
|
+
blocks["proxy"]["include_date_header"] = ns.date_header
|
|
101
|
+
if ns.headers:
|
|
102
|
+
blocks["proxy"]["default_headers"] = list(ns.headers)
|
|
103
|
+
if ns.server_name:
|
|
104
|
+
blocks["proxy"]["server_names"] = listify(ns.server_name)
|
|
105
|
+
if ns.http_versions:
|
|
106
|
+
blocks["http"]["http_versions"] = list(ns.http_versions)
|
|
107
|
+
if getattr(ns, "alt_svc", None):
|
|
108
|
+
blocks["http"]["alt_svc_headers"] = listify(ns.alt_svc)
|
|
109
|
+
if ns.content_codings:
|
|
110
|
+
blocks["http"]["content_codings"] = listify(ns.content_codings)
|
|
111
|
+
if getattr(ns, "connect_allow", None):
|
|
112
|
+
blocks["http"]["connect_allow"] = listify(ns.connect_allow)
|
|
113
|
+
if ns.disable_h2c is not None:
|
|
114
|
+
blocks["http"]["enable_h2c"] = not ns.disable_h2c
|
|
115
|
+
if ns.disable_websocket is not None:
|
|
116
|
+
blocks["websocket"]["enabled"] = not ns.disable_websocket
|
|
117
|
+
if ns.quic_secret is not None:
|
|
118
|
+
blocks["quic"]["quic_secret"] = ns.quic_secret.encode("utf-8") if isinstance(ns.quic_secret, str) else ns.quic_secret
|
|
119
|
+
if getattr(ns, "webtransport_origin", None):
|
|
120
|
+
blocks["webtransport"]["origins"] = listify(ns.webtransport_origin)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
_LOGGING_EXPLICIT_DESTS = {
|
|
124
|
+
"level",
|
|
125
|
+
"access_log",
|
|
126
|
+
"access_log_file",
|
|
127
|
+
"access_log_format",
|
|
128
|
+
"error_log_file",
|
|
129
|
+
"structured",
|
|
130
|
+
"use_colors",
|
|
131
|
+
"log_config",
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_NAMESPACE_MAPPING: dict[str, tuple[str, str]] = {
|
|
135
|
+
"workers": ("process", "workers"),
|
|
136
|
+
"worker_class": ("process", "worker_class"),
|
|
137
|
+
"runtime": ("process", "runtime"),
|
|
138
|
+
"pid": ("process", "pid_file"),
|
|
139
|
+
"worker_healthcheck_timeout": ("process", "worker_healthcheck_timeout"),
|
|
140
|
+
"limit_max_requests": ("process", "limit_max_requests"),
|
|
141
|
+
"max_requests_jitter": ("process", "max_requests_jitter"),
|
|
142
|
+
"ssl_certfile": ("tls", "certfile"),
|
|
143
|
+
"ssl_keyfile": ("tls", "keyfile"),
|
|
144
|
+
"ssl_keyfile_password": ("tls", "keyfile_password"),
|
|
145
|
+
"ssl_ca_certs": ("tls", "ca_certs"),
|
|
146
|
+
"ssl_require_client_cert": ("tls", "require_client_cert"),
|
|
147
|
+
"ssl_ciphers": ("tls", "ciphers"),
|
|
148
|
+
"ssl_ocsp_mode": ("tls", "ocsp_mode"),
|
|
149
|
+
"ssl_ocsp_soft_fail": ("tls", "ocsp_soft_fail"),
|
|
150
|
+
"ssl_ocsp_cache_size": ("tls", "ocsp_cache_size"),
|
|
151
|
+
"ssl_ocsp_max_age": ("tls", "ocsp_max_age"),
|
|
152
|
+
"ssl_crl_mode": ("tls", "crl_mode"),
|
|
153
|
+
"ssl_crl": ("tls", "crl"),
|
|
154
|
+
"proxy_headers": ("proxy", "proxy_headers"),
|
|
155
|
+
"root_path": ("proxy", "root_path"),
|
|
156
|
+
"timeout_keep_alive": ("http", "keep_alive_timeout"),
|
|
157
|
+
"read_timeout": ("http", "read_timeout"),
|
|
158
|
+
"write_timeout": ("http", "write_timeout"),
|
|
159
|
+
"timeout_graceful_shutdown": ("http", "shutdown_timeout"),
|
|
160
|
+
"idle_timeout": ("http", "idle_timeout"),
|
|
161
|
+
"max_body_size": ("http", "max_body_size"),
|
|
162
|
+
"max_header_size": ("http", "max_header_size"),
|
|
163
|
+
"http1_max_incomplete_event_size": ("http", "http1_max_incomplete_event_size"),
|
|
164
|
+
"http1_buffer_size": ("http", "http1_buffer_size"),
|
|
165
|
+
"http1_header_read_timeout": ("http", "http1_header_read_timeout"),
|
|
166
|
+
"http1_keep_alive": ("http", "http1_keep_alive"),
|
|
167
|
+
"http2_max_concurrent_streams": ("http", "http2_max_concurrent_streams"),
|
|
168
|
+
"http2_max_headers_size": ("http", "http2_max_headers_size"),
|
|
169
|
+
"http2_max_frame_size": ("http", "http2_max_frame_size"),
|
|
170
|
+
"http2_adaptive_window": ("http", "http2_adaptive_window"),
|
|
171
|
+
"http2_initial_connection_window_size": ("http", "http2_initial_connection_window_size"),
|
|
172
|
+
"http2_initial_stream_window_size": ("http", "http2_initial_stream_window_size"),
|
|
173
|
+
"http2_keep_alive_interval": ("http", "http2_keep_alive_interval"),
|
|
174
|
+
"http2_keep_alive_timeout": ("http", "http2_keep_alive_timeout"),
|
|
175
|
+
"connect_policy": ("http", "connect_policy"),
|
|
176
|
+
"trailer_policy": ("http", "trailer_policy"),
|
|
177
|
+
"content_coding_policy": ("http", "content_coding_policy"),
|
|
178
|
+
"alt_svc_auto": ("http", "alt_svc_auto"),
|
|
179
|
+
"alt_svc_ma": ("http", "alt_svc_max_age"),
|
|
180
|
+
"alt_svc_persist": ("http", "alt_svc_persist"),
|
|
181
|
+
"websocket_max_message_size": ("websocket", "max_message_size"),
|
|
182
|
+
"websocket_max_queue": ("websocket", "max_queue"),
|
|
183
|
+
"websocket_ping_interval": ("websocket", "ping_interval"),
|
|
184
|
+
"websocket_ping_timeout": ("websocket", "ping_timeout"),
|
|
185
|
+
"websocket_compression": ("websocket", "compression"),
|
|
186
|
+
"static_path_route": ("static", "route"),
|
|
187
|
+
"static_path_mount": ("static", "mount"),
|
|
188
|
+
"static_path_dir_to_file": ("static", "dir_to_file"),
|
|
189
|
+
"static_path_index_file": ("static", "index_file"),
|
|
190
|
+
"static_path_expires": ("static", "expires"),
|
|
191
|
+
"quic_require_retry": ("quic", "require_retry"),
|
|
192
|
+
"quic_max_datagram_size": ("quic", "max_datagram_size"),
|
|
193
|
+
"quic_idle_timeout": ("quic", "idle_timeout"),
|
|
194
|
+
"quic_early_data_policy": ("quic", "early_data_policy"),
|
|
195
|
+
"webtransport_max_sessions": ("webtransport", "max_sessions"),
|
|
196
|
+
"webtransport_max_streams": ("webtransport", "max_streams"),
|
|
197
|
+
"webtransport_max_datagram_size": ("webtransport", "max_datagram_size"),
|
|
198
|
+
"webtransport_path": ("webtransport", "path"),
|
|
199
|
+
"log_level": ("logging", "level"),
|
|
200
|
+
"access_log": ("logging", "access_log"),
|
|
201
|
+
"access_log_file": ("logging", "access_log_file"),
|
|
202
|
+
"access_log_format": ("logging", "access_log_format"),
|
|
203
|
+
"error_log_file": ("logging", "error_log_file"),
|
|
204
|
+
"log_config": ("logging", "log_config"),
|
|
205
|
+
"structured_log": ("logging", "structured"),
|
|
206
|
+
"use_colors": ("logging", "use_colors"),
|
|
207
|
+
"metrics": ("metrics", "enabled"),
|
|
208
|
+
"metrics_bind": ("metrics", "bind"),
|
|
209
|
+
"statsd_host": ("metrics", "statsd_host"),
|
|
210
|
+
"otel_endpoint": ("metrics", "otel_endpoint"),
|
|
211
|
+
"limit_concurrency": ("scheduler", "limit_concurrency"),
|
|
212
|
+
"max_connections": ("scheduler", "max_connections"),
|
|
213
|
+
"max_tasks": ("scheduler", "max_tasks"),
|
|
214
|
+
"max_streams": ("scheduler", "max_streams"),
|
|
215
|
+
}
|