eggfetch 0.1.8__tar.gz → 0.2.0__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.
- {eggfetch-0.1.8 → eggfetch-0.2.0}/Cargo.lock +6 -6
- {eggfetch-0.1.8 → eggfetch-0.2.0}/PKG-INFO +21 -3
- {eggfetch-0.1.8/crates/eggfetch-http-connect → eggfetch-0.2.0}/README.md +20 -2
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/Cargo.toml +3 -3
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/README.md +20 -2
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/body.rs +3 -3
- eggfetch-0.2.0/crates/eggfetch-core/src/client/config.rs +120 -0
- eggfetch-0.2.0/crates/eggfetch-core/src/client/connectors.rs +179 -0
- eggfetch-0.2.0/crates/eggfetch-core/src/client/routes.rs +462 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/client.rs +24 -698
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/compression.rs +129 -81
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/cookie.rs +274 -48
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/headers.rs +60 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/multipart.rs +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/finalize.rs +6 -3
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/h3_dispatch.rs +2 -2
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/hyper_dispatch.rs +28 -29
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/mod.rs +42 -36
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/prepare.rs +57 -21
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/proxy_dispatch.rs +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/redirect.rs +18 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pool.rs +35 -1
- eggfetch-0.2.0/crates/eggfetch-core/src/proxy/environment.rs +37 -0
- eggfetch-0.2.0/crates/eggfetch-core/src/proxy/identity.rs +53 -0
- eggfetch-0.2.0/crates/eggfetch-core/src/proxy/no_proxy.rs +340 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/proxy.rs +59 -430
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/retry.rs +16 -8
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/tls.rs +6 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/direct.rs +10 -9
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/direct_connector.rs +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/proxy.rs +12 -15
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/socks.rs +15 -6
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/uds.rs +1 -5
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/adverse_tests.rs +86 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/cross_feature_tests.rs +37 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/native_http_body_tests.rs +8 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/proxy_tests.rs +27 -0
- eggfetch-0.2.0/crates/eggfetch-core/tests/public_api_contracts.rs +78 -0
- eggfetch-0.2.0/crates/eggfetch-core/tests/streaming_decompression_tests.rs +565 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/Cargo.toml +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0/crates/eggfetch-http-connect}/README.md +20 -2
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/Cargo.toml +2 -2
- eggfetch-0.2.0/crates/eggfetch-python/README.md +244 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/lib.rs +6 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/network_stream.rs +5 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/response.rs +208 -64
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming/async_iterators.rs +470 -0
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming/decoding.rs +97 -0
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming/state.rs +29 -0
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming/sync_bridge.rs +87 -0
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming/sync_iterators.rs +499 -0
- eggfetch-0.2.0/crates/eggfetch-python/src/streaming.rs +737 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/tls.rs +86 -50
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_corrective_02_extensions_and_wire_metadata.py +32 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response_streaming.py +1 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_ssl_context_translation.py +364 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_response_compat.py +72 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_sync.py +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/pyproject.toml +1 -1
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/_ssl_context.py +37 -0
- eggfetch-0.1.8/crates/eggfetch-python/README.md +0 -226
- eggfetch-0.1.8/crates/eggfetch-python/src/streaming.rs +0 -1786
- {eggfetch-0.1.8 → eggfetch-0.2.0}/Cargo.toml +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/examples/quickstart.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/auth.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/error.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/h2_headers.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/http_origin.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/http_version.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/lib.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/limits.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/network_stream.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/lean.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/retry.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/pipeline/route.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/redact.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/redirect.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/request.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/response.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/response_decode.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/service.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/stream/body_timeout.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/stream/mod.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/stream/write_timeout.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/timeout.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/trace.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/alt_svc.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/connect.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/connect_timeout.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/dialer.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/http3.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/hyper_client.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/lifecycle.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/metrics.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/mod.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport/standard_resolver.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/src/transport_hints.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/custom_dialer_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/direct_transport_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/h3_alt_svc_discovery.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/h3_hardening.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/h3_integration.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/h3_interop_qualification.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/http2_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/http3_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/integration.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/lean_policy_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/lean_route_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/native_json.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/native_tower_service_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/network_stream_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/pool_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/proxy_env_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/redirect_strict_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/request_failure_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/resolved_route_cache_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/resource_stabilization.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/response_body_public_shape.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/retry_integration.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/test_server.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/timeout_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/tls_fixtures.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/tls_integration.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/total_body_deadline_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/trailer_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-core/tests/transport_metrics_tests.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/src/error.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/src/lib.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/src/request.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/src/response.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/src/target.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-http-connect/tests/wire.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/async_client.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/auth.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/client.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/conversion.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/cookies.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/errors.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/extensions.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/headers.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/limits.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/multipart.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/proxy.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/request_preparation.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/retry.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/timeout.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/src/trace_bridge.rs +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/conftest.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/fixtures.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/native_fixtures.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_asgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_auth.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_auth_input_normalization.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_auth_replay.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_backends.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_behavior_cases.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_client.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_client_mutability_and_state.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_client_stream_overrides.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_config_objects.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_cookie_scope_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_cookies.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_and_proxy_trust_safety.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_network_proof.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_corrective_03_network_stream_upgrade.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_corrective_kernel.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_downstream_portfolio.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_downstream_runner_negative.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_environment.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_exceptions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_extensions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_h2_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_headers.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_hook_auth_ordering.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_hook_cookie_auth_ordering.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_hooks.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx2_api_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx2_behavior.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx2_sse.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx2_websocket.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx_extras.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx_reference_pinning.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_httpx_required.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_imports.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_lifecycle.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_lossy_conversions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_merge_lossless.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_merge_matrix.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_mock_transport.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_mounts.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_multihop_cleanup.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_native_proxy_tls.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_native_timeout_classification.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_no_proxy_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_oracle_negative.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_protocol_and_unsupported_options.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_proxy_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_proxy_phase05_tracks.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_raw_stream_httpx_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_raw_stream_lifecycle.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_redirect_state_machine_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_request.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_request_construction_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_request_stream_state.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_request_streaming.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_resource_assertions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response_encoding_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response_metadata_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response_status_and_redirect_state.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_response_stream_state_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_shutdown.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_soak.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_socks_transport.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_ssl_context_network_proof.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_stream_resources.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_timeout_integration.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_timeout_proxysis.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_timeout_reference_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_top_level_helpers_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_trace_detection.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_transports.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_uds_transport.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_url_query.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/compat/test_wsgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/conftest.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/native_api_manifest.json +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_async.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_auth.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_close_races.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_cookie_auth.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_cookies.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_differential.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_http2.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_interpreter_shutdown.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_native_api.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_parity.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_proxy.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_python_typing_surface.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_redirects.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_retry.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/test_streaming.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/typing/compat_consumer.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/typing/consumer.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/typing/negative_sync_async_body.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/crates/eggfetch-python/tests/typing/negative_verify_and_return.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/__init__.pyi +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/_native.pyi +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/__init__.pyi +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_asgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_auth.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_client.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_cookies.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_diagnostics.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_exceptions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_headers.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_limits.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_mock.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_proxy.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_request.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_response.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_ssl_context.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_status_codes.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_stream.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_timeout.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_transports.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_urls.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx/_wsgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/__init__.pyi +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_alias.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_api.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_asgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_auth.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_client.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_config.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_cookies.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_exceptions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_headers.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_mock.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_request.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_response.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_sse.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_status_codes.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_stream.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_transports.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_urls.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/_wsgi.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/websockets/__init__.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/websockets/_api.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/websockets/_exceptions.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/websockets/_ping.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/compat/httpx2/websockets/_transport.py +0 -0
- {eggfetch-0.1.8 → eggfetch-0.2.0}/python/eggfetch/py.typed +0 -0
|
@@ -501,7 +501,7 @@ dependencies = [
|
|
|
501
501
|
|
|
502
502
|
[[package]]
|
|
503
503
|
name = "eggfetch-cli"
|
|
504
|
-
version = "0.
|
|
504
|
+
version = "0.2.0"
|
|
505
505
|
dependencies = [
|
|
506
506
|
"anyhow",
|
|
507
507
|
"bytes",
|
|
@@ -518,7 +518,7 @@ dependencies = [
|
|
|
518
518
|
|
|
519
519
|
[[package]]
|
|
520
520
|
name = "eggfetch-core"
|
|
521
|
-
version = "0.
|
|
521
|
+
version = "0.2.0"
|
|
522
522
|
dependencies = [
|
|
523
523
|
"async-compression",
|
|
524
524
|
"base64 0.23.1",
|
|
@@ -564,7 +564,7 @@ dependencies = [
|
|
|
564
564
|
|
|
565
565
|
[[package]]
|
|
566
566
|
name = "eggfetch-ffi"
|
|
567
|
-
version = "0.
|
|
567
|
+
version = "0.2.0"
|
|
568
568
|
dependencies = [
|
|
569
569
|
"bytes",
|
|
570
570
|
"eggfetch-core",
|
|
@@ -576,7 +576,7 @@ dependencies = [
|
|
|
576
576
|
|
|
577
577
|
[[package]]
|
|
578
578
|
name = "eggfetch-http-connect"
|
|
579
|
-
version = "0.
|
|
579
|
+
version = "0.2.0"
|
|
580
580
|
dependencies = [
|
|
581
581
|
"base64 0.23.1",
|
|
582
582
|
"thiserror",
|
|
@@ -585,7 +585,7 @@ dependencies = [
|
|
|
585
585
|
|
|
586
586
|
[[package]]
|
|
587
587
|
name = "eggfetch-node"
|
|
588
|
-
version = "0.
|
|
588
|
+
version = "0.2.0"
|
|
589
589
|
dependencies = [
|
|
590
590
|
"eggfetch-ffi",
|
|
591
591
|
"napi",
|
|
@@ -597,7 +597,7 @@ dependencies = [
|
|
|
597
597
|
|
|
598
598
|
[[package]]
|
|
599
599
|
name = "eggfetch-python"
|
|
600
|
-
version = "0.
|
|
600
|
+
version = "0.2.0"
|
|
601
601
|
dependencies = [
|
|
602
602
|
"bytes",
|
|
603
603
|
"eggfetch-core",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eggfetch
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -39,6 +39,7 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
39
39
|
|
|
40
40
|
- **HTTP/1.1, HTTP/2, HTTP/3** — ALPN negotiation; HTTP/3 over QUIC is experimental ([guide](docs/rust/guide.md))
|
|
41
41
|
- **Streaming** — response bodies stream without eager buffering (`bytes_stream()`, `text_lines()`), with trailers after EOF ([guide](docs/rust/guide.md))
|
|
42
|
+
- **Performance qualification** — API-safe Rust/Python hot-path benchmarks and reproduction commands are documented in [architecture/benchmarks](docs/architecture/benchmarks.md)
|
|
42
43
|
- **Pooling and timeouts** — per-origin connection pools, phase-aware timeouts (pool/connect/write/read/total), and transport metrics ([pool/timeouts](docs/architecture/core-timeout-pool.md))
|
|
43
44
|
- **TLS** — rustls with per-client crypto providers, custom or additive CA roots, mTLS client certs, version policy, and verification toggle ([TLS](docs/concepts/tls.md))
|
|
44
45
|
- **Proxy** — HTTP forwarding, HTTPS CONNECT, proxy auth, per-request override, `NO_PROXY`, SOCKS5, and UDS routes ([proxy](docs/concepts/proxy.md)). CONNECT wire bytes live in the small `eggfetch-http-connect` crate (no sockets/TLS/retry) consumed by `eggfetch-core` via the `proxy` feature.
|
|
@@ -51,6 +52,10 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
51
52
|
- **CLI** — streaming output, machine-readable formats, shell completions ([guide](docs/cli/guide.md))
|
|
52
53
|
- **C ABI and Node.js prototype** — opaque-handle FFI plus an experimental N-API wrapper ([ffi-and-node](docs/architecture/ffi-and-node.md))
|
|
53
54
|
|
|
55
|
+
The implementation ownership and frozen low-level Rust compatibility surfaces
|
|
56
|
+
are tracked in the [architecture overview](docs/architecture/overview.md) and
|
|
57
|
+
the [Rust surface-containment inventory](docs/architecture/rust-surface-containment.md).
|
|
58
|
+
|
|
54
59
|
## Installation
|
|
55
60
|
|
|
56
61
|
**Python:**
|
|
@@ -63,7 +68,7 @@ pip install eggfetch
|
|
|
63
68
|
|
|
64
69
|
```toml
|
|
65
70
|
[dependencies]
|
|
66
|
-
eggfetch-core = "0.
|
|
71
|
+
eggfetch-core = "0.2"
|
|
67
72
|
```
|
|
68
73
|
|
|
69
74
|
The default features are the secure HTTP/1.1 client with Rustls and native roots. See the [feature profile matrix](docs/architecture/feature-flags.md#supported-core-profiles) for minimal, deterministic, and embedded recipes. Low-level embedding without the `url`/`idna`/ICU closure selects `native-http1` (or `native-http2`) without `high-level-url` and uses `Client::execute_http_body` with a caller-owned `http::Uri` (callers own IDNA/punycode conversion). The lean high-level recipe (`standard-http1` + `tls-rustls`, without `advanced-routing` or the `logical-retry`/`redirects`/`basic-auth` policy bundle) keeps the URL/request/response conveniences, Bearer auth, timeouts, body bounds, pooling, TLS, and typed failures while dispatching once over the standard route and returning 3xx without following.
|
|
@@ -141,7 +146,19 @@ from eggfetch.compat.httpx2 import Client as H2Client # httpx2 2.12.0 surface
|
|
|
141
146
|
|
|
142
147
|
See [`docs/python/guide.md`](docs/python/guide.md) for the full Python API reference.
|
|
143
148
|
|
|
144
|
-
The native package supports Python 3.10–3.15 and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
149
|
+
The native package supports Python 3.10–3.15 (3.15 wheels pending the build-only rehearsal tracked in `plans/README.md`) and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
150
|
+
|
|
151
|
+
Buffered Python responses retain the raw body and decode `.text` lazily; their
|
|
152
|
+
`iter_bytes()`, `iter_text()`, and `iter_lines()` iterators yield one item at a
|
|
153
|
+
time without pre-materializing the complete iterator result. Sync streaming
|
|
154
|
+
uses bounded async-aware backpressure so a slow consumer does not block the
|
|
155
|
+
client runtime. These are private implementation details and do not change
|
|
156
|
+
the public API or chunking contracts.
|
|
157
|
+
|
|
158
|
+
Maintainers can run `./scripts/check.sh` for routine local CI-equivalent
|
|
159
|
+
validation. The stable Rust profile contracts run in Tier 1; the exact Rust
|
|
160
|
+
surface snapshots and semver cross-check are an extended gate documented in
|
|
161
|
+
[`compat/rust-public-api/README.md`](compat/rust-public-api/README.md).
|
|
145
162
|
|
|
146
163
|
## Usage -- Rust
|
|
147
164
|
|
|
@@ -232,6 +249,7 @@ See [`docs/reference/compatibility.md`](docs/reference/compatibility.md) for the
|
|
|
232
249
|
| [migration/](docs/migration/) | Migration guides from requests and HTTPX |
|
|
233
250
|
| [cookbook/](docs/cookbook/) | Practical runnable examples |
|
|
234
251
|
| [reference/](docs/reference/) | Compatibility matrix, feature matrix, error reference |
|
|
252
|
+
| [releases/](docs/releases/) | Release process and compatibility policy |
|
|
235
253
|
| [security/](docs/security/) | Security guidelines and troubleshooting |
|
|
236
254
|
| [architecture/](docs/architecture/) | Internal architecture documentation |
|
|
237
255
|
| [ffi/](docs/ffi/) | C ABI and FFI binding guide |
|
|
@@ -13,6 +13,7 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
13
13
|
|
|
14
14
|
- **HTTP/1.1, HTTP/2, HTTP/3** — ALPN negotiation; HTTP/3 over QUIC is experimental ([guide](docs/rust/guide.md))
|
|
15
15
|
- **Streaming** — response bodies stream without eager buffering (`bytes_stream()`, `text_lines()`), with trailers after EOF ([guide](docs/rust/guide.md))
|
|
16
|
+
- **Performance qualification** — API-safe Rust/Python hot-path benchmarks and reproduction commands are documented in [architecture/benchmarks](docs/architecture/benchmarks.md)
|
|
16
17
|
- **Pooling and timeouts** — per-origin connection pools, phase-aware timeouts (pool/connect/write/read/total), and transport metrics ([pool/timeouts](docs/architecture/core-timeout-pool.md))
|
|
17
18
|
- **TLS** — rustls with per-client crypto providers, custom or additive CA roots, mTLS client certs, version policy, and verification toggle ([TLS](docs/concepts/tls.md))
|
|
18
19
|
- **Proxy** — HTTP forwarding, HTTPS CONNECT, proxy auth, per-request override, `NO_PROXY`, SOCKS5, and UDS routes ([proxy](docs/concepts/proxy.md)). CONNECT wire bytes live in the small `eggfetch-http-connect` crate (no sockets/TLS/retry) consumed by `eggfetch-core` via the `proxy` feature.
|
|
@@ -25,6 +26,10 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
25
26
|
- **CLI** — streaming output, machine-readable formats, shell completions ([guide](docs/cli/guide.md))
|
|
26
27
|
- **C ABI and Node.js prototype** — opaque-handle FFI plus an experimental N-API wrapper ([ffi-and-node](docs/architecture/ffi-and-node.md))
|
|
27
28
|
|
|
29
|
+
The implementation ownership and frozen low-level Rust compatibility surfaces
|
|
30
|
+
are tracked in the [architecture overview](docs/architecture/overview.md) and
|
|
31
|
+
the [Rust surface-containment inventory](docs/architecture/rust-surface-containment.md).
|
|
32
|
+
|
|
28
33
|
## Installation
|
|
29
34
|
|
|
30
35
|
**Python:**
|
|
@@ -37,7 +42,7 @@ pip install eggfetch
|
|
|
37
42
|
|
|
38
43
|
```toml
|
|
39
44
|
[dependencies]
|
|
40
|
-
eggfetch-core = "0.
|
|
45
|
+
eggfetch-core = "0.2"
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
The default features are the secure HTTP/1.1 client with Rustls and native roots. See the [feature profile matrix](docs/architecture/feature-flags.md#supported-core-profiles) for minimal, deterministic, and embedded recipes. Low-level embedding without the `url`/`idna`/ICU closure selects `native-http1` (or `native-http2`) without `high-level-url` and uses `Client::execute_http_body` with a caller-owned `http::Uri` (callers own IDNA/punycode conversion). The lean high-level recipe (`standard-http1` + `tls-rustls`, without `advanced-routing` or the `logical-retry`/`redirects`/`basic-auth` policy bundle) keeps the URL/request/response conveniences, Bearer auth, timeouts, body bounds, pooling, TLS, and typed failures while dispatching once over the standard route and returning 3xx without following.
|
|
@@ -115,7 +120,19 @@ from eggfetch.compat.httpx2 import Client as H2Client # httpx2 2.12.0 surface
|
|
|
115
120
|
|
|
116
121
|
See [`docs/python/guide.md`](docs/python/guide.md) for the full Python API reference.
|
|
117
122
|
|
|
118
|
-
The native package supports Python 3.10–3.15 and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
123
|
+
The native package supports Python 3.10–3.15 (3.15 wheels pending the build-only rehearsal tracked in `plans/README.md`) and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
124
|
+
|
|
125
|
+
Buffered Python responses retain the raw body and decode `.text` lazily; their
|
|
126
|
+
`iter_bytes()`, `iter_text()`, and `iter_lines()` iterators yield one item at a
|
|
127
|
+
time without pre-materializing the complete iterator result. Sync streaming
|
|
128
|
+
uses bounded async-aware backpressure so a slow consumer does not block the
|
|
129
|
+
client runtime. These are private implementation details and do not change
|
|
130
|
+
the public API or chunking contracts.
|
|
131
|
+
|
|
132
|
+
Maintainers can run `./scripts/check.sh` for routine local CI-equivalent
|
|
133
|
+
validation. The stable Rust profile contracts run in Tier 1; the exact Rust
|
|
134
|
+
surface snapshots and semver cross-check are an extended gate documented in
|
|
135
|
+
[`compat/rust-public-api/README.md`](compat/rust-public-api/README.md).
|
|
119
136
|
|
|
120
137
|
## Usage -- Rust
|
|
121
138
|
|
|
@@ -206,6 +223,7 @@ See [`docs/reference/compatibility.md`](docs/reference/compatibility.md) for the
|
|
|
206
223
|
| [migration/](docs/migration/) | Migration guides from requests and HTTPX |
|
|
207
224
|
| [cookbook/](docs/cookbook/) | Practical runnable examples |
|
|
208
225
|
| [reference/](docs/reference/) | Compatibility matrix, feature matrix, error reference |
|
|
226
|
+
| [releases/](docs/releases/) | Release process and compatibility policy |
|
|
209
227
|
| [security/](docs/security/) | Security guidelines and troubleshooting |
|
|
210
228
|
| [architecture/](docs/architecture/) | Internal architecture documentation |
|
|
211
229
|
| [ffi/](docs/ffi/) | C ABI and FFI binding guide |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "eggfetch-core"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition.workspace = true
|
|
5
5
|
rust-version.workspace = true
|
|
6
6
|
license.workspace = true
|
|
@@ -73,7 +73,7 @@ json = ["dep:serde", "dep:serde_json"]
|
|
|
73
73
|
compression-gzip = ["dep:async-compression", "async-compression/gzip", "dep:tokio-util", "tokio/io-util", "dep:flate2"]
|
|
74
74
|
compression-brotli = ["dep:async-compression", "async-compression/brotli", "dep:tokio-util", "tokio/io-util", "dep:brotli"]
|
|
75
75
|
compression-zstd = ["dep:async-compression", "async-compression/zstd", "dep:tokio-util", "tokio/io-util", "dep:zstd"]
|
|
76
|
-
compression-deflate = ["dep:async-compression", "async-compression/deflate", "dep:tokio-util", "tokio/io-util", "dep:flate2"]
|
|
76
|
+
compression-deflate = ["dep:async-compression", "async-compression/deflate", "async-compression/zlib", "dep:tokio-util", "tokio/io-util", "dep:flate2"]
|
|
77
77
|
cookies = ["dep:cookie", "high-level-url"]
|
|
78
78
|
proxy = ["http1", "tls-rustls", "tokio/io-util", "high-level-url", "dep:eggfetch-http-connect", "dep:base64"]
|
|
79
79
|
tracing = ["dep:tracing"]
|
|
@@ -82,7 +82,7 @@ test-util = ["tokio/test-util"]
|
|
|
82
82
|
|
|
83
83
|
[dependencies]
|
|
84
84
|
bytes = "1"
|
|
85
|
-
eggfetch-http-connect = { version = "0.
|
|
85
|
+
eggfetch-http-connect = { version = "0.2.0", path = "../eggfetch-http-connect", optional = true }
|
|
86
86
|
futures-core = "0.3"
|
|
87
87
|
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
|
|
88
88
|
http = "1"
|
|
@@ -13,6 +13,7 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
13
13
|
|
|
14
14
|
- **HTTP/1.1, HTTP/2, HTTP/3** — ALPN negotiation; HTTP/3 over QUIC is experimental ([guide](docs/rust/guide.md))
|
|
15
15
|
- **Streaming** — response bodies stream without eager buffering (`bytes_stream()`, `text_lines()`), with trailers after EOF ([guide](docs/rust/guide.md))
|
|
16
|
+
- **Performance qualification** — API-safe Rust/Python hot-path benchmarks and reproduction commands are documented in [architecture/benchmarks](docs/architecture/benchmarks.md)
|
|
16
17
|
- **Pooling and timeouts** — per-origin connection pools, phase-aware timeouts (pool/connect/write/read/total), and transport metrics ([pool/timeouts](docs/architecture/core-timeout-pool.md))
|
|
17
18
|
- **TLS** — rustls with per-client crypto providers, custom or additive CA roots, mTLS client certs, version policy, and verification toggle ([TLS](docs/concepts/tls.md))
|
|
18
19
|
- **Proxy** — HTTP forwarding, HTTPS CONNECT, proxy auth, per-request override, `NO_PROXY`, SOCKS5, and UDS routes ([proxy](docs/concepts/proxy.md)). CONNECT wire bytes live in the small `eggfetch-http-connect` crate (no sockets/TLS/retry) consumed by `eggfetch-core` via the `proxy` feature.
|
|
@@ -25,6 +26,10 @@ eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python b
|
|
|
25
26
|
- **CLI** — streaming output, machine-readable formats, shell completions ([guide](docs/cli/guide.md))
|
|
26
27
|
- **C ABI and Node.js prototype** — opaque-handle FFI plus an experimental N-API wrapper ([ffi-and-node](docs/architecture/ffi-and-node.md))
|
|
27
28
|
|
|
29
|
+
The implementation ownership and frozen low-level Rust compatibility surfaces
|
|
30
|
+
are tracked in the [architecture overview](docs/architecture/overview.md) and
|
|
31
|
+
the [Rust surface-containment inventory](docs/architecture/rust-surface-containment.md).
|
|
32
|
+
|
|
28
33
|
## Installation
|
|
29
34
|
|
|
30
35
|
**Python:**
|
|
@@ -37,7 +42,7 @@ pip install eggfetch
|
|
|
37
42
|
|
|
38
43
|
```toml
|
|
39
44
|
[dependencies]
|
|
40
|
-
eggfetch-core = "0.
|
|
45
|
+
eggfetch-core = "0.2"
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
The default features are the secure HTTP/1.1 client with Rustls and native roots. See the [feature profile matrix](docs/architecture/feature-flags.md#supported-core-profiles) for minimal, deterministic, and embedded recipes. Low-level embedding without the `url`/`idna`/ICU closure selects `native-http1` (or `native-http2`) without `high-level-url` and uses `Client::execute_http_body` with a caller-owned `http::Uri` (callers own IDNA/punycode conversion). The lean high-level recipe (`standard-http1` + `tls-rustls`, without `advanced-routing` or the `logical-retry`/`redirects`/`basic-auth` policy bundle) keeps the URL/request/response conveniences, Bearer auth, timeouts, body bounds, pooling, TLS, and typed failures while dispatching once over the standard route and returning 3xx without following.
|
|
@@ -115,7 +120,19 @@ from eggfetch.compat.httpx2 import Client as H2Client # httpx2 2.12.0 surface
|
|
|
115
120
|
|
|
116
121
|
See [`docs/python/guide.md`](docs/python/guide.md) for the full Python API reference.
|
|
117
122
|
|
|
118
|
-
The native package supports Python 3.10–3.15 and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
123
|
+
The native package supports Python 3.10–3.15 (3.15 wheels pending the build-only rehearsal tracked in `plans/README.md`) and ships `py.typed` stubs for its public API. `Client` and the top-level sync helpers accept lazy sync `content=` iterables (async-only iterables are rejected before dispatch); `AsyncClient` additionally accepts lazy async iterables, pulled only as the transport asks for them. `eggfetch._native` is a private implementation module — import from `eggfetch`.
|
|
124
|
+
|
|
125
|
+
Buffered Python responses retain the raw body and decode `.text` lazily; their
|
|
126
|
+
`iter_bytes()`, `iter_text()`, and `iter_lines()` iterators yield one item at a
|
|
127
|
+
time without pre-materializing the complete iterator result. Sync streaming
|
|
128
|
+
uses bounded async-aware backpressure so a slow consumer does not block the
|
|
129
|
+
client runtime. These are private implementation details and do not change
|
|
130
|
+
the public API or chunking contracts.
|
|
131
|
+
|
|
132
|
+
Maintainers can run `./scripts/check.sh` for routine local CI-equivalent
|
|
133
|
+
validation. The stable Rust profile contracts run in Tier 1; the exact Rust
|
|
134
|
+
surface snapshots and semver cross-check are an extended gate documented in
|
|
135
|
+
[`compat/rust-public-api/README.md`](compat/rust-public-api/README.md).
|
|
119
136
|
|
|
120
137
|
## Usage -- Rust
|
|
121
138
|
|
|
@@ -206,6 +223,7 @@ See [`docs/reference/compatibility.md`](docs/reference/compatibility.md) for the
|
|
|
206
223
|
| [migration/](docs/migration/) | Migration guides from requests and HTTPX |
|
|
207
224
|
| [cookbook/](docs/cookbook/) | Practical runnable examples |
|
|
208
225
|
| [reference/](docs/reference/) | Compatibility matrix, feature matrix, error reference |
|
|
226
|
+
| [releases/](docs/releases/) | Release process and compatibility policy |
|
|
209
227
|
| [security/](docs/security/) | Security guidelines and troubleshooting |
|
|
210
228
|
| [architecture/](docs/architecture/) | Internal architecture documentation |
|
|
211
229
|
| [ffi/](docs/ffi/) | C ABI and FFI binding guide |
|
|
@@ -468,7 +468,7 @@ impl NativeResponseBody {
|
|
|
468
468
|
/// Construct a native body around an eggfetch-owned response body.
|
|
469
469
|
pub(crate) fn new<B>(
|
|
470
470
|
body: B,
|
|
471
|
-
lease: PoolGuardArc
|
|
471
|
+
lease: Option<PoolGuardArc>,
|
|
472
472
|
read_timeout: Option<Duration>,
|
|
473
473
|
total_deadline: Option<ResponseDeadline>,
|
|
474
474
|
) -> Self
|
|
@@ -477,7 +477,7 @@ impl NativeResponseBody {
|
|
|
477
477
|
{
|
|
478
478
|
Self {
|
|
479
479
|
inner: Box::pin(body),
|
|
480
|
-
lease
|
|
480
|
+
lease,
|
|
481
481
|
read_timeout,
|
|
482
482
|
total_deadline,
|
|
483
483
|
read_sleep: None,
|
|
@@ -651,7 +651,7 @@ impl NativeResponseBody {
|
|
|
651
651
|
/// stable eggfetch error type.
|
|
652
652
|
pub(crate) fn from_incoming(
|
|
653
653
|
incoming: hyper::body::Incoming,
|
|
654
|
-
lease: PoolGuardArc
|
|
654
|
+
lease: Option<PoolGuardArc>,
|
|
655
655
|
read_timeout: Option<Duration>,
|
|
656
656
|
total_deadline: Option<ResponseDeadline>,
|
|
657
657
|
) -> Self {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
//! Private client configuration and its redacted defaults.
|
|
2
|
+
|
|
3
|
+
use std::sync::Arc;
|
|
4
|
+
|
|
5
|
+
use crate::headers::Headers;
|
|
6
|
+
use crate::http_version::HttpVersionPolicy;
|
|
7
|
+
use crate::timeout::Timeout;
|
|
8
|
+
|
|
9
|
+
#[cfg(feature = "cookies")]
|
|
10
|
+
use crate::cookie::CookieJar;
|
|
11
|
+
#[cfg(feature = "proxy")]
|
|
12
|
+
use crate::proxy::Proxy;
|
|
13
|
+
#[cfg(feature = "redirects")]
|
|
14
|
+
use crate::redirect::RedirectPolicy;
|
|
15
|
+
#[cfg(feature = "logical-retry")]
|
|
16
|
+
use crate::retry::RetryPolicy;
|
|
17
|
+
#[cfg(feature = "advanced-routing")]
|
|
18
|
+
use crate::transport::dialer::Dialer;
|
|
19
|
+
|
|
20
|
+
/// Shared client configuration. This module is private; public builder
|
|
21
|
+
/// methods remain declared in the parent `client` module.
|
|
22
|
+
#[derive(Clone)]
|
|
23
|
+
pub(crate) struct ClientConfig {
|
|
24
|
+
pub(crate) default_headers: Headers,
|
|
25
|
+
pub(crate) user_agent: Option<String>,
|
|
26
|
+
pub(crate) timeout: Option<Timeout>,
|
|
27
|
+
#[cfg(feature = "redirects")]
|
|
28
|
+
pub(crate) redirect: RedirectPolicy,
|
|
29
|
+
pub(crate) auth: Option<crate::auth::AuthScheme>,
|
|
30
|
+
#[cfg(feature = "cookies")]
|
|
31
|
+
pub(crate) cookie_jar: CookieJar,
|
|
32
|
+
pub(crate) automatic_decompression: bool,
|
|
33
|
+
pub(crate) max_decoded_body_size: Option<usize>,
|
|
34
|
+
pub(crate) max_decompression_ratio: Option<f64>,
|
|
35
|
+
#[cfg(feature = "proxy")]
|
|
36
|
+
pub(crate) proxy: Option<Proxy>,
|
|
37
|
+
#[cfg(feature = "proxy")]
|
|
38
|
+
pub(crate) environment_proxies: Vec<Proxy>,
|
|
39
|
+
#[cfg(feature = "tls-rustls")]
|
|
40
|
+
pub(crate) tls_config: Option<crate::tls::TlsConfig>,
|
|
41
|
+
#[cfg(feature = "logical-retry")]
|
|
42
|
+
pub(crate) retry: Option<RetryPolicy>,
|
|
43
|
+
pub(crate) retry_canceled_requests: bool,
|
|
44
|
+
#[cfg(feature = "advanced-routing")]
|
|
45
|
+
pub(crate) dialer: Option<Arc<dyn Dialer>>,
|
|
46
|
+
#[cfg(feature = "advanced-routing")]
|
|
47
|
+
pub(crate) uds_configured: bool,
|
|
48
|
+
#[allow(
|
|
49
|
+
dead_code,
|
|
50
|
+
reason = "stored for inspection and future request-level use"
|
|
51
|
+
)]
|
|
52
|
+
pub(crate) http_version_policy: HttpVersionPolicy,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
impl std::fmt::Debug for ClientConfig {
|
|
56
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
57
|
+
let mut debug = f.debug_struct("ClientConfig");
|
|
58
|
+
debug
|
|
59
|
+
.field("default_headers", &self.default_headers)
|
|
60
|
+
.field("user_agent", &self.user_agent)
|
|
61
|
+
.field("timeout", &self.timeout);
|
|
62
|
+
#[cfg(feature = "redirects")]
|
|
63
|
+
debug.field("redirect", &self.redirect);
|
|
64
|
+
debug.field("auth", &self.auth);
|
|
65
|
+
#[cfg(feature = "cookies")]
|
|
66
|
+
debug.field("cookie_jar", &self.cookie_jar);
|
|
67
|
+
debug
|
|
68
|
+
.field("automatic_decompression", &self.automatic_decompression)
|
|
69
|
+
.field("max_decoded_body_size", &self.max_decoded_body_size)
|
|
70
|
+
.field("max_decompression_ratio", &self.max_decompression_ratio);
|
|
71
|
+
#[cfg(feature = "proxy")]
|
|
72
|
+
debug
|
|
73
|
+
.field("proxy", &self.proxy)
|
|
74
|
+
.field("environment_proxies", &self.environment_proxies);
|
|
75
|
+
#[cfg(feature = "tls-rustls")]
|
|
76
|
+
debug.field("tls_config", &self.tls_config);
|
|
77
|
+
#[cfg(feature = "logical-retry")]
|
|
78
|
+
debug.field("retry", &self.retry);
|
|
79
|
+
debug.field("retry_canceled_requests", &self.retry_canceled_requests);
|
|
80
|
+
#[cfg(feature = "advanced-routing")]
|
|
81
|
+
debug
|
|
82
|
+
.field("dialer", &self.dialer.as_ref().map(|_| "configured"))
|
|
83
|
+
.field("uds_configured", &self.uds_configured);
|
|
84
|
+
debug
|
|
85
|
+
.field("http_version_policy", &self.http_version_policy)
|
|
86
|
+
.finish_non_exhaustive()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl Default for ClientConfig {
|
|
91
|
+
fn default() -> Self {
|
|
92
|
+
Self {
|
|
93
|
+
default_headers: Headers::new(),
|
|
94
|
+
user_agent: None,
|
|
95
|
+
timeout: None,
|
|
96
|
+
#[cfg(feature = "redirects")]
|
|
97
|
+
redirect: RedirectPolicy::default(),
|
|
98
|
+
auth: None,
|
|
99
|
+
#[cfg(feature = "cookies")]
|
|
100
|
+
cookie_jar: CookieJar::new(),
|
|
101
|
+
automatic_decompression: true,
|
|
102
|
+
max_decoded_body_size: None,
|
|
103
|
+
max_decompression_ratio: None,
|
|
104
|
+
#[cfg(feature = "proxy")]
|
|
105
|
+
proxy: None,
|
|
106
|
+
#[cfg(feature = "proxy")]
|
|
107
|
+
environment_proxies: Vec::new(),
|
|
108
|
+
#[cfg(feature = "tls-rustls")]
|
|
109
|
+
tls_config: None,
|
|
110
|
+
#[cfg(feature = "logical-retry")]
|
|
111
|
+
retry: None,
|
|
112
|
+
retry_canceled_requests: true,
|
|
113
|
+
#[cfg(feature = "advanced-routing")]
|
|
114
|
+
dialer: None,
|
|
115
|
+
#[cfg(feature = "advanced-routing")]
|
|
116
|
+
uds_configured: false,
|
|
117
|
+
http_version_policy: HttpVersionPolicy::default(),
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
//! Private connector preparation for the client.
|
|
2
|
+
|
|
3
|
+
#[cfg(feature = "advanced-routing")]
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
#[cfg(feature = "advanced-routing")]
|
|
7
|
+
use super::Dialer;
|
|
8
|
+
#[cfg(feature = "advanced-routing")]
|
|
9
|
+
use super::{Error, Result};
|
|
10
|
+
#[cfg(any(feature = "transport-http1", feature = "transport-http2"))]
|
|
11
|
+
use crate::transport::Connector;
|
|
12
|
+
|
|
13
|
+
/// Build the standard cleartext connector.
|
|
14
|
+
#[cfg(all(
|
|
15
|
+
not(feature = "tls-rustls"),
|
|
16
|
+
any(feature = "transport-http1", feature = "transport-http2")
|
|
17
|
+
))]
|
|
18
|
+
pub(super) fn build_standard_connector(
|
|
19
|
+
enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
20
|
+
) -> Connector {
|
|
21
|
+
build_standard_connector_with_resolver(
|
|
22
|
+
hyper_util::client::legacy::connect::dns::GaiResolver::new(),
|
|
23
|
+
enabler,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Build a standard cleartext connector with a caller-selected private
|
|
28
|
+
/// resolver. Production callers use the default system resolver; tests use
|
|
29
|
+
/// this generic seam to keep resolver provenance deterministic.
|
|
30
|
+
#[cfg(all(
|
|
31
|
+
not(feature = "tls-rustls"),
|
|
32
|
+
any(feature = "transport-http1", feature = "transport-http2")
|
|
33
|
+
))]
|
|
34
|
+
pub(super) fn build_standard_connector_with_resolver<R>(
|
|
35
|
+
resolver: R,
|
|
36
|
+
enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
37
|
+
) -> hyper_util::client::legacy::connect::HttpConnector<
|
|
38
|
+
crate::transport::standard_resolver::ClassifyingResolver<R>,
|
|
39
|
+
>
|
|
40
|
+
where
|
|
41
|
+
R: tower_service::Service<hyper_util::client::legacy::connect::dns::Name>
|
|
42
|
+
+ Clone
|
|
43
|
+
+ Send
|
|
44
|
+
+ Sync
|
|
45
|
+
+ 'static,
|
|
46
|
+
R::Response: Iterator<Item = std::net::SocketAddr>,
|
|
47
|
+
R::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
|
|
48
|
+
R::Future: Send + 'static,
|
|
49
|
+
{
|
|
50
|
+
let _ = enabler;
|
|
51
|
+
let mut connector = hyper_util::client::legacy::connect::HttpConnector::new_with_resolver(
|
|
52
|
+
crate::transport::standard_resolver::ClassifyingResolver::new(resolver),
|
|
53
|
+
);
|
|
54
|
+
connector.enforce_http(true);
|
|
55
|
+
connector
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Build the standard TLS connector from the authoritative [`TlsConfig`].
|
|
59
|
+
#[cfg(all(
|
|
60
|
+
feature = "tls-rustls",
|
|
61
|
+
any(feature = "transport-http1", feature = "transport-http2")
|
|
62
|
+
))]
|
|
63
|
+
pub(super) fn build_standard_connector(
|
|
64
|
+
config: rustls::ClientConfig,
|
|
65
|
+
enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
66
|
+
) -> Connector {
|
|
67
|
+
build_standard_connector_with_resolver(
|
|
68
|
+
config,
|
|
69
|
+
enabler,
|
|
70
|
+
hyper_util::client::legacy::connect::dns::GaiResolver::new(),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Build the standard Rustls connector around an explicitly constructed
|
|
75
|
+
/// Hyper resolver connector so DNS provenance survives the TLS wrapper.
|
|
76
|
+
#[cfg(all(
|
|
77
|
+
feature = "tls-rustls",
|
|
78
|
+
any(feature = "transport-http1", feature = "transport-http2")
|
|
79
|
+
))]
|
|
80
|
+
pub(super) fn build_standard_connector_with_resolver<R>(
|
|
81
|
+
mut config: rustls::ClientConfig,
|
|
82
|
+
enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
83
|
+
resolver: R,
|
|
84
|
+
) -> hyper_rustls::HttpsConnector<
|
|
85
|
+
hyper_util::client::legacy::connect::HttpConnector<
|
|
86
|
+
crate::transport::standard_resolver::ClassifyingResolver<R>,
|
|
87
|
+
>,
|
|
88
|
+
>
|
|
89
|
+
where
|
|
90
|
+
R: tower_service::Service<hyper_util::client::legacy::connect::dns::Name>
|
|
91
|
+
+ Clone
|
|
92
|
+
+ Send
|
|
93
|
+
+ Sync
|
|
94
|
+
+ 'static,
|
|
95
|
+
R::Response: Iterator<Item = std::net::SocketAddr>,
|
|
96
|
+
R::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
|
|
97
|
+
R::Future: Send + 'static,
|
|
98
|
+
{
|
|
99
|
+
// hyper-rustls fills ALPN from the selected protocol methods.
|
|
100
|
+
config.alpn_protocols.clear();
|
|
101
|
+
let mut http = hyper_util::client::legacy::connect::HttpConnector::new_with_resolver(
|
|
102
|
+
crate::transport::standard_resolver::ClassifyingResolver::new(resolver),
|
|
103
|
+
);
|
|
104
|
+
http.enforce_http(false);
|
|
105
|
+
let builder = hyper_rustls::HttpsConnectorBuilder::new()
|
|
106
|
+
.with_tls_config(config)
|
|
107
|
+
.https_or_http();
|
|
108
|
+
#[cfg(all(feature = "transport-http1", feature = "transport-http2"))]
|
|
109
|
+
{
|
|
110
|
+
match (enabler.enable_http1(), enabler.enable_http2()) {
|
|
111
|
+
(true, true) => builder.enable_http1().enable_http2().wrap_connector(http),
|
|
112
|
+
(true | false, false) => builder.enable_http1().wrap_connector(http),
|
|
113
|
+
(false, true) => builder.enable_http2().wrap_connector(http),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
#[cfg(all(feature = "transport-http1", not(feature = "transport-http2")))]
|
|
117
|
+
{
|
|
118
|
+
let _ = enabler;
|
|
119
|
+
builder.enable_http1().wrap_connector(http)
|
|
120
|
+
}
|
|
121
|
+
#[cfg(all(feature = "transport-http2", not(feature = "transport-http1")))]
|
|
122
|
+
{
|
|
123
|
+
let _ = enabler;
|
|
124
|
+
builder.enable_http2().wrap_connector(http)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[cfg(all(feature = "tls-rustls", feature = "advanced-routing"))]
|
|
129
|
+
pub(super) fn build_custom_connector(
|
|
130
|
+
config: rustls::ClientConfig,
|
|
131
|
+
dialer: Arc<dyn Dialer>,
|
|
132
|
+
enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
133
|
+
sni_hostname: Option<&str>,
|
|
134
|
+
) -> Result<crate::transport::CustomConnector> {
|
|
135
|
+
let config = configure_tls_alpn(config, enabler);
|
|
136
|
+
let resolver: Arc<dyn hyper_rustls::ResolveServerName + Send + Sync> =
|
|
137
|
+
if let Some(sni_hostname) = sni_hostname {
|
|
138
|
+
let server_name = rustls::pki_types::ServerName::try_from(sni_hostname.to_owned())
|
|
139
|
+
.map_err(|error| Error::Tls(format!("invalid SNI hostname: {error}")))?;
|
|
140
|
+
Arc::new(hyper_rustls::FixedServerNameResolver::new(server_name))
|
|
141
|
+
} else {
|
|
142
|
+
Arc::new(hyper_rustls::DefaultServerNameResolver::default())
|
|
143
|
+
};
|
|
144
|
+
Ok(hyper_rustls::HttpsConnector::new(
|
|
145
|
+
crate::transport::dialer::DialerConnector::new(dialer),
|
|
146
|
+
Arc::new(config),
|
|
147
|
+
false,
|
|
148
|
+
resolver,
|
|
149
|
+
))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[cfg(all(not(feature = "tls-rustls"), feature = "advanced-routing"))]
|
|
153
|
+
pub(super) fn build_custom_connector(
|
|
154
|
+
_config: (),
|
|
155
|
+
dialer: Arc<dyn Dialer>,
|
|
156
|
+
_enabler: crate::http_version::HttpVersionPolicyEnabler,
|
|
157
|
+
_sni_hostname: Option<&str>,
|
|
158
|
+
) -> Result<crate::transport::CustomConnector> {
|
|
159
|
+
Ok(crate::transport::dialer::DialerConnector::new(dialer))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Apply the selected HTTP protocol policy to a rustls client configuration.
|
|
163
|
+
///
|
|
164
|
+
/// The standard hyper-rustls connector applies this policy while building its
|
|
165
|
+
/// connector. Custom connectors must set the same ALPN list themselves.
|
|
166
|
+
#[cfg(feature = "tls-rustls")]
|
|
167
|
+
pub(crate) fn configure_tls_alpn(
|
|
168
|
+
mut config: rustls::ClientConfig,
|
|
169
|
+
policy: crate::http_version::HttpVersionPolicyEnabler,
|
|
170
|
+
) -> rustls::ClientConfig {
|
|
171
|
+
config.alpn_protocols.clear();
|
|
172
|
+
if policy.enable_http2() {
|
|
173
|
+
config.alpn_protocols.push(b"h2".to_vec());
|
|
174
|
+
}
|
|
175
|
+
if policy.enable_http1() {
|
|
176
|
+
config.alpn_protocols.push(b"http/1.1".to_vec());
|
|
177
|
+
}
|
|
178
|
+
config
|
|
179
|
+
}
|