tigrcorn-runtime 0.3.18.dev2__tar.gz → 0.3.18.dev3__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.
Files changed (44) hide show
  1. {tigrcorn_runtime-0.3.18.dev2/src/tigrcorn_runtime.egg-info → tigrcorn_runtime-0.3.18.dev3}/PKG-INFO +10 -8
  2. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/README.md +1 -1
  3. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/pyproject.toml +19 -7
  4. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/cli.py +17 -0
  5. tigrcorn_runtime-0.3.18.dev3/src/tigrcorn_runtime/quic_cc.py +42 -0
  6. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/base.py +20 -0
  7. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/client_handlers.py +32 -0
  8. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/diagnostics.py +54 -0
  9. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/http11.py +36 -1
  10. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/http11_serve.py +6 -0
  11. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/imports.py +2 -0
  12. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3/src/tigrcorn_runtime.egg-info}/PKG-INFO +10 -8
  13. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime.egg-info/SOURCES.txt +1 -0
  14. tigrcorn_runtime-0.3.18.dev3/src/tigrcorn_runtime.egg-info/requires.txt +16 -0
  15. tigrcorn_runtime-0.3.18.dev2/src/tigrcorn_runtime.egg-info/requires.txt +0 -14
  16. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/LICENSE +0 -0
  17. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/setup.cfg +0 -0
  18. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/__init__.py +0 -0
  19. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/api.py +0 -0
  20. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/app_interfaces.py +0 -0
  21. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/embedded.py +0 -0
  22. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/py.typed +0 -0
  23. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/__init__.py +0 -0
  24. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/app_loader.py +0 -0
  25. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/bootstrap.py +0 -0
  26. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/hooks.py +0 -0
  27. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/reloader.py +0 -0
  28. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/__init__.py +0 -0
  29. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/core.py +0 -0
  30. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/http11_send.py +0 -0
  31. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/lifecycle.py +0 -0
  32. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/listeners.py +0 -0
  33. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/runner/metrics.py +0 -0
  34. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/shutdown.py +0 -0
  35. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/signals.py +0 -0
  36. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/state.py +0 -0
  37. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/server/supervisor.py +0 -0
  38. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/workers/__init__.py +0 -0
  39. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/workers/local.py +0 -0
  40. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/workers/model.py +0 -0
  41. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/workers/process.py +0 -0
  42. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime/workers/supervisor.py +0 -0
  43. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime.egg-info/dependency_links.txt +0 -0
  44. {tigrcorn_runtime-0.3.18.dev2 → tigrcorn_runtime-0.3.18.dev3}/src/tigrcorn_runtime.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrcorn-runtime
3
- Version: 0.3.18.dev2
3
+ Version: 0.3.18.dev3
4
4
  Summary: Server runner, app loading, lifecycle hooks, workers, reload, and embedded runtime for the Tigrcorn ASGI3 web server.
5
5
  Author-email: Jacob Stewart <jacob@swarmauri.com>
6
6
  License: Apache License
@@ -187,12 +187,14 @@ Classifier: Typing :: Typed
187
187
  Requires-Python: <3.15,>=3.10
188
188
  Description-Content-Type: text/markdown
189
189
  License-File: LICENSE
190
- Requires-Dist: tigrcorn-core==0.3.18.dev2
191
- Requires-Dist: tigrcorn-config==0.3.18.dev2
192
- Requires-Dist: tigrcorn-asgi==0.3.18.dev2
193
- Requires-Dist: tigrcorn-transports==0.3.18.dev2
194
- Requires-Dist: tigrcorn-protocols==0.3.18.dev2
195
- Requires-Dist: tigrcorn-security==0.3.18.dev2
190
+ Requires-Dist: tigrcorn-core==0.3.18.dev3
191
+ Requires-Dist: tigrcorn-config==0.3.18.dev3
192
+ Requires-Dist: tigrcorn-asgi==0.3.18.dev3
193
+ Requires-Dist: tigrcorn-transports==0.3.18.dev3
194
+ Requires-Dist: tigrcorn-protocols==0.3.18.dev3
195
+ Requires-Dist: tigrcorn-security==0.3.18.dev3
196
+ Requires-Dist: tigrcorn-quic-cc==0.3.18.dev3
197
+ Requires-Dist: tigrcorn-quic-cc-reno==0.3.18.dev3
196
198
  Provides-Extra: uvloop
