foghttp 0.3.7__tar.gz → 0.3.8__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.
- {foghttp-0.3.7 → foghttp-0.3.8}/Cargo.lock +15 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/Cargo.toml +3 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/PKG-INFO +11 -2
- {foghttp-0.3.7 → foghttp-0.3.8}/README.md +10 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/index.md +14 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/limitations.md +18 -3
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/policy-hooks.md +6 -5
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/proxies.md +13 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/pyo3-boundary.md +5 -3
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/request-builder.md +4 -3
- foghttp-0.3.8/docs/retries.md +224 -0
- foghttp-0.3.8/docs/ssrf.md +132 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/telemetry.md +13 -5
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/timeouts.md +14 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/upload-types.md +3 -3
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/use-cases.md +3 -2
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/README.md +6 -0
- foghttp-0.3.8/examples/retry_policy.py +41 -0
- foghttp-0.3.8/examples/ssrf_policy.py +30 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/__init__.py +17 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/config.py +5 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/options.py +3 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/raw/errors.py +18 -2
- foghttp-0.3.8/foghttp/_client/raw/lifecycle.py +108 -0
- foghttp-0.3.8/foghttp/_client/raw/ssrf_errors.py +32 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/raw/timeout_errors.py +1 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/response.py +38 -27
- foghttp-0.3.8/foghttp/_client/retry.py +45 -0
- foghttp-0.3.8/foghttp/_client/retry_trace_mapping.py +89 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/dispatcher.py +14 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/emission.py +13 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/request_context.py +31 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/request_events.py +7 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/responses.py +12 -0
- foghttp-0.3.8/foghttp/_client/telemetry/retries.py +19 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_foghttp.pyi +51 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_telemetry.py +1 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/runtime.py +2 -8
- foghttp-0.3.8/foghttp/_validation/ssrf.py +100 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/async_client.py +9 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/client.py +9 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/errors/__init__.py +5 -1
- foghttp-0.3.8/foghttp/errors/base.py +25 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/errors/response.py +14 -1
- foghttp-0.3.8/foghttp/errors/ssrf.py +32 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/policy.py +5 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/response.py +17 -2
- foghttp-0.3.8/foghttp/retry.py +135 -0
- foghttp-0.3.8/foghttp/retry_trace.py +35 -0
- foghttp-0.3.8/foghttp/ssrf.py +91 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/base.py +11 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/status.py +12 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/telemetry/__init__.py +10 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/telemetry/events.py +21 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/timeout_diagnostics.py +1 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/pyproject.toml +1 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/mod.rs +9 -2
- foghttp-0.3.8/src/core/client/ssrf.rs +284 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/error.rs +10 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/mod.rs +4 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/pipeline.rs +50 -2
- foghttp-0.3.8/src/core/policy/retry/tests.rs +253 -0
- foghttp-0.3.8/src/core/policy/retry.rs +160 -0
- foghttp-0.3.8/src/core/policy/ssrf.rs +749 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/tests.rs +3 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/url/mod.rs +8 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/errors.rs +4 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/mod.rs +66 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/parts.rs +2 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/response.rs +21 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/timeout_diagnostics.rs +2 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/body.rs +11 -0
- foghttp-0.3.8/src/py/client/transport/buffered.rs +163 -0
- foghttp-0.3.8/src/py/client/transport/errors.rs +81 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/mod.rs +1 -0
- foghttp-0.3.8/src/py/client/transport/request.rs +601 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/response.rs +16 -1
- foghttp-0.3.8/src/py/client/transport/retry.rs +93 -0
- foghttp-0.3.8/src/py/client/transport/streaming.rs +198 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/tests.rs +7 -5
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/upload_body.rs +47 -21
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/mod.rs +4 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/response.rs +22 -0
- foghttp-0.3.8/src/py/retry.rs +343 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/helpers.py +2 -0
- foghttp-0.3.8/tests/client_options/raw_options.py +39 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_options/test_raw_numeric_boundary.py +15 -45
- foghttp-0.3.8/tests/client_options/test_raw_retry_boundary.py +59 -0
- foghttp-0.3.8/tests/client_options/test_raw_ssrf_boundary.py +170 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/client_options.py +2 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_http_proxy_transport.py +6 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_https_proxy_connect.py +17 -17
- foghttp-0.3.8/tests/client_retry/assertions.py +36 -0
- foghttp-0.3.8/tests/client_retry/conftest.py +11 -0
- foghttp-0.3.8/tests/client_retry/constants.py +18 -0
- foghttp-0.3.8/tests/client_retry/models.py +19 -0
- foghttp-0.3.8/tests/client_retry/server.py +339 -0
- foghttp-0.3.8/tests/client_retry/sources.py +114 -0
- foghttp-0.3.8/tests/client_retry/test_async_retry.py +181 -0
- foghttp-0.3.8/tests/client_retry/test_attempt_trace.py +528 -0
- foghttp-0.3.8/tests/client_retry/test_policy_config.py +114 -0
- foghttp-0.3.8/tests/client_retry/test_sync_retry.py +506 -0
- foghttp-0.3.8/tests/client_ssrf/__init__.py +1 -0
- foghttp-0.3.8/tests/client_ssrf/test_async_ssrf.py +70 -0
- foghttp-0.3.8/tests/client_ssrf/test_policy_config.py +124 -0
- foghttp-0.3.8/tests/client_ssrf/test_sync_ssrf.py +210 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_event_contract.py +4 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/test_async_tls.py +1 -2
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/test_sync_tls.py +1 -2
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/test_tls_config.py +1 -3
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_upload/test_upload_helpers.py +1 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/test_async_fault_injection.py +7 -5
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/test_sync_fault_injection.py +7 -5
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/test_sync_socket_shutdown.py +1 -1
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/network_errors/test_async_network_errors.py +5 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/network_errors/test_sync_network_errors.py +5 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/test_async_future_boundary.py +2 -2
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/test_raw_client_signatures.py +9 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/transport_stats.py +10 -4
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_public_api.py +10 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_raw_client.py +20 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_raw_client_errors.py +75 -0
- foghttp-0.3.8/tests/test_transport_stats_waiters.py +52 -0
- foghttp-0.3.8/tests/types/__init__.py +0 -0
- foghttp-0.3.8/tests/types/test_policy_contracts.py +91 -0
- foghttp-0.3.8/tests/types/test_retry_trace_contracts.py +87 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/uv.lock +1 -1
- foghttp-0.3.7/foghttp/_client/raw/lifecycle.py +0 -45
- foghttp-0.3.7/foghttp/errors/base.py +0 -9
- foghttp-0.3.7/src/py/client/transport/buffered.rs +0 -108
- foghttp-0.3.7/src/py/client/transport/errors.rs +0 -31
- foghttp-0.3.7/src/py/client/transport/request.rs +0 -302
- foghttp-0.3.7/src/py/client/transport/streaming.rs +0 -156
- foghttp-0.3.7/tests/types/test_policy_contracts.py +0 -40
- {foghttp-0.3.7 → foghttp-0.3.8}/.flake8 +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/.github/workflows/ci.yml +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/.github/workflows/release.yml +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/.gitignore +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/.pre-commit-config.yaml +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/CODE_OF_CONDUCT.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/CONTRIBUTING.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/LICENSE +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/SECURITY.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/benchmarks.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/lifecycle.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/packaging.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/quickstart.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/redirects.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/streaming.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/docs/tls.md +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/async_json_fanout.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/async_lifecycle_debug.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/async_resource_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/async_streaming.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/compressed_response.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/http_proxy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/multipart_uploads.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/prepared_requests.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/request_builder_compatibility.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/sync_json_api.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/sync_streaming.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/examples/telemetry_hooks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/asyncio_futures.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/core.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/lifecycle_debug.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/process.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/auth.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/environment.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/no_proxy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/no_proxy_rule.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/no_proxy_tokens.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/resolver.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/transport_policy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/proxy/url_parsing.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/raw/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/raw/requests.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/builder.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/defaults.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/header_policy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/merge.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/request_builder/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/runtime/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/runtime/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/runtime/mode.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/runtime/validation.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/runtime/workers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/stats.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/stream_context.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/clock.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/telemetry/url.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/tls.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/transport.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/transport_requests.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_client/transport_snapshot_mapping.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/content_type.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/encoding.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/fields.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/file_parts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/iterators.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/length.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/normalize.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/parts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/stream.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_multipart/values.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_redaction.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_request_body.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_response/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_response/encoding.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_response/status.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/text/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/text/async_chunks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/text/iterators.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/text/lines.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_streaming/text/sync_chunks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/async_sending.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/chunks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/feeders.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/file_source.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/normalize.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/predicates.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_upload_body/thread_bridge.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_validation/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/_validation/numeric.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/body.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/errors/lifecycle.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/errors/timeout.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/headers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/lifecycle_debug.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/messages.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/methods.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/pool_diagnostics.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/py.typed +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/request.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/request_extensions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/request_info.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stats.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/status_codes/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/status_codes/client_error.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/status_codes/redirect.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/status_codes/server_error.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/status_codes/success.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/async_response.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/bindings.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/lifecycle_debug.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/sync_response.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/stream_response/telemetry.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/telemetry/config.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/telemetry/errors.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/telemetry/sinks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/timeouts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/tls.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/transport_state.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/transport_stats.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/types/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/types/http.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/types/multipart.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/types/request.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/types/streams.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/foghttp/url.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/logo.png +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/ruff.toml +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/scripts/check_all_position.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/scripts/install_wheel_smoke.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/body.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/connection_limit.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/authorization.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/endpoint.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/http.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/proxy/tunnel.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/telemetry/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/telemetry/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/client/write_timeout.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/headers/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/headers/request.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/headers/response.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/headers/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/method.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/atomic.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/buffered.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/counters.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/lifecycle.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/blocking.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/metrics.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/registry.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/snapshots.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/origin/waiters.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/snapshots.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/telemetry.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/metrics/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/numeric/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/numeric/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/proxy.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/action.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/headers.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/method.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/policy.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/status.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/redirect/utils.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/policy/request.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/request.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/response/body.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/response/budget.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/response/decompression.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/response/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/tls/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/tls/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/url/constants.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/url/origin.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/url/scheme.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/core/url/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/lib.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/messages.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/diagnostics.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/gate.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/origin.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/pending.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/permit.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/telemetry.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/acquire/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/active.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/callback.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/registry.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/spawn.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/async_requests/stream_spawn.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/future.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/lifecycle/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/lifecycle/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/options.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/policy_hooks.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/process.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/runtime/constants.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/runtime/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/runtime/shared.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/runtime/tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/runtime/workers.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/callback.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/constants.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/mod.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/read.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/registry.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/state.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/streams/state_tests.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/client.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/client/transport/context.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/pool_diagnostics.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/stats.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/transport_state.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/src/py/url.rs +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/lifecycle_debug_actions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/lifecycle_debug_assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/lifecycle_debug_data.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/lifecycle_debug_predicates.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_cancellation.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_lifecycle_debug_buffered.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_lifecycle_debug_messages.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_lifecycle_debug_streaming.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_lifecycle_debug_strict.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_async_telemetry.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_cancellation/test_asyncio_helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/test_async_keepalive.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_keepalive/test_sync_keepalive.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/fork_actions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_async_client_lifecycle.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_fork_close_safety.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_fork_safety.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_fork_stream_safety.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_raw_client_lifecycle.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_sync_client_lifecycle.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_sync_close_race.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_lifecycle/test_sync_close_waits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/sources.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/test_async_multipart.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/test_multipart_internals.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/test_request_builder.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_multipart/test_sync_multipart.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_options/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_options/test_numeric_validation.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_policy_hooks/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_policy_hooks/requests.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_policy_hooks/test_async_hooks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_policy_hooks/test_contract.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_policy_hooks/test_sync_hooks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/connect_proxy_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/environment.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/http_proxy_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_client_config.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_environment.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_no_proxy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_proxy/test_proxy_models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/test_async_query.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/test_async_query_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/test_request_builder.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/test_sync_query.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_query/test_sync_query_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_connection_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_global_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_origin_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_pool_diagnostics.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_response_body_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_async_transport_state.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_diagnostic_snapshot_contract.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_connection_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_global_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_origin_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_pool_diagnostics.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_response_body_limits.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_resources/test_sync_transport_state.py +0 -0
- {foghttp-0.3.7/tests/client_telemetry → foghttp-0.3.8/tests/client_retry}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/stream_readers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/test_async_streaming.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/test_async_streaming_text_lines.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/test_streaming_text_decoding.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/test_sync_streaming.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/test_sync_streaming_text_lines.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_streaming/text_decoding_sources.py +0 -0
- {foghttp-0.3.7/tests/client_timeouts → foghttp-0.3.8/tests/client_telemetry}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_async_stream_hooks.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_async_stream_telemetry.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_async_telemetry.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_concurrent_delivery.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_sync_stream_telemetry.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_telemetry/test_sync_telemetry.py +0 -0
- {foghttp-0.3.7/tests/client_tls → foghttp-0.3.8/tests/client_timeouts}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_timeouts/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_timeouts/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_timeouts/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_timeouts/test_async_timeouts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_timeouts/test_sync_timeouts.py +0 -0
- {foghttp-0.3.7/tests/client_upload → foghttp-0.3.8/tests/client_tls}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/certificates.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/handshake_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/test_async_tls_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_tls/test_sync_tls_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_transport/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_transport/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_transport/test_transport_adapter.py +0 -0
- {foghttp-0.3.7/tests/fault_injection → foghttp-0.3.8/tests/client_upload}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_upload/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_upload/test_streaming_upload.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/client_warning_actions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/conftest.py +0 -0
- {foghttp-0.3.7/tests/network_errors → foghttp-0.3.8/tests/fault_injection}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/models.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/protocol.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/responses.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/state.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/state_assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/test_async_socket_shutdown.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/timeout_assertions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/fault_injection/transport_waiters.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/http_body_scenarios.py +0 -0
- {foghttp-0.3.7/tests/request_builder → foghttp-0.3.8/tests/network_errors}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/network_errors/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/network_errors/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/network_errors/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/async_future_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/gil_progress.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/test_sync_gil_boundary.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/pyo3_boundary/thread_worker.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/redirect_helpers.py +0 -0
- {foghttp-0.3.7/tests/response_decompression → foghttp-0.3.8/tests/request_builder}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/encoding_property_helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_base_url.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_body.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_default_headers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_default_params.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_encoding_properties.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_extensions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_header_policy.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_headers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_merge_contract.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_no_mutation.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_parity.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_query_params.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_builder/test_shortcuts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/request_factories.py +0 -0
- {foghttp-0.3.7/tests/types → foghttp-0.3.8/tests/response_decompression}/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/conftest.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/constants.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/helpers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/payloads.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/test_async_response_decompression.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/response_decompression/test_sync_response_decompression.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/__init__.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/async_http_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/http_routes.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/raw_responses.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/sync_http_server.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/timeout_diagnostics.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/support/transport_state.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_async_client.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_async_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_async_response.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_client_options.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_headers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_query_params.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_redaction.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_request_extensions.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_request_model.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_response_encoding.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_response_flags.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_runtime_workers.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_sync_client.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_sync_redirects.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_sync_response.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_transport_stats.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/test_url.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/types/test_request_extensions_contracts.py +0 -0
- {foghttp-0.3.7 → foghttp-0.3.8}/tests/types/test_upload_contracts.py +0 -0
|
@@ -92,6 +92,12 @@ dependencies = [
|
|
|
92
92
|
"syn",
|
|
93
93
|
]
|
|
94
94
|
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "fastrand"
|
|
97
|
+
version = "2.4.1"
|
|
98
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
99
|
+
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
|
100
|
+
|
|
95
101
|
[[package]]
|
|
96
102
|
name = "find-msvc-tools"
|
|
97
103
|
version = "0.1.9"
|
|
@@ -110,12 +116,14 @@ dependencies = [
|
|
|
110
116
|
|
|
111
117
|
[[package]]
|
|
112
118
|
name = "foghttp"
|
|
113
|
-
version = "0.3.
|
|
119
|
+
version = "0.3.8"
|
|
114
120
|
dependencies = [
|
|
115
121
|
"brotli",
|
|
116
122
|
"bytes",
|
|
123
|
+
"fastrand",
|
|
117
124
|
"flate2",
|
|
118
125
|
"http-body-util",
|
|
126
|
+
"httpdate",
|
|
119
127
|
"hyper",
|
|
120
128
|
"hyper-rustls",
|
|
121
129
|
"hyper-util",
|
|
@@ -224,6 +232,12 @@ version = "1.10.1"
|
|
|
224
232
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
225
233
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
|
226
234
|
|
|
235
|
+
[[package]]
|
|
236
|
+
name = "httpdate"
|
|
237
|
+
version = "1.0.3"
|
|
238
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
239
|
+
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|
240
|
+
|
|
227
241
|
[[package]]
|
|
228
242
|
name = "hyper"
|
|
229
243
|
version = "1.10.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "foghttp"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.8"
|
|
4
4
|
description = "Observable Rust-powered HTTP client for Python services."
|
|
5
5
|
edition = "2021"
|
|
6
6
|
homepage = "https://github.com/AmberFog/foghttp"
|
|
@@ -20,8 +20,10 @@ extension-module = ["pyo3/extension-module"]
|
|
|
20
20
|
[dependencies]
|
|
21
21
|
brotli = "8.0.4"
|
|
22
22
|
bytes = "1.12.0"
|
|
23
|
+
fastrand = "2.3.0"
|
|
23
24
|
flate2 = "1.1.9"
|
|
24
25
|
http-body-util = "0.1.3"
|
|
26
|
+
httpdate = "1.0.3"
|
|
25
27
|
url = "2.5.8"
|
|
26
28
|
|
|
27
29
|
[dependencies.hyper]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: foghttp
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Framework :: AsyncIO
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -74,6 +74,8 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
74
74
|
- typed telemetry event hooks with redacted request/response lifecycle events
|
|
75
75
|
- versioned telemetry snapshots that separate alert-oriented stats from
|
|
76
76
|
diagnostic dump APIs
|
|
77
|
+
- opt-in Rust-owned retries with replayability gates and immutable attempt
|
|
78
|
+
traces, plus opt-in per-hop and post-DNS SSRF destination controls
|
|
77
79
|
- opt-in async lifecycle debug snapshots for staging and tests
|
|
78
80
|
- lazy process-wide shared Tokio runtime by default, opt-in dedicated runtime
|
|
79
81
|
tuning, and fail-closed client ownership across `fork()`
|
|
@@ -170,6 +172,10 @@ async with foghttp.AsyncClient() as client:
|
|
|
170
172
|
response body, and request completion lifecycle
|
|
171
173
|
- opt-in typed transport policy hooks for lightweight request admission and
|
|
172
174
|
response-head checks without default-path Python callbacks
|
|
175
|
+
- opt-in Rust-owned retry policy for selected statuses and pre-header network
|
|
176
|
+
failures, with safe methods, replayable bodies, and immutable attempt traces
|
|
177
|
+
- opt-in Rust-owned SSRF destination policy with per-hop allowlists,
|
|
178
|
+
post-resolution IP checks, and DNS rebinding mitigation
|
|
173
179
|
- versioned telemetry snapshot metadata for `stats()`, `dump_transport_state()`,
|
|
174
180
|
and `dump_pool_diagnostics()`
|
|
175
181
|
- opt-in async lifecycle debug mode for active request snapshots, strict leak
|
|
@@ -189,6 +195,8 @@ async with foghttp.AsyncClient() as client:
|
|
|
189
195
|
- [Timeout model](https://github.com/AmberFog/foghttp/blob/main/docs/timeouts.md)
|
|
190
196
|
- [Upload typing contracts](https://github.com/AmberFog/foghttp/blob/main/docs/upload-types.md)
|
|
191
197
|
- [Transport policy hooks](https://github.com/AmberFog/foghttp/blob/main/docs/policy-hooks.md)
|
|
198
|
+
- [Retry policy](https://github.com/AmberFog/foghttp/blob/main/docs/retries.md)
|
|
199
|
+
- [SSRF protection](https://github.com/AmberFog/foghttp/blob/main/docs/ssrf.md)
|
|
192
200
|
- [Telemetry contract](https://github.com/AmberFog/foghttp/blob/main/docs/telemetry.md)
|
|
193
201
|
- [Response streaming](https://github.com/AmberFog/foghttp/blob/main/docs/streaming.md)
|
|
194
202
|
- [Proxy and trust_env](https://github.com/AmberFog/foghttp/blob/main/docs/proxies.md)
|
|
@@ -207,7 +215,8 @@ available as bytes/text/line context-managed APIs. Streaming `content=` uploads
|
|
|
207
215
|
and multipart `files=` uploads are available with explicit replayability and
|
|
208
216
|
cleanup rules. HTTP proxy routing and HTTPS proxy `CONNECT` tunnelling are
|
|
209
217
|
available through `proxy=` and `trust_env=True` when the proxy endpoint itself
|
|
210
|
-
uses `http://`.
|
|
218
|
+
uses `http://`. Proxy-routed requests fail closed when `SSRFPolicy` is enabled
|
|
219
|
+
because the client cannot prove which target address a remote proxy resolves.
|
|
211
220
|
Cookies, auth helpers, HTTP/2, automatic `Accept-Encoding` negotiation,
|
|
212
221
|
streaming decompression, and per-request connect timeout reconfiguration are
|
|
213
222
|
planned for later versions. Physical connection caps currently apply to the
|
|
@@ -31,6 +31,8 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
31
31
|
- typed telemetry event hooks with redacted request/response lifecycle events
|
|
32
32
|
- versioned telemetry snapshots that separate alert-oriented stats from
|
|
33
33
|
diagnostic dump APIs
|
|
34
|
+
- opt-in Rust-owned retries with replayability gates and immutable attempt
|
|
35
|
+
traces, plus opt-in per-hop and post-DNS SSRF destination controls
|
|
34
36
|
- opt-in async lifecycle debug snapshots for staging and tests
|
|
35
37
|
- lazy process-wide shared Tokio runtime by default, opt-in dedicated runtime
|
|
36
38
|
tuning, and fail-closed client ownership across `fork()`
|
|
@@ -127,6 +129,10 @@ async with foghttp.AsyncClient() as client:
|
|
|
127
129
|
response body, and request completion lifecycle
|
|
128
130
|
- opt-in typed transport policy hooks for lightweight request admission and
|
|
129
131
|
response-head checks without default-path Python callbacks
|
|
132
|
+
- opt-in Rust-owned retry policy for selected statuses and pre-header network
|
|
133
|
+
failures, with safe methods, replayable bodies, and immutable attempt traces
|
|
134
|
+
- opt-in Rust-owned SSRF destination policy with per-hop allowlists,
|
|
135
|
+
post-resolution IP checks, and DNS rebinding mitigation
|
|
130
136
|
- versioned telemetry snapshot metadata for `stats()`, `dump_transport_state()`,
|
|
131
137
|
and `dump_pool_diagnostics()`
|
|
132
138
|
- opt-in async lifecycle debug mode for active request snapshots, strict leak
|
|
@@ -146,6 +152,8 @@ async with foghttp.AsyncClient() as client:
|
|
|
146
152
|
- [Timeout model](https://github.com/AmberFog/foghttp/blob/main/docs/timeouts.md)
|
|
147
153
|
- [Upload typing contracts](https://github.com/AmberFog/foghttp/blob/main/docs/upload-types.md)
|
|
148
154
|
- [Transport policy hooks](https://github.com/AmberFog/foghttp/blob/main/docs/policy-hooks.md)
|
|
155
|
+
- [Retry policy](https://github.com/AmberFog/foghttp/blob/main/docs/retries.md)
|
|
156
|
+
- [SSRF protection](https://github.com/AmberFog/foghttp/blob/main/docs/ssrf.md)
|
|
149
157
|
- [Telemetry contract](https://github.com/AmberFog/foghttp/blob/main/docs/telemetry.md)
|
|
150
158
|
- [Response streaming](https://github.com/AmberFog/foghttp/blob/main/docs/streaming.md)
|
|
151
159
|
- [Proxy and trust_env](https://github.com/AmberFog/foghttp/blob/main/docs/proxies.md)
|
|
@@ -164,7 +172,8 @@ available as bytes/text/line context-managed APIs. Streaming `content=` uploads
|
|
|
164
172
|
and multipart `files=` uploads are available with explicit replayability and
|
|
165
173
|
cleanup rules. HTTP proxy routing and HTTPS proxy `CONNECT` tunnelling are
|
|
166
174
|
available through `proxy=` and `trust_env=True` when the proxy endpoint itself
|
|
167
|
-
uses `http://`.
|
|
175
|
+
uses `http://`. Proxy-routed requests fail closed when `SSRFPolicy` is enabled
|
|
176
|
+
because the client cannot prove which target address a remote proxy resolves.
|
|
168
177
|
Cookies, auth helpers, HTTP/2, automatic `Accept-Encoding` negotiation,
|
|
169
178
|
streaming decompression, and per-request connect timeout reconfiguration are
|
|
170
179
|
planned for later versions. Physical connection caps currently apply to the
|
|
@@ -4,7 +4,7 @@ layout: "home"
|
|
|
4
4
|
hero:
|
|
5
5
|
name: "FogHTTP"
|
|
6
6
|
text: "Rust-powered HTTP client for Python"
|
|
7
|
-
tagline: "Buffered JSON and form requests, streaming and multipart uploads, sync and async response streaming, transparent response decoding, base URL clients, default headers and params,
|
|
7
|
+
tagline: "Buffered JSON and form requests, streaming and multipart uploads, sync and async response streaming, transparent response decoding, base URL clients, default headers and params, opt-in safe retries and SSRF destination controls, redirects, custom CA certificates, cancellation, and observable request limits with pool diagnostics."
|
|
8
8
|
|
|
9
9
|
features:
|
|
10
10
|
- title: "Rust transport"
|
|
@@ -62,6 +62,10 @@ FogHTTP is designed around a few engineering priorities:
|
|
|
62
62
|
events
|
|
63
63
|
- opt-in typed transport policy hooks for lightweight request admission and
|
|
64
64
|
response-head checks, with Rust-owned redirect safety
|
|
65
|
+
- opt-in Rust-owned retry policy with safe-method defaults, replayability
|
|
66
|
+
gating, bounded backoff, typed decisions, and immutable attempt traces
|
|
67
|
+
- opt-in Rust-owned SSRF destination policy with per-hop allowlists,
|
|
68
|
+
post-resolution IP checks, and DNS rebinding mitigation
|
|
65
69
|
- versioned telemetry snapshots that separate alert-oriented stats from
|
|
66
70
|
diagnostic dump APIs
|
|
67
71
|
- opt-in async lifecycle debug snapshots for tests, staging, and incident
|
|
@@ -79,6 +83,8 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
79
83
|
- [Timeout model](./timeouts.md)
|
|
80
84
|
- [Upload typing contracts](./upload-types.md)
|
|
81
85
|
- [Transport policy hooks](./policy-hooks.md)
|
|
86
|
+
- [Retry policy](./retries.md)
|
|
87
|
+
- [SSRF protection](./ssrf.md)
|
|
82
88
|
- [Telemetry contract](./telemetry.md)
|
|
83
89
|
- [Response streaming](./streaming.md)
|
|
84
90
|
- [TLS trust](./tls.md)
|
|
@@ -98,6 +104,9 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
98
104
|
- cancellable buffered async requests
|
|
99
105
|
- bounded sync and async response streaming
|
|
100
106
|
- prepared requests that can be inspected before sending
|
|
107
|
+
- opt-in safe retries with request-scoped attempt diagnostics
|
|
108
|
+
- direct service clients that need an opt-in SSRF guard for partially trusted
|
|
109
|
+
destination URLs
|
|
101
110
|
- simple benchmarks against other buffered HTTP clients
|
|
102
111
|
|
|
103
112
|
## Key Features
|
|
@@ -136,6 +145,10 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
136
145
|
response body, and request completion lifecycle
|
|
137
146
|
- opt-in typed transport policy hooks with immutable request/response views and
|
|
138
147
|
no default-path Python callback
|
|
148
|
+
- opt-in retry policy for selected statuses and pre-header network failures,
|
|
149
|
+
with safe methods, replayable bodies, and immutable attempt traces
|
|
150
|
+
- opt-in SSRF destination policy that validates initial and redirected targets,
|
|
151
|
+
then pins each connection attempt to its checked DNS result
|
|
139
152
|
- versioned telemetry snapshot metadata for `stats()`, `dump_transport_state()`,
|
|
140
153
|
and `dump_pool_diagnostics()`
|
|
141
154
|
- opt-in async lifecycle debug mode for active request snapshots and strict
|
|
@@ -55,6 +55,12 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
55
55
|
response body, and request completion lifecycle
|
|
56
56
|
- opt-in synchronous transport policy hooks with immutable request/response
|
|
57
57
|
snapshots and Rust-owned redirect safety
|
|
58
|
+
- opt-in retry policy for selected statuses and pre-header network failures,
|
|
59
|
+
gated by method safety and request-body replayability, with immutable
|
|
60
|
+
request-scoped attempt traces on responses and terminal `FogHTTPError`
|
|
61
|
+
- opt-in SSRF destination policy with scheme/origin/domain allowlists,
|
|
62
|
+
per-redirect checks, post-resolution address validation, and typed redacted
|
|
63
|
+
`SSRFError` reasons
|
|
58
64
|
- opt-in async lifecycle debug snapshots for active async request handles,
|
|
59
65
|
pending transport pressure, strict test checks, and unclosed-client context
|
|
60
66
|
- default per-response and aggregate buffered response memory limits
|
|
@@ -99,6 +105,8 @@ try to keep public interfaces stable and avoid unnecessary breaking changes.
|
|
|
99
105
|
| socket lifecycle telemetry granularity | `TransportStats` and `dump_transport_state()["origins"]` expose opened, open-failed, closed, reused, aborted, idle-timeout eviction, active, and idle tracked connection counters for the current HTTP/1 path; dedicated failed-reuse and close-reason taxonomy are not exposed yet because current connector hooks do not provide a stable reason signal |
|
|
100
106
|
| telemetry hook granularity | `TelemetryConfig` currently dispatches Python-level request/response lifecycle events; lower-level Rust pool acquire and connection lifecycle event delivery is planned before Prometheus/OpenTelemetry exporters |
|
|
101
107
|
| transport policy hook execution | `TransportPolicyHooks` callbacks are synchronous, inline, non-reentrant, and may run on Rust transport worker threads; `after_response_body` observes only redirect bodies consumed internally, not the final response body returned to the caller |
|
|
108
|
+
| retry scope | Retry is client-level and opt-in. It covers configured response statuses and pre-header `NetworkError` only; response-body errors, FogHTTP timeouts, local upload-provider failures, auth refresh, hedging, and circuit breaking are not retried. |
|
|
109
|
+
| SSRF protection scope | `SSRFPolicy` is client-level and opt-in. It validates initial and redirected destinations plus every resolved address, but it is not a replacement for network egress policy. Proxy routes fail closed because a remote proxy can resolve the target independently. |
|
|
102
110
|
| diagnostic snapshot transactionality | `stats()`, `dump_transport_state()`, and `dump_pool_diagnostics()` include `schema_version` and a monotonic `snapshot_sequence`, but the `dump_*` APIs remain diagnostic snapshots rather than lock-protected SLA transactions; use `stats()` for alert-oriented low-cardinality metrics |
|
|
103
111
|
|
|
104
112
|
## Practical Guidance
|
|
@@ -116,6 +124,8 @@ Use FogHTTP today when:
|
|
|
116
124
|
- async request cancellation and sync/async stream cleanup behavior are useful
|
|
117
125
|
- global/per-origin request-slot backpressure and opt-in global/per-host
|
|
118
126
|
physical connection caps are enough for your resource control needs
|
|
127
|
+
- you accept partially trusted destination URLs and can use direct connections
|
|
128
|
+
with an explicit client-level `SSRFPolicy`
|
|
119
129
|
- you can reuse clients instead of creating many short-lived transport and pool
|
|
120
130
|
instances once requests start flowing
|
|
121
131
|
|
|
@@ -123,6 +133,8 @@ Wait before using FogHTTP when:
|
|
|
123
133
|
|
|
124
134
|
- you need transparent streaming decompression
|
|
125
135
|
- you need SOCKS, PAC, WPAD, or platform proxy discovery
|
|
136
|
+
- you require proxy routing and application-layer SSRF protection on the same
|
|
137
|
+
client; enforce destination policy at the proxy or network egress boundary
|
|
126
138
|
- you rely on cookies across requests
|
|
127
139
|
- you need per-request connect timeout reconfiguration
|
|
128
140
|
- you need automatic compression negotiation instead of manual
|
|
@@ -132,12 +144,15 @@ Wait before using FogHTTP when:
|
|
|
132
144
|
|
|
133
145
|
## Error Surface
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
Pre-header transport failures map to `NetworkError`, a `RequestError` subtype;
|
|
148
|
+
local request-provider and other request failures remain `RequestError`. Pool acquire timeout and
|
|
136
149
|
queue-full conditions map to `PoolTimeout`. Response body progress timeout maps
|
|
137
150
|
to `ReadTimeout` for buffered responses and streamed body chunks. Buffered and
|
|
138
151
|
streamed request body write progress timeout maps to `WriteTimeout`. The broader
|
|
139
152
|
buffered transport deadline maps to the base `TimeoutError` with phase-aware
|
|
140
153
|
diagnostics; for streaming responses it covers acquire, redirects, and response
|
|
141
|
-
headers before the stream is returned.
|
|
142
|
-
|
|
154
|
+
headers before the stream is returned. An enabled SSRF policy raises
|
|
155
|
+
`SSRFError` with a stable `SSRFViolationReason` before a blocked destination is
|
|
156
|
+
sent. Dedicated connect timeout exception mapping is reserved for later timeout
|
|
157
|
+
work. See
|
|
143
158
|
[Timeout model](./timeouts.md) for the current behavior.
|
|
@@ -47,13 +47,14 @@ or replace transport state.
|
|
|
47
47
|
|
|
48
48
|
| Hook | Ordering and scope |
|
|
49
49
|
|---|---|
|
|
50
|
-
| `before_send` | Runs for the initial request and every redirect hop after Rust has selected and validated the transport route, but before request-slot acquire. |
|
|
51
|
-
| `on_response_headers` | Runs for every response after Rust has classified any redirect action and before FogHTTP consumes or returns the response body. |
|
|
50
|
+
| `before_send` | Runs for the initial request, every retry attempt, and every redirect hop after Rust has selected and validated the transport route, but before request-slot acquire. |
|
|
51
|
+
| `on_response_headers` | Runs for every response attempt after Rust has classified any redirect action and before FogHTTP consumes or returns the response body. |
|
|
52
52
|
| `after_response_body` | Runs only for redirect response bodies consumed internally by FogHTTP. Rust first validates the redirect limit, replayability, scheme downgrade, header policy, and proxy boundary; the hook runs before the already validated mutation is applied. It does not run for the final body returned to the caller. |
|
|
53
53
|
|
|
54
|
-
There is deliberately no error, timeout,
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
There is deliberately no public error, timeout, cancellation, or retry-decision
|
|
55
|
+
hook. The built-in retry policy uses a Rust-owned pre-header network-error
|
|
56
|
+
classification and exposes decisions through typed telemetry instead of
|
|
57
|
+
calling Python policy code from failure paths.
|
|
57
58
|
|
|
58
59
|
## Snapshot Contract
|
|
59
60
|
|
|
@@ -7,8 +7,9 @@ For `https://` targets, FogHTTP opens a `CONNECT` tunnel to the proxy and then
|
|
|
7
7
|
performs the TLS handshake against the **target** host over that tunnel, so the
|
|
8
8
|
certificate is validated for the target origin, not the proxy. If the tunnel
|
|
9
9
|
cannot be established — proxy authentication failure, a non-2xx `CONNECT`
|
|
10
|
-
status, or the proxy closing the tunnel early — FogHTTP raises `
|
|
11
|
-
instead of silently falling back to a direct
|
|
10
|
+
status, or the proxy closing the tunnel early — FogHTTP raises `NetworkError`
|
|
11
|
+
(a `RequestError` subtype) instead of silently falling back to a direct
|
|
12
|
+
connection.
|
|
12
13
|
|
|
13
14
|
## Explicit Proxy
|
|
14
15
|
|
|
@@ -61,7 +62,7 @@ Host: api.internal:443
|
|
|
61
62
|
TLS is validated against the **target** host using the same `TLSConfig` rules as
|
|
62
63
|
a direct connection; the proxy never terminates TLS and cannot impersonate the
|
|
63
64
|
target. A failed tunnel (`CONNECT` non-2xx, proxy auth failure, or early close)
|
|
64
|
-
maps to a stable `
|
|
65
|
+
maps to a stable `NetworkError`, releases the request slot, and does not return
|
|
65
66
|
a poisoned connection to the pool. The tunnelled request itself is sent in
|
|
66
67
|
origin-form, exactly like a direct HTTPS request.
|
|
67
68
|
|
|
@@ -193,10 +194,18 @@ FogHTTP intentionally does not expose a `verify=False` compatibility mode and
|
|
|
193
194
|
does not honor environment variables that disable certificate verification.
|
|
194
195
|
Use explicit `TLSConfig` for custom CA bundles and custom-only trust.
|
|
195
196
|
|
|
197
|
+
## SSRF Policy Compatibility
|
|
198
|
+
|
|
199
|
+
Requests that select a proxy route fail closed when `SSRFPolicy` is enabled.
|
|
200
|
+
Forward and `CONNECT` proxies can resolve the target hostname remotely, so the
|
|
201
|
+
client cannot guarantee that its locally checked DNS result is the address
|
|
202
|
+
used by the proxy. See [SSRF protection](./ssrf.md) for the trust boundary and
|
|
203
|
+
recommended network-layer controls.
|
|
204
|
+
|
|
196
205
|
## Not Implemented Yet
|
|
197
206
|
|
|
198
207
|
- SOCKS5/SOCKS5h
|
|
199
208
|
- TLS-to-proxy endpoints (`https://proxy.example:443`)
|
|
200
209
|
- PAC/WPAD or platform/browser proxy discovery
|
|
201
210
|
- per-route proxy policies
|
|
202
|
-
- proxy
|
|
211
|
+
- proxy failover, rotation, or per-route policy
|
|
@@ -73,9 +73,11 @@ seam does not mutate response status, headers, or body content.
|
|
|
73
73
|
Policy diagnostics may include a normalized origin, but must not include URL
|
|
74
74
|
userinfo, path, query parameters, or fragments.
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
The built-in retry stage owns a narrow Rust failure taxonomy: eligible network
|
|
77
|
+
failures occur before response headers and exclude timeout, connection-acquire,
|
|
78
|
+
and user request-body provider errors. Retry decisions remain typed core values;
|
|
79
|
+
core policy code does not accept or return `PyErr`. The PyO3 adapter maps final
|
|
80
|
+
errors and carries structured retry decisions to optional Python telemetry.
|
|
79
81
|
|
|
80
82
|
The PyO3 bridge lives outside `src/core/policy/`. It invokes only callbacks
|
|
81
83
|
resolved when `RawClient` is created, rejects non-`None` callback returns, and
|
|
@@ -64,9 +64,10 @@ sniff the body or invent a media type. `Accept-Query` response headers are
|
|
|
64
64
|
available through the normal case-insensitive `response.headers` interface;
|
|
65
65
|
FogHTTP does not yet parse that Structured Field value.
|
|
66
66
|
|
|
67
|
-
RFC 10008 classifies QUERY as safe and idempotent. FogHTTP
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
RFC 10008 classifies QUERY as safe and idempotent. FogHTTP therefore includes
|
|
68
|
+
it in the default method set of the opt-in [retry policy](./retries.md), but a
|
|
69
|
+
body-bearing QUERY is eligible only when its provider is replayable. FogHTTP
|
|
70
|
+
does not currently provide an HTTP cache. RFC 10008 permits QUERY responses to
|
|
70
71
|
be cached, but a QUERY cache key must incorporate the request content and
|
|
71
72
|
related metadata, including `Content-Type`; a URL-only GET-style cache key is
|
|
72
73
|
not valid for QUERY.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Retry Policy
|
|
2
|
+
|
|
3
|
+
FogHTTP provides an opt-in, client-level retry policy for transient response
|
|
4
|
+
statuses and network failures before response headers arrive. The default
|
|
5
|
+
client does not enable this policy.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import foghttp
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
retry = foghttp.RetryPolicy(
|
|
12
|
+
retries=2,
|
|
13
|
+
backoff=0.1,
|
|
14
|
+
jitter=0.1,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
with foghttp.Client(retry=retry) as client:
|
|
18
|
+
response = client.get("https://api.example.com/items")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`retries` is the number of additional sends allowed by retry decisions across
|
|
22
|
+
one logical request. With no redirects, a policy with `retries=2` can therefore
|
|
23
|
+
make at most three attempts; redirect hops are bounded separately by
|
|
24
|
+
`max_redirects`. The same policy contract is available on `Client` and
|
|
25
|
+
`AsyncClient`, including buffered and streaming response entry points. For a
|
|
26
|
+
streaming response, retries finish before the response object is exposed;
|
|
27
|
+
failures while the caller consumes the returned response body are not retried.
|
|
28
|
+
|
|
29
|
+
## Defaults
|
|
30
|
+
|
|
31
|
+
`RetryPolicy()` uses these defaults:
|
|
32
|
+
|
|
33
|
+
| Option | Default | Meaning |
|
|
34
|
+
|---|---:|---|
|
|
35
|
+
| `retries` | `2` | Additional attempts after the initial send. |
|
|
36
|
+
| `backoff` | `0.1` | Base exponential delay in seconds. |
|
|
37
|
+
| `jitter` | `0.1` | Maximum random delay added to each backoff, in seconds. |
|
|
38
|
+
| `retry_on.statuses` | `429`, `502`, `503`, `504` | Response statuses eligible for retry. |
|
|
39
|
+
| `retry_on.exceptions` | `NetworkError` | Pre-header transport failure eligible for retry. |
|
|
40
|
+
| `methods` | `GET`, `HEAD`, `OPTIONS`, `QUERY`, `TRACE` | Methods eligible for automatic retry. |
|
|
41
|
+
|
|
42
|
+
The delay before retry number `n`, starting at zero, is
|
|
43
|
+
`backoff * 2**n + uniform(0, jitter)`. A valid `Retry-After` delta or HTTP date
|
|
44
|
+
on a retryable response is honored as a minimum delay. Backoff and response
|
|
45
|
+
draining remain inside the request's shared `Timeouts.total` budget, including
|
|
46
|
+
any redirect hops in the same logical request. Malformed `Retry-After` values
|
|
47
|
+
are ignored, and accepted server delays are capped by the same duration ceiling
|
|
48
|
+
as local numeric options.
|
|
49
|
+
|
|
50
|
+
`RetryConditions` makes triggers explicit:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
conditions = foghttp.RetryConditions(
|
|
54
|
+
statuses=(429, 503),
|
|
55
|
+
exceptions=(foghttp.NetworkError,),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
retry = foghttp.RetryPolicy(
|
|
59
|
+
retries=3,
|
|
60
|
+
backoff=0.2,
|
|
61
|
+
jitter=0.05,
|
|
62
|
+
retry_on=conditions,
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Use an empty tuple to disable a trigger category. The current exception
|
|
67
|
+
contract supports `NetworkError` only; local upload-provider failures and
|
|
68
|
+
FogHTTP timeout exceptions are not classified as retryable network failures.
|
|
69
|
+
Response-body failures after headers are also not retried.
|
|
70
|
+
|
|
71
|
+
## Method And Body Safety
|
|
72
|
+
|
|
73
|
+
`POST` is deliberately absent from the default method set. This prevents an
|
|
74
|
+
automatic retry from duplicating a side effect. Applications can explicitly
|
|
75
|
+
replace `methods`, but they then own the endpoint's idempotency guarantee:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
retry = foghttp.RetryPolicy(methods=("GET", "PUT", "POST"))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Method eligibility is necessary but not sufficient. A non-empty request body
|
|
82
|
+
must also be replayable:
|
|
83
|
+
|
|
84
|
+
| Body source | Replayable |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `bytes`, `str`, `json=`, encoded `data=` | Yes |
|
|
87
|
+
| bytes-like multipart parts | Yes |
|
|
88
|
+
| zero-argument stream or multipart factory | Yes, if it returns equivalent fresh content |
|
|
89
|
+
| direct iterator, async iterator, or file-like `content=` | No |
|
|
90
|
+
| direct file or stream multipart part | No |
|
|
91
|
+
|
|
92
|
+
RFC 10008 classifies `QUERY` as safe and idempotent, so it is enabled by
|
|
93
|
+
default. A body-bearing QUERY is still retried only when its provider is
|
|
94
|
+
replayable. If a matching response or network failure occurs with a
|
|
95
|
+
non-replayable body, FogHTTP returns the response or raises the original error
|
|
96
|
+
without a second send and records `block_non_replayable` with reason
|
|
97
|
+
`non_replayable_body`.
|
|
98
|
+
|
|
99
|
+
There is no public `body_replayable=True` escape hatch. Use a zero-argument
|
|
100
|
+
factory when a stream can be recreated safely for every attempt.
|
|
101
|
+
|
|
102
|
+
## Ordering And Resources
|
|
103
|
+
|
|
104
|
+
Retry runs inside the Rust transport policy pipeline. Redirect classification
|
|
105
|
+
has priority for a response that is both a redirect and listed in retry
|
|
106
|
+
statuses. Each new retry attempt reruns route selection and lightweight
|
|
107
|
+
`before_send`/`on_response_headers` policy hooks.
|
|
108
|
+
|
|
109
|
+
Before retrying a status response, FogHTTP drains its body under the configured
|
|
110
|
+
read and total deadlines. A clean keep-alive connection can then return to the
|
|
111
|
+
pool. A connection that fails before headers is not reused. The request slot is
|
|
112
|
+
released before backoff, so sleeping retries do not occupy active transport
|
|
113
|
+
capacity. Cancelling an async request cancels its pending backoff and releases
|
|
114
|
+
the logical request lifecycle.
|
|
115
|
+
|
|
116
|
+
## Observability
|
|
117
|
+
|
|
118
|
+
A request made by a client with `RetryPolicy` exposes one immutable,
|
|
119
|
+
request-scoped `RetryTrace` when transport execution returns a buffered or
|
|
120
|
+
streaming response, or raises a terminal `FogHTTPError`. Responses provide it
|
|
121
|
+
as `response.retry_trace`; terminal `FogHTTPError` instances provide the same
|
|
122
|
+
property. Errors raised before native transport execution are outside this
|
|
123
|
+
contract. A client without a retry policy returns `None`, so the default path
|
|
124
|
+
does not collect attempt history or allocate terminal trace values.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
try:
|
|
128
|
+
response = client.get("https://api.example.com/items")
|
|
129
|
+
except foghttp.FogHTTPError as error:
|
|
130
|
+
trace = error.retry_trace
|
|
131
|
+
else:
|
|
132
|
+
trace = response.retry_trace
|
|
133
|
+
|
|
134
|
+
if trace is not None:
|
|
135
|
+
for attempt in trace.attempts:
|
|
136
|
+
print(attempt.attempt, attempt.decision, attempt.reason)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`RetryTrace` contains:
|
|
140
|
+
|
|
141
|
+
| Field | Meaning |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `attempts` | Ordered immutable tuple, bounded to at most `retries + 1` entries. |
|
|
144
|
+
| `outcome` | `response` when the transport returned a response object, or `error` when it raised. |
|
|
145
|
+
| `status_code` | Final response status, otherwise `None`. |
|
|
146
|
+
| `error_type` | Diagnostic name of the final public exception class, otherwise `None`. |
|
|
147
|
+
| `elapsed` | Total logical-request time in seconds through the returned response or error. |
|
|
148
|
+
|
|
149
|
+
Each `RetryAttempt` represents one retry-policy execution cycle inside the
|
|
150
|
+
native transport, not one socket send or server-observed request. Redirect hops
|
|
151
|
+
remain within the same retry attempt and rerun route selection, the
|
|
152
|
+
`before_send` hook and request-slot/connection acquisition. An attempt can
|
|
153
|
+
therefore end with a policy or acquire error before a connection sends any
|
|
154
|
+
request bytes.
|
|
155
|
+
|
|
156
|
+
An attempt contains the 1-based `attempt`, normalized `method`, credential-free
|
|
157
|
+
`origin`, zero-based `redirect_hop`, optional `status_code` and `error_type`,
|
|
158
|
+
typed `decision` and `reason`, selected `backoff` in seconds, optional cumulative
|
|
159
|
+
`decision_elapsed` seconds when the retry decision was committed by the
|
|
160
|
+
transport, and cumulative `completed_elapsed` seconds when that execution
|
|
161
|
+
attempt ended. All elapsed values are measured from the start of the logical
|
|
162
|
+
request, not from the start of an individual attempt. A buffered attempt can
|
|
163
|
+
contain both a status and a later body-read error. The final retry execution
|
|
164
|
+
attempt is always present. Its `decision` and `reason` are `None` when that
|
|
165
|
+
attempt did not trigger retry policy; a terminal method stop, replayability
|
|
166
|
+
block or exhaustion retains its typed decision and reason.
|
|
167
|
+
|
|
168
|
+
`error_type` follows the corresponding public exception class name and is a
|
|
169
|
+
diagnostic field rather than a separate error-category enum. When class-name
|
|
170
|
+
comparison is needed, prefer `foghttp.NetworkError.__name__` over a repeated
|
|
171
|
+
string literal. `backoff` is the delay selected by policy, not a measurement of
|
|
172
|
+
time actually slept; a timeout or cancellation can end the wait early.
|
|
173
|
+
|
|
174
|
+
A logical request can fail after its latest transport attempt has completed.
|
|
175
|
+
For example, a total timeout during retry backoff is reported by
|
|
176
|
+
`RetryTrace.error_type` and `RetryTrace.elapsed`; it does not overwrite the
|
|
177
|
+
completed status or network result of the preceding attempt.
|
|
178
|
+
|
|
179
|
+
`RetryTraceOutcome.RESPONSE` means that request execution produced a response;
|
|
180
|
+
it does not classify an HTTP status as successful. For streaming responses the
|
|
181
|
+
trace is complete when response headers are exposed, because body-consumption
|
|
182
|
+
failures are outside the retry scope and do not mutate the immutable trace.
|
|
183
|
+
`HTTPStatusError` preserves the trace of its response.
|
|
184
|
+
|
|
185
|
+
FogHTTP preserves the original terminal request error if CPython cannot
|
|
186
|
+
allocate or attach the native trace handoff object. That exceptional attachment
|
|
187
|
+
failure is reported through `sys.unraisablehook`; the error then has no trace
|
|
188
|
+
rather than being replaced by an observability error.
|
|
189
|
+
|
|
190
|
+
Trace values never include a request path, query, headers, body, credentials or
|
|
191
|
+
userinfo. They retain only the normalized origin and typed operational fields,
|
|
192
|
+
so their default representation is safe for diagnostics.
|
|
193
|
+
|
|
194
|
+
When telemetry is enabled and the native request returns or raises, each retry
|
|
195
|
+
decision emits a typed `TelemetryEventType.RETRY_DECISION` event with:
|
|
196
|
+
|
|
197
|
+
- `retry_attempt`, starting at `1` for the failed initial attempt;
|
|
198
|
+
- `status_code` or `error_type`;
|
|
199
|
+
- `retry_decision`: `retry`, `stop`, or `block_non_replayable`;
|
|
200
|
+
- `retry_reason`: `status`, `network_error`, `method_not_allowed`,
|
|
201
|
+
`non_replayable_body`, or `retries_exhausted`;
|
|
202
|
+
- `retry_backoff_ns` and `elapsed_ns`;
|
|
203
|
+
- normalized method and origin, without request path, query, headers, or body.
|
|
204
|
+
|
|
205
|
+
The public trace and telemetry events are derived from the same native attempt
|
|
206
|
+
records; terminal attempts without a retry decision do not emit a
|
|
207
|
+
`retry_decision` event. The final `request_finished` event still reports the
|
|
208
|
+
logical request outcome. Native records are delivered when the transport
|
|
209
|
+
returns or raises, not as live callbacks during backoff.
|
|
210
|
+
`RetryAttempt.decision` and `RetryAttempt.reason` intentionally reuse
|
|
211
|
+
`TelemetryRetryDecision` and `TelemetryRetryReason` as the shared public retry
|
|
212
|
+
vocabulary; telemetry is a projection of the same decision records rather than
|
|
213
|
+
a separate enum contract.
|
|
214
|
+
For a `retry_decision` event, `elapsed_ns` is derived from
|
|
215
|
+
`RetryAttempt.decision_elapsed`, and `status_code`/`error_type` describe the
|
|
216
|
+
trigger seen when that decision was committed. A later buffered-body error can
|
|
217
|
+
therefore appear on the state-oriented `RetryAttempt` without rewriting the
|
|
218
|
+
earlier decision event.
|
|
219
|
+
|
|
220
|
+
Cancelling an async caller can abort the native task before a response or
|
|
221
|
+
exception is returned. `asyncio.CancelledError` therefore does not manufacture
|
|
222
|
+
a partial `retry_trace`, and buffered retry decisions, including the decision
|
|
223
|
+
that selected a cancelled backoff, are not emitted to the Python sink. There is
|
|
224
|
+
no global retry-trace history.
|