tigrcorn-protocols 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.
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/PKG-INFO +7 -7
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/README.md +1 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/pyproject.toml +6 -6
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/connect.py +8 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/core.py +6 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/frames.py +1 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/imports.py +1 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/http2/handler/inventory.py +56 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/streams.py +12 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/websocket_support.py +10 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/codec.py +2 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/connect.py +15 -2
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/core.py +10 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/custom.py +1 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/imports.py +6 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/lifecycle.py +77 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/outbound.py +113 -19
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/packet.py +118 -21
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/http3/handler/priority_lock.py +107 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/requests.py +15 -2
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/responses.py +5 -5
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/session.py +16 -1
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/websocket_runtime.py +32 -3
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/webtransport.py +232 -74
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/webtransport_datagrams.py +1 -1
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/http3/handler/webtransport_send.py +166 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/http3/handler/webtransport_stream_flow.py +104 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/webtransport_streams.py +100 -5
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/http3/handler/webtransport_support.py +256 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/sessions/__init__.py +27 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/sessions/inventory.py +317 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/webtransport/__init__.py +44 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/webtransport/governance.py +5 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/webtransport/negotiation.py +107 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols/webtransport/profiles.py +179 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols.egg-info/PKG-INFO +7 -7
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols.egg-info/SOURCES.txt +7 -0
- tigrcorn_protocols-0.3.18.dev3/src/tigrcorn_protocols.egg-info/requires.txt +5 -0
- tigrcorn_protocols-0.3.18.dev2/src/tigrcorn_protocols/http3/handler/webtransport_support.py +0 -101
- tigrcorn_protocols-0.3.18.dev2/src/tigrcorn_protocols/sessions/__init__.py +0 -1
- tigrcorn_protocols-0.3.18.dev2/src/tigrcorn_protocols.egg-info/requires.txt +0 -5
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/LICENSE +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/setup.cfg +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/_compression.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/matrix.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/models.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/robustness.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/topology.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/client_session_coverage/validation.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/connect.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/content_coding.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/custom/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/custom/adapters.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/custom/registry.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/backpressure.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/buffers.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/credits.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/keepalive.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/timeouts.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/flow/watermarks.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/keepalive.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/body.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/core.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/head.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/models.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/target.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/parser/validation.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/serializer.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http1/state.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/codec.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/flow.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/flow_control.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/headers.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/io.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/requests.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/handler/responses.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/hpack.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/state.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/streams.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http2/websocket.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/handler/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/decoder.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/encoder.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/model.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/static_table.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/qpack/wire.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/state.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/streams/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/streams/connection.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/streams/constants.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/streams/request.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/http3/websocket.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/lifespan/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/lifespan/driver.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/py.typed +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/rawframed/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/rawframed/codec.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/rawframed/frames.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/rawframed/handler.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/rawframed/state.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/registry.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/resume.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/cancellation.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/dispatch.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/fairness.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/policy.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/priorities.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/quotas.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/runtime.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/scheduler/tasks.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/base.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/connection.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/limits.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/manager.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/metadata.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/sessions/quic.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/base.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/ids.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/multiplex.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/registry.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/streams/singleplex.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/codec.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/extensions.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/frames.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/handler/__init__.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/handler/app_send.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/handler/core.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/handler/errors.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/handshake.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/websocket/state.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols/webtransport/wire.py +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols.egg-info/dependency_links.txt +0 -0
- {tigrcorn_protocols-0.3.18.dev2 → tigrcorn_protocols-0.3.18.dev3}/src/tigrcorn_protocols.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrcorn-protocols
|
|
3
|
-
Version: 0.3.18.
|
|
3
|
+
Version: 0.3.18.dev3
|
|
4
4
|
Summary: Protocol handlers for Tigrcorn HTTP/1.1, HTTP/2, HTTP/3, QUIC, WebSocket, WebTransport, lifespan, and ASGI3 traffic.
|
|
5
5
|
Author-email: Jacob Stewart <jacob@swarmauri.com>
|
|
6
6
|
License: Apache License
|
|
@@ -187,11 +187,11 @@ 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.
|
|
191
|
-
Requires-Dist: tigrcorn-config==0.3.18.
|
|
192
|
-
Requires-Dist: tigrcorn-asgi==0.3.18.
|
|
193
|
-
Requires-Dist: tigrcorn-http==0.3.18.
|
|
194
|
-
Requires-Dist: tigrcorn-transports==0.3.18.
|
|
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-http==0.3.18.dev3
|
|
194
|
+
Requires-Dist: tigrcorn-transports==0.3.18.dev3
|
|
195
195
|
Dynamic: license-file
|
|
196
196
|
|
|
197
197
|
<div align="center">
|
|
@@ -284,7 +284,7 @@ The package exposes its supported public surface through the <code>tigrcorn_prot
|
|
|
284
284
|
|
|
285
285
|
## Package Graph
|
|
286
286
|
|
|
287
|
-
[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/)
|
|
287
|
+
[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/)
|
|
288
288
|
|
|
289
289
|
## Best Practices
|
|
290
290
|
|
|
@@ -88,7 +88,7 @@ The package exposes its supported public surface through the <code>tigrcorn_prot
|
|
|
88
88
|
|
|
89
89
|
## Package Graph
|
|
90
90
|
|
|
91
|
-
[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/)
|
|
91
|
+
[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/)
|
|
92
92
|
|
|
93
93
|
## Best Practices
|
|
94
94
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tigrcorn-protocols"
|
|
7
|
-
version = "0.3.18.
|
|
7
|
+
version = "0.3.18.dev3"
|
|
8
8
|
description = "Protocol handlers for Tigrcorn HTTP/1.1, HTTP/2, HTTP/3, QUIC, WebSocket, WebTransport, lifespan, and ASGI3 traffic."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<3.15"
|
|
@@ -30,11 +30,11 @@ classifiers = [
|
|
|
30
30
|
"Typing :: Typed",
|
|
31
31
|
]
|
|
32
32
|
dependencies = [
|
|
33
|
-
"tigrcorn-core==0.3.18.
|
|
34
|
-
"tigrcorn-config==0.3.18.
|
|
35
|
-
"tigrcorn-asgi==0.3.18.
|
|
36
|
-
"tigrcorn-http==0.3.18.
|
|
37
|
-
"tigrcorn-transports==0.3.18.
|
|
33
|
+
"tigrcorn-core==0.3.18.dev3",
|
|
34
|
+
"tigrcorn-config==0.3.18.dev3",
|
|
35
|
+
"tigrcorn-asgi==0.3.18.dev3",
|
|
36
|
+
"tigrcorn-http==0.3.18.dev3",
|
|
37
|
+
"tigrcorn-transports==0.3.18.dev3",
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[tool.setuptools]
|
|
@@ -67,6 +67,7 @@ class _HTTP2ConnectTunnel:
|
|
|
67
67
|
if self.work_lease is not None:
|
|
68
68
|
self.work_lease.release()
|
|
69
69
|
await close_tcp_writer(self.upstream_writer)
|
|
70
|
+
self.handler._close_inventory_session(self.stream_id, reason="connect-complete")
|
|
70
71
|
self.handler._finalize_stream_if_complete(self.stream_id)
|
|
71
72
|
|
|
72
73
|
async def _relay_upstream_to_client(self) -> None:
|
|
@@ -108,6 +109,7 @@ class HTTP2ConnectMixin:
|
|
|
108
109
|
await self._write_raw(serialize_rst_stream(stream_id, H2_CONNECT_ERROR))
|
|
109
110
|
state.mark_reset_sent()
|
|
110
111
|
self._cancel_stream(stream_id)
|
|
112
|
+
self._close_inventory_session(stream_id, reason="connect-reset")
|
|
111
113
|
self.streams.close(stream_id)
|
|
112
114
|
self._maybe_finish_after_goaway()
|
|
113
115
|
|
|
@@ -163,11 +165,17 @@ class HTTP2ConnectMixin:
|
|
|
163
165
|
work_lease=self.stream_work_leases.get(stream_id),
|
|
164
166
|
)
|
|
165
167
|
state.connect_tunnel = tunnel
|
|
168
|
+
self._open_inventory_session(
|
|
169
|
+
stream_id,
|
|
170
|
+
kind="connect-tunnel",
|
|
171
|
+
metadata={"authority": request.target},
|
|
172
|
+
)
|
|
166
173
|
self.state.last_stream_id = max(self.state.last_stream_id, stream_id)
|
|
167
174
|
try:
|
|
168
175
|
await tunnel.start()
|
|
169
176
|
except Exception:
|
|
170
177
|
state.connect_tunnel = None
|
|
178
|
+
self._close_inventory_session(stream_id, reason="connect-start-failed")
|
|
171
179
|
await close_tcp_writer(upstream_writer)
|
|
172
180
|
raise
|
|
173
181
|
if state.end_stream_received:
|
|
@@ -5,6 +5,7 @@ from .connect import HTTP2ConnectMixin
|
|
|
5
5
|
from .flow_control import HTTP2FlowControlMixin
|
|
6
6
|
from .frames import HTTP2FrameDispatchMixin
|
|
7
7
|
from .headers import HTTP2HeadersMixin
|
|
8
|
+
from .inventory import HTTP2InventoryMixin
|
|
8
9
|
from .io import HTTP2IOMixin
|
|
9
10
|
from .requests import HTTP2RequestsMixin
|
|
10
11
|
from .responses import HTTP2ResponsesMixin
|
|
@@ -22,6 +23,7 @@ class HTTP2ConnectionHandler(
|
|
|
22
23
|
HTTP2ResponsesMixin,
|
|
23
24
|
HTTP2ConnectMixin,
|
|
24
25
|
HTTP2WebSocketSupportMixin,
|
|
26
|
+
HTTP2InventoryMixin,
|
|
25
27
|
):
|
|
26
28
|
def __init__(
|
|
27
29
|
self,
|
|
@@ -38,6 +40,8 @@ class HTTP2ConnectionHandler(
|
|
|
38
40
|
scheme: str,
|
|
39
41
|
prebuffer: bytes = b"",
|
|
40
42
|
scope_extensions: dict | None = None,
|
|
43
|
+
connection_id: str | None = None,
|
|
44
|
+
connection_inventory: RuntimeConnectionInventory | None = None,
|
|
41
45
|
) -> None:
|
|
42
46
|
self.app = app
|
|
43
47
|
self.config = config
|
|
@@ -51,6 +55,8 @@ class HTTP2ConnectionHandler(
|
|
|
51
55
|
self.scheme = scheme
|
|
52
56
|
self.prebuffer = prebuffer
|
|
53
57
|
self.scope_extensions = dict(scope_extensions or {})
|
|
58
|
+
self.connection_id = connection_id
|
|
59
|
+
self.connection_inventory = connection_inventory
|
|
54
60
|
self.state = H2ConnectionState()
|
|
55
61
|
self.state.local_settings[SETTING_MAX_CONCURRENT_STREAMS] = self.config.http.http2_max_concurrent_streams
|
|
56
62
|
self.state.local_settings[SETTING_MAX_HEADER_LIST_SIZE] = self.config.http.http2_max_headers_size
|
|
@@ -116,6 +116,7 @@ class HTTP2FrameDispatchMixin:
|
|
|
116
116
|
await state.connect_tunnel.abort()
|
|
117
117
|
self._cancel_stream(frame.stream_id)
|
|
118
118
|
state.mark_reset_received()
|
|
119
|
+
self._close_inventory_session(frame.stream_id, reason="rst-stream")
|
|
119
120
|
self.streams.close(frame.stream_id)
|
|
120
121
|
self._notify_waiter(frame.stream_id)
|
|
121
122
|
self._maybe_finish_after_goaway()
|
|
@@ -63,6 +63,7 @@ from tigrcorn_protocols.scheduler.runtime import ProductionScheduler, WorkLease
|
|
|
63
63
|
from tigrcorn_protocols.http2.streams import H2StreamRegistry
|
|
64
64
|
from tigrcorn_protocols.connect import close_tcp_writer, half_close_tcp_writer, is_connect_allowed, parse_connect_authority
|
|
65
65
|
from tigrcorn_protocols.http2.websocket import H2WebSocketSession
|
|
66
|
+
from tigrcorn_protocols.sessions import RuntimeConnectionInventory
|
|
66
67
|
from tigrcorn_core.types import ASGIApp
|
|
67
68
|
from tigrcorn_core.utils.authority import authority_allowed
|
|
68
69
|
from tigrcorn_core.utils.headers import apply_response_header_policy, sanitize_early_hints_headers, strip_connection_specific_headers
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .imports import *
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HTTP2InventoryMixin:
|
|
7
|
+
def _inventory_session_id(self, stream_id: int) -> str | None:
|
|
8
|
+
if self.connection_inventory is None or self.connection_id is None:
|
|
9
|
+
return None
|
|
10
|
+
return f"h2:{self.connection_id}:{stream_id}"
|
|
11
|
+
|
|
12
|
+
def _open_inventory_session(
|
|
13
|
+
self,
|
|
14
|
+
stream_id: int,
|
|
15
|
+
*,
|
|
16
|
+
kind: str,
|
|
17
|
+
metadata: dict | None = None,
|
|
18
|
+
) -> str | None:
|
|
19
|
+
session_id = self._inventory_session_id(stream_id)
|
|
20
|
+
if session_id is None:
|
|
21
|
+
return None
|
|
22
|
+
payload = {"protocol": "http2", "stream_id": stream_id}
|
|
23
|
+
payload.update(metadata or {})
|
|
24
|
+
self.connection_inventory.open_session(
|
|
25
|
+
session_id,
|
|
26
|
+
connection_id=self.connection_id,
|
|
27
|
+
kind=kind,
|
|
28
|
+
stream_ids=(str(stream_id),),
|
|
29
|
+
metadata=payload,
|
|
30
|
+
)
|
|
31
|
+
self.connection_inventory.increment_connection_counter(self.connection_id, "streams")
|
|
32
|
+
if kind == "http-request":
|
|
33
|
+
self.connection_inventory.increment_connection_counter(self.connection_id, "requests")
|
|
34
|
+
elif kind == "websocket":
|
|
35
|
+
self.connection_inventory.increment_connection_counter(self.connection_id, "websockets")
|
|
36
|
+
elif kind == "connect-tunnel":
|
|
37
|
+
self.connection_inventory.increment_connection_counter(self.connection_id, "connect_tunnels")
|
|
38
|
+
return session_id
|
|
39
|
+
|
|
40
|
+
def _update_inventory_session(
|
|
41
|
+
self,
|
|
42
|
+
stream_id: int,
|
|
43
|
+
*,
|
|
44
|
+
state: str | None = None,
|
|
45
|
+
metadata: dict | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
session_id = self._inventory_session_id(stream_id)
|
|
48
|
+
if session_id is None:
|
|
49
|
+
return
|
|
50
|
+
self.connection_inventory.update_session(session_id, state=state, metadata=metadata)
|
|
51
|
+
|
|
52
|
+
def _close_inventory_session(self, stream_id: int, *, reason: str | None = None) -> None:
|
|
53
|
+
session_id = self._inventory_session_id(stream_id)
|
|
54
|
+
if session_id is None:
|
|
55
|
+
return
|
|
56
|
+
self.connection_inventory.close_session(session_id, reason=reason)
|
|
@@ -51,6 +51,12 @@ class HTTP2StreamsMixin:
|
|
|
51
51
|
await self._start_connect_tunnel(stream_id)
|
|
52
52
|
return
|
|
53
53
|
self.state.last_stream_id = max(self.state.last_stream_id, stream_id)
|
|
54
|
+
request = self._build_request(state)
|
|
55
|
+
self._open_inventory_session(
|
|
56
|
+
stream_id,
|
|
57
|
+
kind="http-request",
|
|
58
|
+
metadata={"method": request.method, "path": request.path},
|
|
59
|
+
)
|
|
54
60
|
task = asyncio.create_task(self._run_stream(stream_id), name=f"tigrcorn-h2-stream-{stream_id}")
|
|
55
61
|
self.stream_tasks[stream_id] = task
|
|
56
62
|
|
|
@@ -66,6 +72,7 @@ class HTTP2StreamsMixin:
|
|
|
66
72
|
if state is not None:
|
|
67
73
|
state.websocket_session = None
|
|
68
74
|
self._release_stream_work_lease(stream_id)
|
|
75
|
+
self._close_inventory_session(stream_id, reason="websocket-complete")
|
|
69
76
|
self._finalize_stream_if_complete(stream_id)
|
|
70
77
|
|
|
71
78
|
|
|
@@ -87,6 +94,7 @@ class HTTP2StreamsMixin:
|
|
|
87
94
|
if state is None or state.websocket_session is not None or state.connect_tunnel is not None:
|
|
88
95
|
return
|
|
89
96
|
if state.local_closed and state.end_stream_received:
|
|
97
|
+
self._close_inventory_session(stream_id, reason="stream-complete")
|
|
90
98
|
self._release_stream_work_lease(stream_id)
|
|
91
99
|
self._cancel_stream(stream_id)
|
|
92
100
|
self.streams.close(stream_id)
|
|
@@ -125,6 +133,7 @@ class HTTP2StreamsMixin:
|
|
|
125
133
|
self._finalize_stream_if_complete(stream_id)
|
|
126
134
|
self._maybe_finish_after_goaway()
|
|
127
135
|
finally:
|
|
136
|
+
self._close_inventory_session(stream_id, reason="request-complete")
|
|
128
137
|
self._release_stream_work_lease(stream_id)
|
|
129
138
|
|
|
130
139
|
|
|
@@ -141,14 +150,17 @@ class HTTP2StreamsMixin:
|
|
|
141
150
|
if state.websocket_session is not None:
|
|
142
151
|
with suppress(Exception):
|
|
143
152
|
await state.websocket_session.abort()
|
|
153
|
+
self._close_inventory_session(state.stream_id, reason="shutdown")
|
|
144
154
|
if state.connect_tunnel is not None:
|
|
145
155
|
with suppress(Exception):
|
|
146
156
|
await state.connect_tunnel.abort()
|
|
157
|
+
self._close_inventory_session(state.stream_id, reason="shutdown")
|
|
147
158
|
for stream_id, task in list(self.stream_tasks.items()):
|
|
148
159
|
task.cancel()
|
|
149
160
|
with suppress(asyncio.CancelledError):
|
|
150
161
|
await task
|
|
151
162
|
self.stream_tasks.pop(stream_id, None)
|
|
163
|
+
self._close_inventory_session(stream_id, reason="shutdown")
|
|
152
164
|
if not self.state.local_goaway_sent:
|
|
153
165
|
self.state.local_goaway_sent = True
|
|
154
166
|
self.state.local_goaway_last_stream_id = self.state.last_stream_id
|
|
@@ -41,6 +41,15 @@ class HTTP2WebSocketSupportMixin:
|
|
|
41
41
|
on_close=lambda stream_id=stream_id: self._on_websocket_stream_closed(stream_id),
|
|
42
42
|
)
|
|
43
43
|
state.websocket_session = session
|
|
44
|
+
self._open_inventory_session(
|
|
45
|
+
stream_id,
|
|
46
|
+
kind="websocket",
|
|
47
|
+
metadata={"path": request.path},
|
|
48
|
+
)
|
|
44
49
|
self.state.last_stream_id = max(self.state.last_stream_id, stream_id)
|
|
45
|
-
|
|
50
|
+
try:
|
|
51
|
+
await session.start()
|
|
52
|
+
except Exception:
|
|
53
|
+
self._close_inventory_session(stream_id, reason="websocket-start-failed")
|
|
54
|
+
raise
|
|
46
55
|
|
|
@@ -25,6 +25,7 @@ SETTING_ENABLE_CONNECT_PROTOCOL = 0x08
|
|
|
25
25
|
SETTING_H3_DATAGRAM = 0x33
|
|
26
26
|
SETTING_ENABLE_WEBTRANSPORT = 0x2B603742
|
|
27
27
|
SETTING_WT_MAX_SESSIONS = 0x14E9CD29
|
|
28
|
+
SETTING_WT_ENABLED = 0x2C7CF000
|
|
28
29
|
|
|
29
30
|
H3_DATAGRAM_ERROR = 0x33
|
|
30
31
|
H3_NO_ERROR = 0x0100
|
|
@@ -69,6 +70,7 @@ HTTP3_IANA_SETTINGS = {
|
|
|
69
70
|
"SETTINGS_ENABLE_CONNECT_PROTOCOL": SETTING_ENABLE_CONNECT_PROTOCOL,
|
|
70
71
|
"SETTINGS_H3_DATAGRAM": SETTING_H3_DATAGRAM,
|
|
71
72
|
"SETTINGS_WT_MAX_SESSIONS": SETTING_WT_MAX_SESSIONS,
|
|
73
|
+
"SETTINGS_WT_ENABLED": SETTING_WT_ENABLED,
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
HTTP3_IANA_ERROR_CODES = {
|
|
@@ -70,6 +70,11 @@ class _HTTP3ConnectTunnel:
|
|
|
70
70
|
with suppress(asyncio.CancelledError):
|
|
71
71
|
await self.relay_task
|
|
72
72
|
self.session.connect_tunnels.pop(self.stream_id, None)
|
|
73
|
+
if self.handler.connection_inventory is not None:
|
|
74
|
+
self.handler.connection_inventory.close_session(
|
|
75
|
+
f"{self.handler._connection_id_for_session(self.session)}:connect:{self.stream_id}",
|
|
76
|
+
reason='connect-abort',
|
|
77
|
+
)
|
|
73
78
|
lease = self.session.stream_work_leases.pop(self.stream_id, None)
|
|
74
79
|
if lease is not None:
|
|
75
80
|
lease.release()
|
|
@@ -128,7 +133,7 @@ class HTTP3ConnectMixin:
|
|
|
128
133
|
already_locked: bool = False,
|
|
129
134
|
) -> None:
|
|
130
135
|
if not already_locked:
|
|
131
|
-
async with
|
|
136
|
+
async with session.lock:
|
|
132
137
|
await self._send_http3_tunnel_data(
|
|
133
138
|
session,
|
|
134
139
|
stream_id,
|
|
@@ -154,7 +159,7 @@ class HTTP3ConnectMixin:
|
|
|
154
159
|
already_locked: bool = False,
|
|
155
160
|
) -> None:
|
|
156
161
|
if not already_locked:
|
|
157
|
-
async with
|
|
162
|
+
async with session.lock:
|
|
158
163
|
await self._reset_http3_tunnel_stream(
|
|
159
164
|
session,
|
|
160
165
|
stream_id,
|
|
@@ -252,6 +257,14 @@ class HTTP3ConnectMixin:
|
|
|
252
257
|
work_lease=session.stream_work_leases.get(stream_id),
|
|
253
258
|
)
|
|
254
259
|
session.connect_tunnels[stream_id] = tunnel
|
|
260
|
+
if self.connection_inventory is not None:
|
|
261
|
+
self.connection_inventory.open_session(
|
|
262
|
+
f"{self._connection_id_for_session(session)}:connect:{stream_id}",
|
|
263
|
+
connection_id=self._connection_id_for_session(session),
|
|
264
|
+
kind='connect-tunnel',
|
|
265
|
+
stream_ids=(str(stream_id),),
|
|
266
|
+
metadata={'authority': authority, 'protocol': 'http3'},
|
|
267
|
+
)
|
|
255
268
|
tunnel.start()
|
|
256
269
|
self.access_logger.log_http(session.addr, 'CONNECT', authority, 200, 'HTTP/3')
|
|
257
270
|
return self._build_http3_response_datagrams_locked(session, stream_id, 200, [], b'', end_stream=False)
|
|
@@ -6,16 +6,19 @@ from .custom import HTTP3CustomQuicMixin
|
|
|
6
6
|
from .lifecycle import HTTP3LifecycleMixin
|
|
7
7
|
from .outbound import HTTP3OutboundMixin
|
|
8
8
|
from .packet import HTTP3PacketMixin
|
|
9
|
+
from .priority_lock import PriorityLock
|
|
9
10
|
from .requests import HTTP3RequestsMixin
|
|
10
11
|
from .responses import HTTP3ResponsesMixin
|
|
11
12
|
from .websocket_runtime import HTTP3WebSocketRuntimeMixin
|
|
12
13
|
from .webtransport_datagrams import HTTP3WebTransportDatagramsMixin
|
|
14
|
+
from .webtransport_stream_flow import HTTP3WebTransportStreamFlowMixin
|
|
13
15
|
from .webtransport_streams import HTTP3WebTransportStreamsMixin
|
|
14
16
|
from .webtransport_support import HTTP3WebTransportSupportMixin
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class HTTP3DatagramHandler(
|
|
18
20
|
HTTP3PacketMixin,
|
|
21
|
+
HTTP3WebTransportStreamFlowMixin,
|
|
19
22
|
HTTP3WebTransportStreamsMixin,
|
|
20
23
|
HTTP3WebTransportDatagramsMixin,
|
|
21
24
|
HTTP3WebTransportSupportMixin,
|
|
@@ -41,6 +44,9 @@ class HTTP3DatagramHandler(
|
|
|
41
44
|
scheduler: ProductionScheduler | None = None,
|
|
42
45
|
metrics: Metrics | None = None,
|
|
43
46
|
webtransport_governance: Any | None = None,
|
|
47
|
+
connection_inventory: RuntimeConnectionInventory | None = None,
|
|
48
|
+
congestion_controller_factory: Any | None = None,
|
|
49
|
+
congestion_controller_options: Any | None = None,
|
|
44
50
|
) -> None:
|
|
45
51
|
self.app = app
|
|
46
52
|
self.config = config
|
|
@@ -49,10 +55,13 @@ class HTTP3DatagramHandler(
|
|
|
49
55
|
self.scheduler = scheduler
|
|
50
56
|
self.metrics = metrics
|
|
51
57
|
self.webtransport_governance = webtransport_governance
|
|
58
|
+
self.connection_inventory = connection_inventory
|
|
59
|
+
self.congestion_controller_factory = congestion_controller_factory
|
|
60
|
+
self.congestion_controller_options = dict(congestion_controller_options or {})
|
|
52
61
|
self.sessions: dict[tuple[str, int], HTTP3Session] = {}
|
|
53
62
|
self.sessions_by_local_cid: dict[bytes, HTTP3Session] = {}
|
|
54
63
|
self._session_sequence = 0
|
|
55
|
-
self._lock =
|
|
64
|
+
self._lock = PriorityLock()
|
|
56
65
|
self.webtransport_trace: list[dict[str, object]] = []
|
|
57
66
|
|
|
58
67
|
|
|
@@ -47,7 +47,7 @@ class _CustomQuicSend:
|
|
|
47
47
|
raise RuntimeError(f'unexpected custom quic send event: {typ!r}')
|
|
48
48
|
data = bytes(message.get('data', b''))
|
|
49
49
|
fin = not bool(message.get('more_data', False))
|
|
50
|
-
self.messages.
|
|
50
|
+
self.messages.extend(self.session.quic.send_stream_data_packets(self.stream_id, data, fin=fin))
|
|
51
51
|
|
|
52
52
|
def flush(self) -> list[bytes]:
|
|
53
53
|
return list(self.messages)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
4
6
|
from contextlib import suppress
|
|
5
7
|
from dataclasses import dataclass, field
|
|
6
8
|
from typing import Any
|
|
@@ -31,6 +33,7 @@ from tigrcorn_protocols.http3.codec import (
|
|
|
31
33
|
SETTING_MAX_FIELD_SECTION_SIZE,
|
|
32
34
|
SETTING_QPACK_MAX_TABLE_CAPACITY,
|
|
33
35
|
SETTING_WT_MAX_SESSIONS,
|
|
36
|
+
SETTING_WT_ENABLED,
|
|
34
37
|
HTTP3ConnectionError,
|
|
35
38
|
HTTP3StreamError,
|
|
36
39
|
encode_frame,
|
|
@@ -42,7 +45,10 @@ from tigrcorn_protocols.http3.streams import (
|
|
|
42
45
|
HTTP3ConnectionCore,
|
|
43
46
|
)
|
|
44
47
|
from tigrcorn_protocols.http3.websocket import H3WebSocketSession
|
|
48
|
+
from tigrcorn_protocols.sessions import RuntimeConnectionInventory, peer_id_from_address
|
|
45
49
|
from tigrcorn_protocols.webtransport.governance import WebTransportGovernanceError
|
|
50
|
+
from tigrcorn_protocols.webtransport.negotiation import negotiate_profiles, settings_for_profiles
|
|
51
|
+
from tigrcorn_protocols.webtransport.profiles import conflicting_request_profile, missing_peer_requirement, missing_request_requirement, profile_spec
|
|
46
52
|
from tigrcorn_transports.quic.connection import QuicConnection
|
|
47
53
|
from tigrcorn_transports.quic.handshake import QuicTlsHandshakeDriver, TransportParameters
|
|
48
54
|
from tigrcorn_transports.quic.packets import QuicLongHeaderPacket, QuicLongHeaderType, QuicRetryPacket, QuicShortHeaderPacket, QuicVersionNegotiationPacket, decode_packet
|
|
@@ -25,6 +25,62 @@ class HTTP3LifecycleMixin:
|
|
|
25
25
|
def _webtransport_address(addr: tuple[str, int]) -> str:
|
|
26
26
|
return f"{addr[0]}:{addr[1]}"
|
|
27
27
|
|
|
28
|
+
def _connection_id_for_session(self, session: HTTP3Session) -> str:
|
|
29
|
+
return f"conn:h3:{session.runtime_id or session.quic.local_cid.hex() or 'session'}"
|
|
30
|
+
|
|
31
|
+
def _listener_id(self) -> str:
|
|
32
|
+
return self.listener.label or f"{self.listener.kind}:{self.listener.host}:{self.listener.port}"
|
|
33
|
+
|
|
34
|
+
def _register_h3_connection(self, session: HTTP3Session) -> None:
|
|
35
|
+
inventory = self.connection_inventory
|
|
36
|
+
if inventory is None:
|
|
37
|
+
return
|
|
38
|
+
address = self._webtransport_address(session.addr)
|
|
39
|
+
handshake = session.quic.handshake_driver
|
|
40
|
+
inventory.open_connection(
|
|
41
|
+
self._connection_id_for_session(session),
|
|
42
|
+
transport='quic',
|
|
43
|
+
protocols=tuple(sorted(self.listener.enabled_protocols)),
|
|
44
|
+
listener_id=self._listener_id(),
|
|
45
|
+
peer_id=peer_id_from_address(address),
|
|
46
|
+
remote_address=address,
|
|
47
|
+
local_address=self.listener.label,
|
|
48
|
+
security={
|
|
49
|
+
'alpn': getattr(handshake, 'selected_alpn', None) if handshake is not None else None,
|
|
50
|
+
'local_cid': session.quic.local_cid.hex(),
|
|
51
|
+
'remote_cid': session.quic.remote_cid.hex() if isinstance(session.quic.remote_cid, bytes) else None,
|
|
52
|
+
'tls': bool(handshake is not None),
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def _update_h3_connection(self, session: HTTP3Session) -> None:
|
|
57
|
+
inventory = self.connection_inventory
|
|
58
|
+
if inventory is None:
|
|
59
|
+
return
|
|
60
|
+
inventory.update_connection(
|
|
61
|
+
self._connection_id_for_session(session),
|
|
62
|
+
remote_address=self._webtransport_address(session.addr),
|
|
63
|
+
counters={
|
|
64
|
+
'bytes_received': session.bytes_received,
|
|
65
|
+
'bytes_sent': session.bytes_sent,
|
|
66
|
+
'requests': len(session.responded_streams),
|
|
67
|
+
'streams': len(session.h3.requests) + len(session.webtransport_streams),
|
|
68
|
+
'webtransport_sessions': len(session.webtransport_sessions),
|
|
69
|
+
'websocket_sessions': len(session.websocket_sessions),
|
|
70
|
+
},
|
|
71
|
+
security={
|
|
72
|
+
'local_cid': session.quic.local_cid.hex(),
|
|
73
|
+
'remote_cid': session.quic.remote_cid.hex() if isinstance(session.quic.remote_cid, bytes) else None,
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def _close_h3_connection(self, session: HTTP3Session, *, reason: str) -> None:
|
|
78
|
+
inventory = self.connection_inventory
|
|
79
|
+
if inventory is None:
|
|
80
|
+
return
|
|
81
|
+
self._update_h3_connection(session)
|
|
82
|
+
inventory.close_connection(self._connection_id_for_session(session), reason=reason)
|
|
83
|
+
|
|
28
84
|
def _webtransport_budget_snapshot(self) -> dict[str, Any] | None:
|
|
29
85
|
manager = self.webtransport_governance
|
|
30
86
|
if manager is None:
|
|
@@ -37,16 +93,29 @@ class HTTP3LifecycleMixin:
|
|
|
37
93
|
return None
|
|
38
94
|
return manager.open_session(
|
|
39
95
|
webtransport.session_id,
|
|
40
|
-
peer_id=self._webtransport_address(session.addr),
|
|
96
|
+
peer_id=peer_id_from_address(self._webtransport_address(session.addr)),
|
|
41
97
|
address=self._webtransport_address(session.addr),
|
|
42
98
|
)
|
|
43
99
|
|
|
44
100
|
def _webtransport_register_stream(self, webtransport: _HTTP3WebTransportSession, stream_id: int | str) -> dict[str, Any] | None:
|
|
101
|
+
if self.connection_inventory is not None:
|
|
102
|
+
self.connection_inventory.update_session(
|
|
103
|
+
webtransport.session_id,
|
|
104
|
+
stream_ids=(str(stream_id),),
|
|
105
|
+
)
|
|
45
106
|
manager = self.webtransport_governance
|
|
46
107
|
if manager is None:
|
|
47
108
|
return None
|
|
48
109
|
return manager.open_stream(webtransport.session_id, str(stream_id))
|
|
49
110
|
|
|
111
|
+
def _webtransport_release_stream(
|
|
112
|
+
self, webtransport: _HTTP3WebTransportSession, stream_id: int | str
|
|
113
|
+
) -> dict[str, Any] | None:
|
|
114
|
+
manager = self.webtransport_governance
|
|
115
|
+
if manager is None:
|
|
116
|
+
return None
|
|
117
|
+
return manager.close_stream(webtransport.session_id, str(stream_id))
|
|
118
|
+
|
|
50
119
|
def _webtransport_register_datagram(
|
|
51
120
|
self,
|
|
52
121
|
webtransport: _HTTP3WebTransportSession,
|
|
@@ -107,6 +176,13 @@ class HTTP3LifecycleMixin:
|
|
|
107
176
|
max_streams_uni=self.config.scheduler.max_streams or 128,
|
|
108
177
|
idle_timeout=int(self.config.quic.idle_timeout * 1000),
|
|
109
178
|
max_datagram_frame_size=self.listener.max_datagram_size,
|
|
179
|
+
reset_stream_at=(
|
|
180
|
+
'webtransport' in self.listener.enabled_protocols
|
|
181
|
+
and any(
|
|
182
|
+
profile_spec(name, max_sessions=int(self.config.webtransport.max_sessions or 1)).requires_reset_stream_at
|
|
183
|
+
for name in self.config.webtransport.profiles
|
|
184
|
+
)
|
|
185
|
+
),
|
|
110
186
|
)
|
|
111
187
|
session.quic.configure_handshake(
|
|
112
188
|
QuicTlsHandshakeDriver(
|