eggfetch 0.2.1__tar.gz → 0.2.2__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.2.1 → eggfetch-0.2.2}/Cargo.lock +6 -6
- eggfetch-0.2.2/PKG-INFO +152 -0
- eggfetch-0.2.2/README.md +125 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/Cargo.toml +2 -2
- eggfetch-0.2.2/crates/eggfetch-core/README.md +125 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/auth.rs +4 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/body.rs +20 -11
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/client.rs +23 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/compression.rs +14 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/cookie.rs +16 -6
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/error.rs +471 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/lib.rs +1 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/multipart.rs +5 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/network_stream.rs +9 -3
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/mod.rs +22 -6
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/prepare.rs +26 -5
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/proxy_dispatch.rs +8 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/redirect.rs +11 -7
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/retry.rs +60 -8
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/route.rs +6 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pool.rs +15 -5
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy.rs +8 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/redact.rs +34 -6
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/redirect.rs +4 -2
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/response.rs +23 -2
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/retry.rs +57 -6
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/body_timeout.rs +2 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/write_timeout.rs +4 -2
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/timeout.rs +54 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/tls.rs +8 -5
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/connect.rs +64 -7
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/proxy.rs +50 -15
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/socks.rs +20 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport_hints.rs +4 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/adverse_tests.rs +1 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/integration.rs +1 -1
- eggfetch-0.2.2/crates/eggfetch-core/tests/native_transport_failure_classification.rs +429 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/public_api_contracts.rs +12 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/Cargo.toml +1 -1
- eggfetch-0.2.2/crates/eggfetch-http-connect/README.md +125 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/target.rs +23 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/tests/wire.rs +13 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/Cargo.toml +2 -2
- eggfetch-0.2.2/crates/eggfetch-python/README.md +125 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/async_client.rs +1 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/client.rs +17 -13
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/conversion.rs +85 -11
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/cookies.rs +59 -10
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/errors.rs +65 -11
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/headers.rs +43 -8
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/lib.rs +10 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/limits.rs +7 -3
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/multipart.rs +81 -19
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/network_stream.rs +95 -46
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/request_preparation.rs +32 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/response.rs +16 -24
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/retry.rs +13 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/async_iterators.rs +22 -10
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/sync_bridge.rs +36 -8
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/sync_iterators.rs +43 -4
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/timeout.rs +52 -17
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/tls.rs +5 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/trace_bridge.rs +56 -17
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_cookies.py +20 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_websocket.py +27 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_ssl_context_translation.py +21 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/conftest.py +14 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_differential.py +19 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_sync.py +10 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/pyproject.toml +1 -1
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/_ssl_context.py +17 -6
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_client.py +6 -2
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_cookies.py +1 -2
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_api.py +50 -8
- eggfetch-0.2.1/PKG-INFO +0 -271
- eggfetch-0.2.1/README.md +0 -244
- eggfetch-0.2.1/crates/eggfetch-core/README.md +0 -244
- eggfetch-0.2.1/crates/eggfetch-http-connect/README.md +0 -244
- eggfetch-0.2.1/crates/eggfetch-python/README.md +0 -244
- {eggfetch-0.2.1 → eggfetch-0.2.2}/Cargo.toml +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/examples/quickstart.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/config.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/connectors.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/routes.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/h2_headers.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/headers.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/http_origin.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/http_version.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/limits.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/finalize.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/h3_dispatch.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/hyper_dispatch.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/lean.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/environment.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/identity.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/no_proxy.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/request.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/response_decode.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/service.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/mod.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/trace.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/alt_svc.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/connect_timeout.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/dialer.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/direct.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/direct_connector.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/http3.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/hyper_client.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/lifecycle.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/metrics.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/mod.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/standard_resolver.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/uds.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/cross_feature_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/custom_dialer_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/direct_transport_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_alt_svc_discovery.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_hardening.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_integration.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_interop_qualification.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/http2_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/http3_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/lean_policy_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/lean_route_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_http_body_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_json.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_tower_service_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/network_stream_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/pool_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/proxy_env_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/proxy_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/redirect_strict_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/request_failure_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/resolved_route_cache_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/resource_stabilization.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/response_body_public_shape.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/retry_integration.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/streaming_decompression_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/test_server.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/timeout_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/tls_fixtures.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/tls_integration.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/tls_response_completeness.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/total_body_deadline_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/trailer_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-core/tests/transport_metrics_tests.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/error.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/lib.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/request.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/response.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/auth.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/extensions.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/proxy.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/decoding.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/state.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming.rs +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/conftest.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/fixtures.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/native_fixtures.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_asgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth_input_normalization.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth_replay.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_backends.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_behavior_cases.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client_mutability_and_state.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client_stream_overrides.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_config_objects.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_cookie_scope_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_and_proxy_trust_safety.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_network_proof.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_02_extensions_and_wire_metadata.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_03_network_stream_upgrade.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_kernel.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_downstream_portfolio.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_downstream_runner_negative.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_environment.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_exceptions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_extensions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_h2_differential.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_headers.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hook_auth_ordering.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hook_cookie_auth_ordering.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hooks.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_api_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_behavior.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_sse.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_extras.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_reference_pinning.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_required.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_imports.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_lifecycle.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_lossy_conversions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_merge_lossless.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_merge_matrix.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_mock_transport.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_mounts.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_multihop_cleanup.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_native_proxy_tls.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_native_timeout_classification.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_no_proxy_differential.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_oracle_negative.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_protocol_and_unsupported_options.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_proxy_differential.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_proxy_phase05_tracks.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_raw_stream_httpx_differential.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_raw_stream_lifecycle.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_redirect_state_machine_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_construction_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_stream_state.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_streaming.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_resource_assertions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_encoding_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_metadata_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_status_and_redirect_state.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_stream_state_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_streaming.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_shutdown.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_soak.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_socks_transport.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_ssl_context_network_proof.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_stream_resources.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_integration.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_proxysis.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_reference_differential.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_top_level_helpers_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_trace_detection.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_transports.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_uds_transport.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_url_query.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_wsgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/native_api_manifest.json +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_async.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_auth.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_close_races.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_cookie_auth.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_cookies.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_http2.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_interpreter_shutdown.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_native_api.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_parity.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_proxy.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_python_typing_surface.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_redirects.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_response_compat.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_retry.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_streaming.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/compat_consumer.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/consumer.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/negative_sync_async_body.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/negative_verify_and_return.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/__init__.pyi +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/_native.pyi +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/__init__.pyi +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_asgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_auth.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_diagnostics.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_exceptions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_headers.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_limits.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_mock.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_proxy.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_request.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_response.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_ssl_context.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_status_codes.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_stream.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_timeout.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_transports.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_urls.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_wsgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/__init__.pyi +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_alias.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_api.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_asgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_auth.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_client.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_config.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_cookies.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_exceptions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_headers.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_mock.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_request.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_response.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_sse.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_status_codes.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_stream.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_transports.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_urls.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_wsgi.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/__init__.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_exceptions.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_ping.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_transport.py +0 -0
- {eggfetch-0.2.1 → eggfetch-0.2.2}/python/eggfetch/py.typed +0 -0
|
@@ -505,7 +505,7 @@ dependencies = [
|
|
|
505
505
|
|
|
506
506
|
[[package]]
|
|
507
507
|
name = "eggfetch-cli"
|
|
508
|
-
version = "0.2.
|
|
508
|
+
version = "0.2.2"
|
|
509
509
|
dependencies = [
|
|
510
510
|
"anyhow",
|
|
511
511
|
"bytes",
|
|
@@ -522,7 +522,7 @@ dependencies = [
|
|
|
522
522
|
|
|
523
523
|
[[package]]
|
|
524
524
|
name = "eggfetch-core"
|
|
525
|
-
version = "0.2.
|
|
525
|
+
version = "0.2.2"
|
|
526
526
|
dependencies = [
|
|
527
527
|
"async-compression",
|
|
528
528
|
"base64 0.23.1",
|
|
@@ -568,7 +568,7 @@ dependencies = [
|
|
|
568
568
|
|
|
569
569
|
[[package]]
|
|
570
570
|
name = "eggfetch-ffi"
|
|
571
|
-
version = "0.2.
|
|
571
|
+
version = "0.2.2"
|
|
572
572
|
dependencies = [
|
|
573
573
|
"bytes",
|
|
574
574
|
"eggfetch-core",
|
|
@@ -580,7 +580,7 @@ dependencies = [
|
|
|
580
580
|
|
|
581
581
|
[[package]]
|
|
582
582
|
name = "eggfetch-http-connect"
|
|
583
|
-
version = "0.2.
|
|
583
|
+
version = "0.2.2"
|
|
584
584
|
dependencies = [
|
|
585
585
|
"base64 0.23.1",
|
|
586
586
|
"thiserror",
|
|
@@ -589,7 +589,7 @@ dependencies = [
|
|
|
589
589
|
|
|
590
590
|
[[package]]
|
|
591
591
|
name = "eggfetch-node"
|
|
592
|
-
version = "0.2.
|
|
592
|
+
version = "0.2.2"
|
|
593
593
|
dependencies = [
|
|
594
594
|
"eggfetch-ffi",
|
|
595
595
|
"napi",
|
|
@@ -601,7 +601,7 @@ dependencies = [
|
|
|
601
601
|
|
|
602
602
|
[[package]]
|
|
603
603
|
name = "eggfetch-python"
|
|
604
|
-
version = "0.2.
|
|
604
|
+
version = "0.2.2"
|
|
605
605
|
dependencies = [
|
|
606
606
|
"bytes",
|
|
607
607
|
"eggfetch-core",
|
eggfetch-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eggfetch
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
12
|
+
Classifier: Programming Language :: Rust
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Requires-Dist: pytest>=8.4 ; extra == 'test'
|
|
15
|
+
Requires-Dist: pytest-asyncio>=1.0 ; extra == 'test'
|
|
16
|
+
Provides-Extra: test
|
|
17
|
+
Summary: Python bindings for eggfetch
|
|
18
|
+
Keywords: http,client,httpx,async,rust
|
|
19
|
+
Home-Page: https://github.com/eggstack/eggfetch
|
|
20
|
+
Author: eggfetch contributors
|
|
21
|
+
License: MIT
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
24
|
+
Project-URL: Homepage, https://github.com/eggstack/eggfetch
|
|
25
|
+
Project-URL: Repository, https://github.com/eggstack/eggfetch
|
|
26
|
+
|
|
27
|
+
# eggfetch
|
|
28
|
+
|
|
29
|
+
[](https://github.com/eggstack/eggfetch/actions/workflows/ci.yml)
|
|
30
|
+
[](https://crates.io/crates/eggfetch-core)
|
|
31
|
+
[](https://crates.io/crates/d/eggfetch-core)
|
|
32
|
+
[](https://pypi.org/project/eggfetch/)
|
|
33
|
+
[](https://pepy.tech/projects/eggfetch)
|
|
34
|
+
[](LICENSE-MIT)
|
|
35
|
+
|
|
36
|
+
eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python bindings and a CLI. All networking lives in `eggfetch-core`; the Python sync API blocks on the async engine while releasing the GIL, and the async API integrates with asyncio.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **HTTP/1.1, HTTP/2, and experimental HTTP/3** with ALPN negotiation
|
|
41
|
+
- **Streaming** request and response bodies with backpressure, plus trailers
|
|
42
|
+
- **Pooling and phase-aware timeouts** (pool/connect/write/read/total) with transport metrics
|
|
43
|
+
- **TLS** via rustls: additive or replacement CA roots, mTLS, version policy ([TLS](docs/concepts/tls.md))
|
|
44
|
+
- **Proxy**: HTTP forwarding, HTTPS CONNECT, proxy auth, `NO_PROXY`, SOCKS5 ([proxy](docs/concepts/proxy.md))
|
|
45
|
+
- **Cookies, auth, multipart, compression**: RFC 6265 jar, Basic/Bearer, streaming uploads, gzip/brotli/zstd/deflate
|
|
46
|
+
- **Retries and redirects**: backoff with `Retry-After`, replayable-body redirect handling
|
|
47
|
+
- **Python**: requests/HTTPX-compatible sync and async APIs plus versioned `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0) facades ([compatibility](docs/reference/compatibility.md))
|
|
48
|
+
- **CLI** with streaming output, machine-readable formats, and shell completions ([guide](docs/cli/guide.md))
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install eggfetch # Python 3.10–3.15
|
|
54
|
+
cargo install eggfetch-cli # CLI
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```toml
|
|
58
|
+
[dependencies]
|
|
59
|
+
eggfetch-core = "0.2" # Rust (MSRV 1.89)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
See [installation](docs/getting-started/installation.md) for wheels, feature recipes, and platform support.
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import eggfetch
|
|
68
|
+
|
|
69
|
+
r = eggfetch.get("https://httpbin.org/get")
|
|
70
|
+
print(r.status_code, r.json())
|
|
71
|
+
|
|
72
|
+
with eggfetch.Client(headers={"User-Agent": "my-app/1.0"}) as client:
|
|
73
|
+
r = client.post("https://httpbin.org/post", json={"key": "value"})
|
|
74
|
+
print(r.status_code)
|
|
75
|
+
|
|
76
|
+
with client.stream("GET", "https://httpbin.org/stream-bytes/10000") as r:
|
|
77
|
+
for chunk in r.iter_bytes():
|
|
78
|
+
print(f"chunk: {len(chunk)} bytes")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import asyncio
|
|
83
|
+
import eggfetch
|
|
84
|
+
|
|
85
|
+
async def main():
|
|
86
|
+
async with eggfetch.AsyncClient() as client:
|
|
87
|
+
responses = await asyncio.gather(
|
|
88
|
+
client.get("https://httpbin.org/get"),
|
|
89
|
+
client.get("https://httpbin.org/ip"),
|
|
90
|
+
)
|
|
91
|
+
for resp in responses:
|
|
92
|
+
print(resp.status_code, resp.json())
|
|
93
|
+
|
|
94
|
+
asyncio.run(main())
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```rust
|
|
98
|
+
use eggfetch_core::{Client, Timeout};
|
|
99
|
+
use futures_util::StreamExt;
|
|
100
|
+
|
|
101
|
+
#[tokio::main]
|
|
102
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
103
|
+
let client = Client::builder()
|
|
104
|
+
.timeout(Timeout::from_secs(30))
|
|
105
|
+
.follow_redirects(true)
|
|
106
|
+
.user_agent("my-app/1.0")
|
|
107
|
+
.build();
|
|
108
|
+
|
|
109
|
+
let mut resp = client.get("https://httpbin.org/get")?.send().await?;
|
|
110
|
+
println!("Status: {}", resp.status());
|
|
111
|
+
println!("Body: {}", resp.text().await?);
|
|
112
|
+
|
|
113
|
+
let mut resp = client.get("https://httpbin.org/stream/3")?.send().await?;
|
|
114
|
+
let mut stream = resp.bytes_stream()?;
|
|
115
|
+
while let Some(chunk) = stream.next().await {
|
|
116
|
+
println!("chunk: {} bytes", chunk?.len());
|
|
117
|
+
}
|
|
118
|
+
Ok(())
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
eggfetch https://httpbin.org/get
|
|
124
|
+
eggfetch -X POST https://httpbin.org/post --json '{"key": "value"}'
|
|
125
|
+
eggfetch --auth user:pass https://httpbin.org/basic-auth/user/pass
|
|
126
|
+
eggfetch --output file.bin https://httpbin.org/stream-bytes/10000
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
Runnable starting points (each takes an optional base URL, default `https://httpbin.org`):
|
|
132
|
+
|
|
133
|
+
- `cargo run -p eggfetch-core --example quickstart` — configured GET/POST, timeout override, auth, streaming
|
|
134
|
+
- `python examples/python_sync.py` — sync client, JSON POST, streaming download
|
|
135
|
+
- `python examples/python_async.py` — async client with concurrent requests
|
|
136
|
+
|
|
137
|
+
More patterns are in [`docs/cookbook/`](docs/cookbook/).
|
|
138
|
+
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
- [Getting started](docs/getting-started/quickstart.md) — installation and first requests
|
|
142
|
+
- [Rust guide](docs/rust/guide.md) · [Python guide](docs/python/guide.md) · [CLI guide](docs/cli/guide.md)
|
|
143
|
+
- [Migration](docs/migration/from-httpx.md) from HTTPX/requests · [Compatibility](docs/reference/compatibility.md) · [C ABI / FFI](docs/ffi/)
|
|
144
|
+
|
|
145
|
+
## Security
|
|
146
|
+
|
|
147
|
+
All `Debug`/`Display`/error output redacts credentials, cookies, and tokens. See [SECURITY.md](SECURITY.md) to report a vulnerability.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
eggfetch is licensed under the [MIT License](LICENSE-MIT).
|
|
152
|
+
|
eggfetch-0.2.2/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# eggfetch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eggstack/eggfetch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://crates.io/crates/eggfetch-core)
|
|
5
|
+
[](https://crates.io/crates/d/eggfetch-core)
|
|
6
|
+
[](https://pypi.org/project/eggfetch/)
|
|
7
|
+
[](https://pepy.tech/projects/eggfetch)
|
|
8
|
+
[](LICENSE-MIT)
|
|
9
|
+
|
|
10
|
+
eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python bindings and a CLI. All networking lives in `eggfetch-core`; the Python sync API blocks on the async engine while releasing the GIL, and the async API integrates with asyncio.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **HTTP/1.1, HTTP/2, and experimental HTTP/3** with ALPN negotiation
|
|
15
|
+
- **Streaming** request and response bodies with backpressure, plus trailers
|
|
16
|
+
- **Pooling and phase-aware timeouts** (pool/connect/write/read/total) with transport metrics
|
|
17
|
+
- **TLS** via rustls: additive or replacement CA roots, mTLS, version policy ([TLS](docs/concepts/tls.md))
|
|
18
|
+
- **Proxy**: HTTP forwarding, HTTPS CONNECT, proxy auth, `NO_PROXY`, SOCKS5 ([proxy](docs/concepts/proxy.md))
|
|
19
|
+
- **Cookies, auth, multipart, compression**: RFC 6265 jar, Basic/Bearer, streaming uploads, gzip/brotli/zstd/deflate
|
|
20
|
+
- **Retries and redirects**: backoff with `Retry-After`, replayable-body redirect handling
|
|
21
|
+
- **Python**: requests/HTTPX-compatible sync and async APIs plus versioned `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0) facades ([compatibility](docs/reference/compatibility.md))
|
|
22
|
+
- **CLI** with streaming output, machine-readable formats, and shell completions ([guide](docs/cli/guide.md))
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install eggfetch # Python 3.10–3.15
|
|
28
|
+
cargo install eggfetch-cli # CLI
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```toml
|
|
32
|
+
[dependencies]
|
|
33
|
+
eggfetch-core = "0.2" # Rust (MSRV 1.89)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
See [installation](docs/getting-started/installation.md) for wheels, feature recipes, and platform support.
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import eggfetch
|
|
42
|
+
|
|
43
|
+
r = eggfetch.get("https://httpbin.org/get")
|
|
44
|
+
print(r.status_code, r.json())
|
|
45
|
+
|
|
46
|
+
with eggfetch.Client(headers={"User-Agent": "my-app/1.0"}) as client:
|
|
47
|
+
r = client.post("https://httpbin.org/post", json={"key": "value"})
|
|
48
|
+
print(r.status_code)
|
|
49
|
+
|
|
50
|
+
with client.stream("GET", "https://httpbin.org/stream-bytes/10000") as r:
|
|
51
|
+
for chunk in r.iter_bytes():
|
|
52
|
+
print(f"chunk: {len(chunk)} bytes")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
import eggfetch
|
|
58
|
+
|
|
59
|
+
async def main():
|
|
60
|
+
async with eggfetch.AsyncClient() as client:
|
|
61
|
+
responses = await asyncio.gather(
|
|
62
|
+
client.get("https://httpbin.org/get"),
|
|
63
|
+
client.get("https://httpbin.org/ip"),
|
|
64
|
+
)
|
|
65
|
+
for resp in responses:
|
|
66
|
+
print(resp.status_code, resp.json())
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
use eggfetch_core::{Client, Timeout};
|
|
73
|
+
use futures_util::StreamExt;
|
|
74
|
+
|
|
75
|
+
#[tokio::main]
|
|
76
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
77
|
+
let client = Client::builder()
|
|
78
|
+
.timeout(Timeout::from_secs(30))
|
|
79
|
+
.follow_redirects(true)
|
|
80
|
+
.user_agent("my-app/1.0")
|
|
81
|
+
.build();
|
|
82
|
+
|
|
83
|
+
let mut resp = client.get("https://httpbin.org/get")?.send().await?;
|
|
84
|
+
println!("Status: {}", resp.status());
|
|
85
|
+
println!("Body: {}", resp.text().await?);
|
|
86
|
+
|
|
87
|
+
let mut resp = client.get("https://httpbin.org/stream/3")?.send().await?;
|
|
88
|
+
let mut stream = resp.bytes_stream()?;
|
|
89
|
+
while let Some(chunk) = stream.next().await {
|
|
90
|
+
println!("chunk: {} bytes", chunk?.len());
|
|
91
|
+
}
|
|
92
|
+
Ok(())
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
eggfetch https://httpbin.org/get
|
|
98
|
+
eggfetch -X POST https://httpbin.org/post --json '{"key": "value"}'
|
|
99
|
+
eggfetch --auth user:pass https://httpbin.org/basic-auth/user/pass
|
|
100
|
+
eggfetch --output file.bin https://httpbin.org/stream-bytes/10000
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
Runnable starting points (each takes an optional base URL, default `https://httpbin.org`):
|
|
106
|
+
|
|
107
|
+
- `cargo run -p eggfetch-core --example quickstart` — configured GET/POST, timeout override, auth, streaming
|
|
108
|
+
- `python examples/python_sync.py` — sync client, JSON POST, streaming download
|
|
109
|
+
- `python examples/python_async.py` — async client with concurrent requests
|
|
110
|
+
|
|
111
|
+
More patterns are in [`docs/cookbook/`](docs/cookbook/).
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
- [Getting started](docs/getting-started/quickstart.md) — installation and first requests
|
|
116
|
+
- [Rust guide](docs/rust/guide.md) · [Python guide](docs/python/guide.md) · [CLI guide](docs/cli/guide.md)
|
|
117
|
+
- [Migration](docs/migration/from-httpx.md) from HTTPX/requests · [Compatibility](docs/reference/compatibility.md) · [C ABI / FFI](docs/ffi/)
|
|
118
|
+
|
|
119
|
+
## Security
|
|
120
|
+
|
|
121
|
+
All `Debug`/`Display`/error output redacts credentials, cookies, and tokens. See [SECURITY.md](SECURITY.md) to report a vulnerability.
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
eggfetch is licensed under the [MIT License](LICENSE-MIT).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "eggfetch-core"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
edition.workspace = true
|
|
5
5
|
rust-version.workspace = true
|
|
6
6
|
license.workspace = true
|
|
@@ -82,7 +82,7 @@ test-util = ["tokio/test-util"]
|
|
|
82
82
|
|
|
83
83
|
[dependencies]
|
|
84
84
|
bytes = "1"
|
|
85
|
-
eggfetch-http-connect = { version = "0.2.
|
|
85
|
+
eggfetch-http-connect = { version = "0.2.2", 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"
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# eggfetch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eggstack/eggfetch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://crates.io/crates/eggfetch-core)
|
|
5
|
+
[](https://crates.io/crates/d/eggfetch-core)
|
|
6
|
+
[](https://pypi.org/project/eggfetch/)
|
|
7
|
+
[](https://pepy.tech/projects/eggfetch)
|
|
8
|
+
[](LICENSE-MIT)
|
|
9
|
+
|
|
10
|
+
eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python bindings and a CLI. All networking lives in `eggfetch-core`; the Python sync API blocks on the async engine while releasing the GIL, and the async API integrates with asyncio.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **HTTP/1.1, HTTP/2, and experimental HTTP/3** with ALPN negotiation
|
|
15
|
+
- **Streaming** request and response bodies with backpressure, plus trailers
|
|
16
|
+
- **Pooling and phase-aware timeouts** (pool/connect/write/read/total) with transport metrics
|
|
17
|
+
- **TLS** via rustls: additive or replacement CA roots, mTLS, version policy ([TLS](docs/concepts/tls.md))
|
|
18
|
+
- **Proxy**: HTTP forwarding, HTTPS CONNECT, proxy auth, `NO_PROXY`, SOCKS5 ([proxy](docs/concepts/proxy.md))
|
|
19
|
+
- **Cookies, auth, multipart, compression**: RFC 6265 jar, Basic/Bearer, streaming uploads, gzip/brotli/zstd/deflate
|
|
20
|
+
- **Retries and redirects**: backoff with `Retry-After`, replayable-body redirect handling
|
|
21
|
+
- **Python**: requests/HTTPX-compatible sync and async APIs plus versioned `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0) facades ([compatibility](docs/reference/compatibility.md))
|
|
22
|
+
- **CLI** with streaming output, machine-readable formats, and shell completions ([guide](docs/cli/guide.md))
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install eggfetch # Python 3.10–3.15
|
|
28
|
+
cargo install eggfetch-cli # CLI
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```toml
|
|
32
|
+
[dependencies]
|
|
33
|
+
eggfetch-core = "0.2" # Rust (MSRV 1.89)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
See [installation](docs/getting-started/installation.md) for wheels, feature recipes, and platform support.
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import eggfetch
|
|
42
|
+
|
|
43
|
+
r = eggfetch.get("https://httpbin.org/get")
|
|
44
|
+
print(r.status_code, r.json())
|
|
45
|
+
|
|
46
|
+
with eggfetch.Client(headers={"User-Agent": "my-app/1.0"}) as client:
|
|
47
|
+
r = client.post("https://httpbin.org/post", json={"key": "value"})
|
|
48
|
+
print(r.status_code)
|
|
49
|
+
|
|
50
|
+
with client.stream("GET", "https://httpbin.org/stream-bytes/10000") as r:
|
|
51
|
+
for chunk in r.iter_bytes():
|
|
52
|
+
print(f"chunk: {len(chunk)} bytes")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
import eggfetch
|
|
58
|
+
|
|
59
|
+
async def main():
|
|
60
|
+
async with eggfetch.AsyncClient() as client:
|
|
61
|
+
responses = await asyncio.gather(
|
|
62
|
+
client.get("https://httpbin.org/get"),
|
|
63
|
+
client.get("https://httpbin.org/ip"),
|
|
64
|
+
)
|
|
65
|
+
for resp in responses:
|
|
66
|
+
print(resp.status_code, resp.json())
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
use eggfetch_core::{Client, Timeout};
|
|
73
|
+
use futures_util::StreamExt;
|
|
74
|
+
|
|
75
|
+
#[tokio::main]
|
|
76
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
77
|
+
let client = Client::builder()
|
|
78
|
+
.timeout(Timeout::from_secs(30))
|
|
79
|
+
.follow_redirects(true)
|
|
80
|
+
.user_agent("my-app/1.0")
|
|
81
|
+
.build();
|
|
82
|
+
|
|
83
|
+
let mut resp = client.get("https://httpbin.org/get")?.send().await?;
|
|
84
|
+
println!("Status: {}", resp.status());
|
|
85
|
+
println!("Body: {}", resp.text().await?);
|
|
86
|
+
|
|
87
|
+
let mut resp = client.get("https://httpbin.org/stream/3")?.send().await?;
|
|
88
|
+
let mut stream = resp.bytes_stream()?;
|
|
89
|
+
while let Some(chunk) = stream.next().await {
|
|
90
|
+
println!("chunk: {} bytes", chunk?.len());
|
|
91
|
+
}
|
|
92
|
+
Ok(())
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
eggfetch https://httpbin.org/get
|
|
98
|
+
eggfetch -X POST https://httpbin.org/post --json '{"key": "value"}'
|
|
99
|
+
eggfetch --auth user:pass https://httpbin.org/basic-auth/user/pass
|
|
100
|
+
eggfetch --output file.bin https://httpbin.org/stream-bytes/10000
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
Runnable starting points (each takes an optional base URL, default `https://httpbin.org`):
|
|
106
|
+
|
|
107
|
+
- `cargo run -p eggfetch-core --example quickstart` — configured GET/POST, timeout override, auth, streaming
|
|
108
|
+
- `python examples/python_sync.py` — sync client, JSON POST, streaming download
|
|
109
|
+
- `python examples/python_async.py` — async client with concurrent requests
|
|
110
|
+
|
|
111
|
+
More patterns are in [`docs/cookbook/`](docs/cookbook/).
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
- [Getting started](docs/getting-started/quickstart.md) — installation and first requests
|
|
116
|
+
- [Rust guide](docs/rust/guide.md) · [Python guide](docs/python/guide.md) · [CLI guide](docs/cli/guide.md)
|
|
117
|
+
- [Migration](docs/migration/from-httpx.md) from HTTPX/requests · [Compatibility](docs/reference/compatibility.md) · [C ABI / FFI](docs/ffi/)
|
|
118
|
+
|
|
119
|
+
## Security
|
|
120
|
+
|
|
121
|
+
All `Debug`/`Display`/error output redacts credentials, cookies, and tokens. See [SECURITY.md](SECURITY.md) to report a vulnerability.
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
eggfetch is licensed under the [MIT License](LICENSE-MIT).
|
|
@@ -264,6 +264,10 @@ fn validate_auth_bytes(bytes: &[u8], field_name: &str) -> Result<()> {
|
|
|
264
264
|
/// If the request has an explicit `Authorization` header AND auth is
|
|
265
265
|
/// configured, returns `Err` to avoid ambiguity (the user must choose
|
|
266
266
|
/// one or the other).
|
|
267
|
+
///
|
|
268
|
+
/// Note: `proxy-authorization` conflicts are enforced separately at the
|
|
269
|
+
/// proxy dispatch layer (`pipeline/proxy_dispatch.rs`), which sees the
|
|
270
|
+
/// effective proxy config; this function only governs origin auth.
|
|
267
271
|
pub(crate) fn resolve_request_auth(
|
|
268
272
|
request_auth: Option<&AuthScheme>,
|
|
269
273
|
request_auth_disabled: bool,
|
|
@@ -81,6 +81,12 @@ pub enum RequestBody {
|
|
|
81
81
|
/// the user has not supplied a conflicting value. If `length` is
|
|
82
82
|
/// `None`, no `Content-Length` is set; the transport selects a
|
|
83
83
|
/// safe transfer mode (chunked for HTTP/1.1).
|
|
84
|
+
///
|
|
85
|
+
/// Note: stream bodies are never replayable, even with
|
|
86
|
+
/// `length = Some(n)` — the bytes are consumed on first send and
|
|
87
|
+
/// retry/redirect degrades to a single attempt
|
|
88
|
+
/// (`BodyNotReplayableForRedirect` / no retry). Callers needing
|
|
89
|
+
/// retried uploads must buffer into `Bytes` first.
|
|
84
90
|
Stream {
|
|
85
91
|
/// The stream of body chunks.
|
|
86
92
|
stream: BoxBytesStream,
|
|
@@ -143,7 +149,10 @@ impl RequestBody {
|
|
|
143
149
|
/// Stream bodies may not have a known length until fully consumed.
|
|
144
150
|
/// Returns `0` for unknown-length streams; callers that need to
|
|
145
151
|
/// distinguish "empty" from "unknown length" must check
|
|
146
|
-
/// [`has_known_length`](Self::has_known_length) first
|
|
152
|
+
/// [`has_known_length`](Self::has_known_length) first and must not use
|
|
153
|
+
/// `len() == 0` to decide emptiness (use [`is_empty`](Self::is_empty),
|
|
154
|
+
/// which returns `false` for streams). Internal Content-Length logic
|
|
155
|
+
/// matches on the `Stream { length }` variant directly, never on `len()`.
|
|
147
156
|
#[must_use]
|
|
148
157
|
pub fn len(&self) -> usize {
|
|
149
158
|
match self {
|
|
@@ -372,18 +381,16 @@ impl http_body::Body for NativeRequestBody {
|
|
|
372
381
|
if let Some(timeout) = *this.write_timeout {
|
|
373
382
|
if !*this.started {
|
|
374
383
|
*this.started = true;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
.reset(tokio::time::Instant::now() + timeout);
|
|
384
|
+
let next = crate::timeout::saturating_tokio_now_plus(timeout);
|
|
385
|
+
this.timer.as_mut().reset(next);
|
|
378
386
|
}
|
|
379
387
|
}
|
|
380
388
|
|
|
381
389
|
match this.inner.as_mut().poll_frame(cx) {
|
|
382
390
|
Poll::Ready(Some(Ok(frame))) => {
|
|
383
391
|
if let Some(timeout) = *this.write_timeout {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
.reset(tokio::time::Instant::now() + timeout);
|
|
392
|
+
let next = crate::timeout::saturating_tokio_now_plus(timeout);
|
|
393
|
+
this.timer.as_mut().reset(next);
|
|
387
394
|
}
|
|
388
395
|
Poll::Ready(Some(Ok(frame)))
|
|
389
396
|
}
|
|
@@ -555,9 +562,8 @@ impl http_body::Body for NativeResponseBody {
|
|
|
555
562
|
if let (Some(read_sleep), Some(timeout)) =
|
|
556
563
|
(this.read_sleep.as_mut(), this.read_timeout.as_ref())
|
|
557
564
|
{
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
.reset(tokio::time::Instant::now() + *timeout);
|
|
565
|
+
let next = crate::timeout::saturating_tokio_now_plus(*timeout);
|
|
566
|
+
read_sleep.as_mut().reset(next);
|
|
561
567
|
}
|
|
562
568
|
Poll::Ready(Some(Ok(frame)))
|
|
563
569
|
}
|
|
@@ -709,7 +715,10 @@ impl SharedTrailers {
|
|
|
709
715
|
/// before expecting trailers.
|
|
710
716
|
#[must_use]
|
|
711
717
|
pub fn get(&self) -> Option<http::HeaderMap> {
|
|
712
|
-
self.inner
|
|
718
|
+
self.inner
|
|
719
|
+
.lock()
|
|
720
|
+
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
|
721
|
+
.clone()
|
|
713
722
|
}
|
|
714
723
|
}
|
|
715
724
|
|
|
@@ -1276,18 +1276,37 @@ impl ClientBuilder {
|
|
|
1276
1276
|
#[cfg(feature = "advanced-routing")]
|
|
1277
1277
|
let custom_client = if let Some(dialer) = self.dialer.clone() {
|
|
1278
1278
|
#[cfg(feature = "tls-rustls")]
|
|
1279
|
-
let custom_config = self
|
|
1279
|
+
let custom_config = match self
|
|
1280
1280
|
.tls_config
|
|
1281
1281
|
.clone()
|
|
1282
1282
|
.unwrap_or_default()
|
|
1283
1283
|
.build_rustls_config()
|
|
1284
|
-
|
|
1284
|
+
{
|
|
1285
|
+
Ok(config) => Some(config),
|
|
1286
|
+
Err(e) => {
|
|
1287
|
+
#[cfg(feature = "tracing")]
|
|
1288
|
+
tracing::warn!("eggfetch: custom-route TLS config failed: {e}");
|
|
1289
|
+
#[cfg(not(feature = "tracing"))]
|
|
1290
|
+
let _ = &e;
|
|
1291
|
+
None
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1285
1294
|
#[cfg(not(feature = "tls-rustls"))]
|
|
1286
1295
|
let custom_config = Some(());
|
|
1287
1296
|
|
|
1288
1297
|
custom_config.and_then(|custom_config| {
|
|
1289
|
-
let connector =
|
|
1290
|
-
|
|
1298
|
+
let connector = match build_custom_connector(custom_config, dialer, enabler, None) {
|
|
1299
|
+
Ok(connector) => connector,
|
|
1300
|
+
Err(e) => {
|
|
1301
|
+
// Surface the root cause: returning `None` here falls
|
|
1302
|
+
// back to the standard route with no diagnostic.
|
|
1303
|
+
#[cfg(feature = "tracing")]
|
|
1304
|
+
tracing::warn!("eggfetch: custom connector build failed: {e}");
|
|
1305
|
+
#[cfg(not(feature = "tracing"))]
|
|
1306
|
+
let _ = &e;
|
|
1307
|
+
return None;
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1291
1310
|
let connect_timeout = self.timeout.as_ref().and_then(|timeout| timeout.connect);
|
|
1292
1311
|
Some(build_hyper_client(
|
|
1293
1312
|
connector,
|
|
@@ -200,6 +200,12 @@ pub fn accept_encoding_value() -> Option<&'static str> {
|
|
|
200
200
|
/// `identity` tokens (RFC 9110 §8.4, meaning "no encoding") are no-ops
|
|
201
201
|
/// and are skipped.
|
|
202
202
|
///
|
|
203
|
+
/// Unknown tokens are silently dropped here: this function must only be
|
|
204
|
+
/// called after [`validate_content_encodings`] has rejected unsupported
|
|
205
|
+
/// codings (see `decompress_stream` / `finalize.rs`). Keep the two
|
|
206
|
+
/// coupled — calling this alone on unvalidated input would silently
|
|
207
|
+
/// ignore an encoding instead of erroring.
|
|
208
|
+
///
|
|
203
209
|
/// Returns `None` if the header is empty, contains only whitespace, or
|
|
204
210
|
/// contains only no-op codings such as bare `identity`.
|
|
205
211
|
pub(crate) fn parse_content_encodings(header: &str) -> Option<Vec<ContentCoding>> {
|
|
@@ -888,7 +894,14 @@ impl LimitingStream {
|
|
|
888
894
|
return Err(LimitExceededKind::Ratio);
|
|
889
895
|
}
|
|
890
896
|
let compressed = self.compressed_counter.load(Ordering::Acquire);
|
|
891
|
-
if compressed
|
|
897
|
+
if compressed == 0 {
|
|
898
|
+
// No compressed bytes observed yet: any decoded output
|
|
899
|
+
// already exceeds any finite ratio, so fail closed instead
|
|
900
|
+
// of emitting an unbounded zip-bomb prefix up to the chunk size.
|
|
901
|
+
if self.decoded_bytes > 0 {
|
|
902
|
+
return Err(LimitExceededKind::Ratio);
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
892
905
|
let ratio = self.decoded_bytes as f64 / compressed as f64;
|
|
893
906
|
if ratio > max_ratio {
|
|
894
907
|
return Err(LimitExceededKind::Ratio);
|