197
199
  Requires-Dist: uvloop>=0.19.0; platform_system != "Windows" and extra == "uvloop"
198
200
  Provides-Extra: trio
@@ -293,7 +295,7 @@ The package exposes its supported public surface through the <code>tigrcorn_runt
293
295
 
294
296
  ## Package Graph
295
297
 
296
- [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
298
+ [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-quic-cc](https://pypi.org/project/tigrcorn-quic-cc/) | [tigrcorn-quic-cc-reno](https://pypi.org/project/tigrcorn-quic-cc-reno/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
297
299
 
298
300
  ## Best Practices
299
301
 
@@ -92,7 +92,7 @@ The package exposes its supported public surface through the <code>tigrcorn_runt
92
92
 
93
93
  ## Package Graph
94
94
 
95
- [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
95
+ [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-quic-cc](https://pypi.org/project/tigrcorn-quic-cc/) | [tigrcorn-quic-cc-reno](https://pypi.org/project/tigrcorn-quic-cc-reno/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
96
96
 
97
97
  ## Best Practices
98
98
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tigrcorn-runtime"
7
- version = "0.3.18.dev2"
7
+ version = "0.3.18.dev3"
8
8
  description = "Server runner, app loading, lifecycle hooks, workers, reload, and embedded runtime for the Tigrcorn ASGI3 web server."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10,<3.15"
@@ -30,14 +30,26 @@ classifiers = [
30
30
  "Typing :: Typed",
31
31
  ]
32
32
  dependencies = [
33
- "tigrcorn-core==0.3.18.dev2",
34
- "tigrcorn-config==0.3.18.dev2",
35
- "tigrcorn-asgi==0.3.18.dev2",
36
- "tigrcorn-transports==0.3.18.dev2",
37
- "tigrcorn-protocols==0.3.18.dev2",
38
- "tigrcorn-security==0.3.18.dev2",
33
+ "tigrcorn-core==0.3.18.dev3",
34
+ "tigrcorn-config==0.3.18.dev3",
35
+ "tigrcorn-asgi==0.3.18.dev3",
36
+ "tigrcorn-transports==0.3.18.dev3",
37
+ "tigrcorn-protocols==0.3.18.dev3",
38
+ "tigrcorn-security==0.3.18.dev3",
39
+ "tigrcorn-quic-cc==0.3.18.dev3",
40
+ "tigrcorn-quic-cc-reno==0.3.18.dev3",
39
41
  ]
40
42
 
43
+ [tool.uv.sources]
44
+ tigrcorn-core = { workspace = true }
45
+ tigrcorn-config = { workspace = true }
46
+ tigrcorn-asgi = { workspace = true }
47
+ tigrcorn-transports = { workspace = true }
48
+ tigrcorn-protocols = { workspace = true }
49
+ tigrcorn-security = { workspace = true }
50
+ tigrcorn-quic-cc = { workspace = true }
51
+ tigrcorn-quic-cc-reno = { workspace = true }
52
+
41
53
  [project.optional-dependencies]
42
54
  uvloop = ["uvloop>=0.19.0; platform_system != 'Windows'"]
43
55
  trio = ["trio>=0.25.0"]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
+ import json
4
5
  import os
5
6
  import sys
6
7
 
@@ -18,6 +19,16 @@ def _add_flag_pair(group: argparse._ArgumentGroup, positive: str, negative: str,
18
19
  group.add_argument(negative, action='store_false', dest=dest, default=None, help=f"Disable {help_text.lower()}")
19
20
 
20
21
 
22
+ def _json_object(value: str) -> dict[str, object]:
23
+ try:
24
+ parsed = json.loads(value)
25
+ except json.JSONDecodeError as exc:
26
+ raise argparse.ArgumentTypeError(f"invalid JSON object: {exc.msg}") from exc
27
+ if not isinstance(parsed, dict):
28
+ raise argparse.ArgumentTypeError("value must be a JSON object")
29
+ return parsed
30
+
31
+
21
32
  def build_parser() -> argparse.ArgumentParser:
22
33
  parser = argparse.ArgumentParser(prog="tigrcorn", description="ASGI3-compatible transport server")
23
34
  parser.add_argument("app", nargs="?", help="Application import string in module:attr form")
@@ -152,11 +163,17 @@ def build_parser() -> argparse.ArgumentParser:
152
163
  protocol_group.add_argument("--quic-max-datagram-size", type=int, default=None, help=quic_flag_help("--quic-max-datagram-size"))
153
164
  protocol_group.add_argument("--quic-idle-timeout", type=float, default=None, help=quic_flag_help("--quic-idle-timeout"))
154
165
  protocol_group.add_argument("--quic-early-data-policy", choices=["allow", "deny", "require"], default=None, help=quic_flag_help("--quic-early-data-policy"))
166
+ protocol_group.add_argument("--quic-congestion-control", default=None, help=quic_flag_help("--quic-congestion-control"))
167
+ protocol_group.add_argument("--quic-congestion-control-options", type=_json_object, default=None, help=quic_flag_help("--quic-congestion-control-options"))
155
168
  protocol_group.add_argument("--webtransport-max-sessions", type=int, default=None, help="Maximum concurrently active WebTransport sessions")
156
169
  protocol_group.add_argument("--webtransport-max-streams", type=int, default=None, help="Maximum concurrently active WebTransport streams")
157
170
  protocol_group.add_argument("--webtransport-max-datagram-size", type=int, default=None, help="Maximum WebTransport datagram payload size")
171
+ protocol_group.add_argument("--webtransport-stream-receive-coalesce-bytes", type=int, default=None, help="Maximum buffered client-stream bytes before emitting an ASGI receive event")
172
+ protocol_group.add_argument("--webtransport-stream-receive-max-delay-ms", type=int, default=None, help="Maximum delay before buffered client-stream bytes are emitted to the ASGI application")
158
173
  protocol_group.add_argument("--webtransport-origin", action="append", default=None, help="Allowed WebTransport Origin value; repeat or use comma-separated values")
159
174
  protocol_group.add_argument("--webtransport-path", default=None, help="WebTransport CONNECT path prefix")
175
+ protocol_group.add_argument("--webtransport-profile", dest="webtransport_profiles", action="append", default=None, help=argparse.SUPPRESS)
176
+ protocol_group.add_argument("--webtransport-preferred-profile", default=None, help=argparse.SUPPRESS)
160
177
  protocol_group.add_argument("--pipe-mode", choices=["rawframed", "stream"], default=None)
161
178
  protocol_group.add_argument("--quic-secret", default=None, help=argparse.SUPPRESS)
162
179
  return parser
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass
5
+
6
+ from tigrcorn_quic_cc import (
7
+ CongestionControllerFactory,
8
+ ProviderMetadata,
9
+ ProviderRegistry,
10
+ )
11
+ from tigrcorn_quic_cc_reno import factory as reno_factory
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class ResolvedCongestionControl:
16
+ factory: CongestionControllerFactory
17
+ options: Mapping[str, object]
18
+ metadata: ProviderMetadata
19
+
20
+
21
+ def resolve_congestion_control(
22
+ algorithm_id: str,
23
+ options: Mapping[str, object] | None = None,
24
+ ) -> ResolvedCongestionControl:
25
+ """Resolve and validate one provider during listener construction."""
26
+
27
+ discovered = ProviderRegistry()
28
+ registry = (
29
+ discovered
30
+ if algorithm_id in discovered.provider_names()
31
+ else ProviderRegistry(builtins={"reno": reno_factory})
32
+ )
33
+ factory = registry.resolve(algorithm_id)
34
+ validated_options = factory.validate_options(options or {})
35
+ return ResolvedCongestionControl(
36
+ factory=factory,
37
+ options=dict(validated_options),
38
+ metadata=factory.metadata,
39
+ )
40
+
41
+
42
+ __all__ = ["ResolvedCongestionControl", "resolve_congestion_control"]
@@ -23,7 +23,10 @@ class _TigrCornServerBase:
23
23
  self._listeners: list[TCPListener | UDPListener | UnixListener | PipeListener | InProcListener] = []
24
24
  self._datagram_handlers: list[HTTP3DatagramHandler] = []
25
25
  self._transport_domain_accounting = TransportDomainAccounting()
26
+ self._connection_inventory = RuntimeConnectionInventory()
27
+ self._connection_sequence = 0
26
28
  self._quic_operational_security: dict[str, QuicOperationalSecurityRuntime] = {}
29
+ self._quic_congestion_controls: dict[int, Any] = {}
27
30
  self._webtransport_governance = WebTransportGovernanceManager(default_webtransport_budget_policy())
28
31
  self._should_exit = asyncio.Event()
29
32
  self._started = False
@@ -46,4 +49,21 @@ class _TigrCornServerBase:
46
49
  jitter = max(0, config.process.max_requests_jitter)
47
50
  self._request_budget = config.process.limit_max_requests + (random.randint(0, jitter) if jitter else 0)
48
51
 
52
+ def _next_connection_id(self, listener_cfg: ListenerConfig) -> str:
53
+ self._connection_sequence += 1
54
+ try:
55
+ listener_index = self.config.listeners.index(listener_cfg)
56
+ except ValueError:
57
+ listener_index = self._connection_sequence
58
+ listener_id = f"listener:{listener_index}"
59
+ return f"conn:{listener_id}:{self._connection_sequence}"
60
+
61
+ @staticmethod
62
+ def _address_string(address: Any) -> str | None:
63
+ if isinstance(address, tuple) and len(address) >= 2:
64
+ return f"{address[0]}:{address[1]}"
65
+ if isinstance(address, str):
66
+ return address
67
+ return None
68
+
49
69
  __all__ = [name for name in globals() if not name.startswith('__')]
@@ -5,6 +5,15 @@ from .imports import *
5
5
  class _TigrCornServerClientHandlerMixin:
6
6
  def _make_client_handler(self, listener_cfg: ListenerConfig):
7
7
  if listener_cfg.kind == 'udp':
8
+ listener_congestion_control = (
9
+ listener_cfg.congestion_control
10
+ or self.config.quic.congestion_control
11
+ )
12
+ congestion_control = resolve_congestion_control(
13
+ listener_congestion_control.algorithm or "reno",
14
+ listener_congestion_control.options,
15
+ )
16
+ self._quic_congestion_controls[id(listener_cfg)] = congestion_control
8
17
  h3_handler = HTTP3DatagramHandler(
9
18
  app=self.app,
10
19
  config=self.config,
@@ -13,6 +22,9 @@ class _TigrCornServerClientHandlerMixin:
13
22
  scheduler=self.scheduler,
14
23
  metrics=self.state.metrics,
15
24
  webtransport_governance=self._webtransport_governance,
25
+ connection_inventory=self._connection_inventory,
26
+ congestion_controller_factory=congestion_control.factory,
27
+ congestion_controller_options=congestion_control.options,
16
28
  )
17
29
  self._datagram_handlers.append(h3_handler)
18
30
 
@@ -74,6 +86,20 @@ class _TigrCornServerClientHandlerMixin:
74
86
  server = (server_host or '', server_port)
75
87
  scheme = 'https' if ssl_obj else (listener_cfg.scheme or 'http')
76
88
  ws_scheme = 'wss' if ssl_obj else 'ws'
89
+ connection_id = self._next_connection_id(listener_cfg)
90
+ remote_address = self._address_string(peername)
91
+ local_address = self._address_string(sockname)
92
+ listener_index = self.config.listeners.index(listener_cfg) if listener_cfg in self.config.listeners else 0
93
+ self._connection_inventory.open_connection(
94
+ connection_id,
95
+ transport=listener_cfg.kind,
96
+ protocols=tuple(sorted(listener_cfg.enabled_protocols)),
97
+ listener_id=f"listener:{listener_index}",
98
+ peer_id=peer_id_from_address(remote_address),
99
+ remote_address=remote_address,
100
+ local_address=local_address,
101
+ security={"alpn": selected_alpn, "tls": bool(ssl_obj)},
102
+ )
77
103
  try:
78
104
  if selected_alpn == 'h2' and '2' in listener_cfg.http_versions:
79
105
  h2_handler = HTTP2ConnectionHandler(
@@ -88,6 +114,8 @@ class _TigrCornServerClientHandlerMixin:
88
114
  server=server,
89
115
  scheme=scheme,
90
116
  scope_extensions=scope_tls_extensions,
117
+ connection_id=connection_id,
118
+ connection_inventory=self._connection_inventory,
91
119
  )
92
120
  await h2_handler.handle()
93
121
  return
@@ -109,6 +137,8 @@ class _TigrCornServerClientHandlerMixin:
109
137
  scheme=scheme,
110
138
  prebuffer=initial,
111
139
  scope_extensions=scope_tls_extensions,
140
+ connection_id=connection_id,
141
+ connection_inventory=self._connection_inventory,
112
142
  )
113
143
  await h2_handler.handle()
114
144
  return
@@ -123,8 +153,10 @@ class _TigrCornServerClientHandlerMixin:
123
153
  scheme=scheme,
124
154
  ws_scheme=ws_scheme,
125
155
  scope_extensions=scope_tls_extensions,
156
+ connection_id=connection_id,
126
157
  )
127
158
  finally:
159
+ self._connection_inventory.close_connection(connection_id, reason='client-handler-complete')
128
160
  lease.release()
129
161
  self.state.metrics.connection_closed()
130
162
  writer.close()
@@ -21,6 +21,7 @@ class _TigrCornServerDiagnosticsMixin:
21
21
  "bound_endpoint": bound_endpoints[index] if index < len(bound_endpoints) else None,
22
22
  "protocols": list(protocols),
23
23
  "http_versions": list(listener.http_versions),
24
+ "quic_congestion_control": self._describe_congestion_control(listener),
24
25
  "tls": {
25
26
  "enabled": listener.ssl_enabled,
26
27
  "alpn_protocols": list(listener.alpn_protocols),
@@ -44,6 +45,22 @@ class _TigrCornServerDiagnosticsMixin:
44
45
  "reason": self.app_interface_reason,
45
46
  },
46
47
  "capabilities": {
48
+ "ids": [
49
+ "protocol.http1",
50
+ "protocol.http2",
51
+ "protocol.http3",
52
+ "protocol.lifespan",
53
+ "protocol.quic",
54
+ "protocol.websocket",
55
+ "protocol.webtransport",
56
+ "transport.inproc",
57
+ "transport.pipe",
58
+ "transport.quic",
59
+ "transport.quic.congestion-control.v1",
60
+ "transport.tcp",
61
+ "transport.udp",
62
+ "transport.unix",
63
+ ],
47
64
  "protocols": ["http1", "http2", "http3", "lifespan", "quic", "websocket", "webtransport"],
48
65
  "transports": sorted(TRANSPORTS),
49
66
  },
@@ -52,6 +69,7 @@ class _TigrCornServerDiagnosticsMixin:
52
69
  "configured_transports": sorted(configured_transports),
53
70
  "active_transports": sorted(configured_transports) if self._started else [],
54
71
  "transport_domains": self.transport_domain_diagnostics(),
72
+ "connection_inventory": self.connection_inventory(),
55
73
  "quic_operational_security": self.quic_operational_security_evidence(),
56
74
  "webtransport_resource_governance": self.webtransport_resource_governance(),
57
75
  "listeners": listeners,
@@ -102,6 +120,9 @@ class _TigrCornServerDiagnosticsMixin:
102
120
  for label, collector in sorted(self._quic_operational_security.items())
103
121
  }
104
122
 
123
+ def connection_inventory(self) -> dict[str, Any]:
124
+ return self._jsonable(self._connection_inventory.snapshot())
125
+
105
126
  def webtransport_resource_governance(self) -> dict[str, Any]:
106
127
  return self._jsonable(self._webtransport_governance.snapshot())
107
128
 
@@ -134,6 +155,39 @@ class _TigrCornServerDiagnosticsMixin:
134
155
  def _redact(value: Any) -> str | None:
135
156
  return "<redacted>" if value else None
136
157
 
158
+ @staticmethod
159
+ def _redact_provider_options(options: dict[str, Any]) -> dict[str, Any]:
160
+ sensitive = ("secret", "token", "password", "credential", "key")
161
+ return {
162
+ key: "<redacted>" if any(part in key.lower() for part in sensitive) else value
163
+ for key, value in options.items()
164
+ }
165
+
166
+ def _describe_congestion_control(self, listener: ListenerConfig) -> dict[str, Any] | None:
167
+ if listener.kind != "udp":
168
+ return None
169
+ congestion_control = (
170
+ listener.congestion_control or self.config.quic.congestion_control
171
+ )
172
+ resolved = self._quic_congestion_controls.get(id(listener))
173
+ metadata = None
174
+ if resolved is not None:
175
+ metadata = {
176
+ "algorithm_id": resolved.metadata.algorithm_id,
177
+ "display_name": resolved.metadata.display_name,
178
+ "distribution": resolved.metadata.distribution,
179
+ "distribution_version": resolved.metadata.distribution_version,
180
+ "api_version": resolved.metadata.api_version,
181
+ "capabilities": list(resolved.metadata.capabilities),
182
+ }
183
+ return {
184
+ "algorithm": congestion_control.algorithm,
185
+ "options": self._redact_provider_options(
186
+ congestion_control.options
187
+ ),
188
+ "resolved_provider": metadata,
189
+ }
190
+
137
191
  @staticmethod
138
192
  def _jsonable(value: Any) -> Any:
139
193
  if isinstance(value, tuple):
@@ -23,6 +23,7 @@ class _TigrCornServerHTTP11Mixin:
23
23
  scheme: str,
24
24
  ws_scheme: str,
25
25
  scope_extensions: dict | None = None,
26
+ connection_id: str | None = None,
26
27
  ) -> None:
27
28
  keep_handling = True
28
29
  handled_requests = 0
@@ -67,7 +68,19 @@ class _TigrCornServerHTTP11Mixin:
67
68
  request.keep_alive = apply_keep_alive_policy(request.keep_alive, enabled=self.config.http.http1_keep_alive)
68
69
 
69
70
  if request.method.upper() == 'CONNECT':
70
- await self._handle_http11_connect_tunnel(reader, writer, request, client=request_client)
71
+ session_id = f"{connection_id}:connect:{handled_requests}" if connection_id else None
72
+ if session_id is not None:
73
+ self._connection_inventory.open_session(
74
+ session_id,
75
+ connection_id=connection_id,
76
+ kind='connect-tunnel',
77
+ metadata={'authority': request.target, 'protocol': 'http1'},
78
+ )
79
+ try:
80
+ await self._handle_http11_connect_tunnel(reader, writer, request, client=request_client)
81
+ finally:
82
+ if session_id is not None:
83
+ self._connection_inventory.close_session(session_id, reason='connect-complete')
71
84
  keep_handling = False
72
85
  break
73
86
 
@@ -93,10 +106,20 @@ class _TigrCornServerHTTP11Mixin:
93
106
  scope_extensions=scope_extensions,
94
107
  metrics=self.state.metrics,
95
108
  )
109
+ session_id = f"{connection_id}:websocket:{handled_requests}" if connection_id else None
110
+ if session_id is not None:
111
+ self._connection_inventory.open_session(
112
+ session_id,
113
+ connection_id=connection_id,
114
+ kind='websocket',
115
+ metadata={'path': request.path, 'protocol': 'http1'},
116
+ )
96
117
  try:
97
118
  self.state.metrics.websocket_opened()
98
119
  await handler.handle()
99
120
  finally:
121
+ if session_id is not None:
122
+ self._connection_inventory.close_session(session_id, reason='websocket-complete')
100
123
  work_lease.release()
101
124
  self.state.metrics.websocket_closed()
102
125
  keep_handling = False
@@ -108,6 +131,14 @@ class _TigrCornServerHTTP11Mixin:
108
131
  await self._write_error(writer, 503, b'scheduler overloaded', keep_alive=False)
109
132
  break
110
133
  try:
134
+ session_id = f"{connection_id}:http1:{handled_requests}" if connection_id else None
135
+ if session_id is not None:
136
+ self._connection_inventory.open_session(
137
+ session_id,
138
+ connection_id=connection_id,
139
+ kind='http-request',
140
+ metadata={'method': request.method, 'path': request.path, 'protocol': 'http1'},
141
+ )
111
142
  keep_handling = await self._serve_http11_request(
112
143
  reader,
113
144
  writer,
@@ -116,8 +147,12 @@ class _TigrCornServerHTTP11Mixin:
116
147
  server=request_server,
117
148
  scheme=request_scheme,
118
149
  scope_extensions=scope_extensions,
150
+ connection_id=connection_id,
151
+ session_id=session_id,
119
152
  )
120
153
  finally:
154
+ if session_id is not None:
155
+ self._connection_inventory.close_session(session_id, reason='request-complete')
121
156
  work_lease.release()
122
157
  handled_requests += 1
123
158
 
@@ -13,6 +13,8 @@ class _TigrCornServerHTTP11ServeMixin:
13
13
  server: tuple[str, int] | tuple[str, None] | None,
14
14
  scheme: str,
15
15
  scope_extensions: dict | None = None,
16
+ connection_id: str | None = None,
17
+ session_id: str | None = None,
16
18
  ) -> bool:
17
19
  host_header = get_header(request.headers, b'host')
18
20
  if self.config.allowed_server_names and not authority_allowed(host_header, self.config.allowed_server_names):
@@ -161,6 +163,10 @@ class _TigrCornServerHTTP11ServeMixin:
161
163
  trailers=trailers,
162
164
  )
163
165
  self.state.metrics.requests_served += 1
166
+ if connection_id is not None:
167
+ self._connection_inventory.increment_connection_counter(connection_id, 'requests')
168
+ if session_id is not None:
169
+ self._connection_inventory.increment_session_counter(session_id, 'responses')
164
170
  except ProtocolError:
165
171
  self.state.metrics.requests_failed += 1
166
172
  await self._write_error(writer, 400, b'bad request trailers', keep_alive=False)
@@ -30,9 +30,11 @@ from tigrcorn_protocols.lifespan.driver import LifespanManager
30
30
  from tigrcorn_protocols.rawframed.handler import RawFramedApplicationHandler
31
31
  from tigrcorn_protocols.websocket.handler import WebSocketConnectionHandler
32
32
  from tigrcorn_protocols.scheduler import ProductionScheduler, SchedulerPolicy
33
+ from tigrcorn_protocols.sessions import RuntimeConnectionInventory, peer_id_from_address
33
34
  from tigrcorn_security.tls import build_server_ssl_context, tls_extension_payload
34
35
  from tigrcorn_runtime.server.hooks import run_async_hooks
35
36
  from tigrcorn_runtime.server.state import ServerState
37
+ from tigrcorn_runtime.quic_cc import resolve_congestion_control
36
38
  from tigrcorn_transports.tcp.reader import PrebufferedReader
37
39
  from tigrcorn_transports.registry import (
38
40
  TRANSPORTS,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrcorn-runtime
3
- Version: 0.3.18.dev2
3
+ Version: 0.3.18.dev3
4
4
  Summary: Server runner, app loading, lifecycle hooks, workers, reload, and embedded runtime for the Tigrcorn ASGI3 web server.
5
5
  Author-email: Jacob Stewart <jacob@swarmauri.com>
6
6
  License: Apache License
@@ -187,12 +187,14 @@ Classifier: Typing :: Typed
187
187
  Requires-Python: <3.15,>=3.10
188
188
  Description-Content-Type: text/markdown
189
189
  License-File: LICENSE
190
- Requires-Dist: tigrcorn-core==0.3.18.dev2
191
- Requires-Dist: tigrcorn-config==0.3.18.dev2
192
- Requires-Dist: tigrcorn-asgi==0.3.18.dev2
193
- Requires-Dist: tigrcorn-transports==0.3.18.dev2
194
- Requires-Dist: tigrcorn-protocols==0.3.18.dev2
195
- Requires-Dist: tigrcorn-security==0.3.18.dev2
190
+ Requires-Dist: tigrcorn-core==0.3.18.dev3
191
+ Requires-Dist: tigrcorn-config==0.3.18.dev3
192
+ Requires-Dist: tigrcorn-asgi==0.3.18.dev3
193
+ Requires-Dist: tigrcorn-transports==0.3.18.dev3
194
+ Requires-Dist: tigrcorn-protocols==0.3.18.dev3
195
+ Requires-Dist: tigrcorn-security==0.3.18.dev3
196
+ Requires-Dist: tigrcorn-quic-cc==0.3.18.dev3
197
+ Requires-Dist: tigrcorn-quic-cc-reno==0.3.18.dev3
196
198
  Provides-Extra: uvloop
197
199
  Requires-Dist: uvloop>=0.19.0; platform_system != "Windows" and extra == "uvloop"
198
200
  Provides-Extra: trio
@@ -293,7 +295,7 @@ The package exposes its supported public surface through the <code>tigrcorn_runt
293
295
 
294
296
  ## Package Graph
295
297
 
296
- [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
298
+ [tigrcorn-core](https://pypi.org/project/tigrcorn-core/) | [tigrcorn-quic-cc](https://pypi.org/project/tigrcorn-quic-cc/) | [tigrcorn-quic-cc-reno](https://pypi.org/project/tigrcorn-quic-cc-reno/) | [tigrcorn-config](https://pypi.org/project/tigrcorn-config/) | [tigrcorn-http](https://pypi.org/project/tigrcorn-http/) | [tigrcorn-asgi](https://pypi.org/project/tigrcorn-asgi/) | [tigrcorn-contract](https://pypi.org/project/tigrcorn-contract/) | [tigrcorn-transports](https://pypi.org/project/tigrcorn-transports/) | [tigrcorn-security](https://pypi.org/project/tigrcorn-security/) | [tigrcorn-protocols](https://pypi.org/project/tigrcorn-protocols/) | [tigrcorn-static](https://pypi.org/project/tigrcorn-static/) | [tigrcorn-observability](https://pypi.org/project/tigrcorn-observability/) | [tigrcorn-runtime](https://pypi.org/project/tigrcorn-runtime/) | [tigrcorn-compat](https://pypi.org/project/tigrcorn-compat/) | [tigrcorn-certification](https://pypi.org/project/tigrcorn-certification/)
297
299
 
298
300
  ## Best Practices
299
301
 
@@ -7,6 +7,7 @@ src/tigrcorn_runtime/app_interfaces.py
7
7
  src/tigrcorn_runtime/cli.py
8
8
  src/tigrcorn_runtime/embedded.py
9
9
  src/tigrcorn_runtime/py.typed
10
+ src/tigrcorn_runtime/quic_cc.py
10
11
  src/tigrcorn_runtime.egg-info/PKG-INFO
11
12
  src/tigrcorn_runtime.egg-info/SOURCES.txt
12
13
  src/tigrcorn_runtime.egg-info/dependency_links.txt
@@ -0,0 +1,16 @@
1
+ tigrcorn-core==0.3.18.dev3
2
+ tigrcorn-config==0.3.18.dev3
3
+ tigrcorn-asgi==0.3.18.dev3
4
+ tigrcorn-transports==0.3.18.dev3
5
+ tigrcorn-protocols==0.3.18.dev3
6
+ tigrcorn-security==0.3.18.dev3
7
+ tigrcorn-quic-cc==0.3.18.dev3
8
+ tigrcorn-quic-cc-reno==0.3.18.dev3
9
+
10
+ [trio]
11
+ trio>=0.25.0
12
+
13
+ [uvloop]
14
+
15
+ [uvloop:platform_system != "Windows"]
16
+ uvloop>=0.19.0
@@ -1,14 +0,0 @@
1
- tigrcorn-core==0.3.18.dev2
2
- tigrcorn-config==0.3.18.dev2
3
- tigrcorn-asgi==0.3.18.dev2
4
- tigrcorn-transports==0.3.18.dev2
5
- tigrcorn-protocols==0.3.18.dev2
6
- tigrcorn-security==0.3.18.dev2
7
-
8
- [trio]
9
- trio>=0.25.0
10
-
11
- [uvloop]
12
-
13
- [uvloop:platform_system != "Windows"]
14
- uvloop>=0.19.0