eggfetch 0.2.0__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.0 → eggfetch-0.2.2}/Cargo.lock +11 -6
- eggfetch-0.2.2/PKG-INFO +152 -0
- eggfetch-0.2.2/README.md +125 -0
- {eggfetch-0.2.0 → 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.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/auth.rs +4 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/body.rs +20 -11
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/client.rs +23 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/compression.rs +14 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/cookie.rs +16 -6
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/error.rs +471 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/lib.rs +1 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/multipart.rs +5 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/network_stream.rs +9 -3
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/mod.rs +22 -6
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/prepare.rs +26 -5
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/proxy_dispatch.rs +8 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/redirect.rs +11 -7
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/retry.rs +60 -8
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/route.rs +6 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pool.rs +15 -5
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy.rs +8 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/redact.rs +34 -6
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/redirect.rs +4 -2
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/response.rs +23 -2
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/retry.rs +57 -6
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/body_timeout.rs +2 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/write_timeout.rs +4 -2
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/timeout.rs +54 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/tls.rs +8 -5
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/connect.rs +64 -7
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/proxy.rs +50 -15
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/socks.rs +20 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport_hints.rs +4 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/adverse_tests.rs +1 -1
- {eggfetch-0.2.0 → 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.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/public_api_contracts.rs +12 -0
- eggfetch-0.2.2/crates/eggfetch-core/tests/tls_response_completeness.rs +943 -0
- {eggfetch-0.2.0 → 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.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/target.rs +23 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/tests/wire.rs +13 -1
- {eggfetch-0.2.0 → 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.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/async_client.rs +1 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/client.rs +17 -13
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/conversion.rs +85 -11
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/cookies.rs +59 -10
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/errors.rs +65 -11
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/headers.rs +43 -8
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/lib.rs +10 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/limits.rs +7 -3
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/multipart.rs +81 -19
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/network_stream.rs +95 -46
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/request_preparation.rs +32 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/response.rs +16 -24
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/retry.rs +13 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/async_iterators.rs +22 -10
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/sync_bridge.rs +36 -8
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/sync_iterators.rs +43 -4
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/timeout.rs +52 -17
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/tls.rs +5 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/trace_bridge.rs +56 -17
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_cookies.py +20 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_websocket.py +27 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_ssl_context_translation.py +21 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/conftest.py +14 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_differential.py +19 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_sync.py +10 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/pyproject.toml +1 -1
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/_ssl_context.py +17 -6
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_client.py +6 -2
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_cookies.py +1 -2
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_api.py +50 -8
- eggfetch-0.2.0/PKG-INFO +0 -271
- eggfetch-0.2.0/README.md +0 -244
- eggfetch-0.2.0/crates/eggfetch-core/README.md +0 -244
- eggfetch-0.2.0/crates/eggfetch-http-connect/README.md +0 -244
- eggfetch-0.2.0/crates/eggfetch-python/README.md +0 -244
- {eggfetch-0.2.0 → eggfetch-0.2.2}/Cargo.toml +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/examples/quickstart.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/config.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/connectors.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/client/routes.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/h2_headers.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/headers.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/http_origin.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/http_version.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/limits.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/finalize.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/h3_dispatch.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/hyper_dispatch.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/pipeline/lean.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/environment.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/identity.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/proxy/no_proxy.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/request.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/response_decode.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/service.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/stream/mod.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/trace.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/alt_svc.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/connect_timeout.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/dialer.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/direct.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/direct_connector.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/http3.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/hyper_client.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/lifecycle.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/metrics.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/mod.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/standard_resolver.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/src/transport/uds.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/cross_feature_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/custom_dialer_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/direct_transport_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_alt_svc_discovery.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_hardening.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_integration.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/h3_interop_qualification.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/http2_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/http3_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/lean_policy_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/lean_route_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_http_body_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_json.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/native_tower_service_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/network_stream_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/pool_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/proxy_env_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/proxy_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/redirect_strict_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/request_failure_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/resolved_route_cache_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/resource_stabilization.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/response_body_public_shape.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/retry_integration.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/streaming_decompression_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/test_server.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/timeout_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/tls_fixtures.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/tls_integration.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/total_body_deadline_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/trailer_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-core/tests/transport_metrics_tests.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/error.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/lib.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/request.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-http-connect/src/response.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/auth.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/extensions.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/proxy.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/decoding.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming/state.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/src/streaming.rs +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/conftest.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/fixtures.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/native_fixtures.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_asgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth_input_normalization.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_auth_replay.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_backends.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_behavior_cases.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client_mutability_and_state.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_client_stream_overrides.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_config_objects.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_cookie_scope_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_and_proxy_trust_safety.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_network_proof.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_02_extensions_and_wire_metadata.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_03_network_stream_upgrade.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_corrective_kernel.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_downstream_portfolio.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_downstream_runner_negative.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_environment.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_exceptions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_extensions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_h2_differential.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_headers.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hook_auth_ordering.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hook_cookie_auth_ordering.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_hooks.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_api_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_behavior.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx2_sse.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_extras.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_reference_pinning.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_httpx_required.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_imports.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_lifecycle.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_lossy_conversions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_merge_lossless.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_merge_matrix.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_mock_transport.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_mounts.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_multihop_cleanup.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_native_proxy_tls.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_native_timeout_classification.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_no_proxy_differential.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_oracle_negative.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_protocol_and_unsupported_options.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_proxy_differential.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_proxy_phase05_tracks.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_raw_stream_httpx_differential.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_raw_stream_lifecycle.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_redirect_state_machine_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_construction_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_stream_state.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_request_streaming.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_resource_assertions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_encoding_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_metadata_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_status_and_redirect_state.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_stream_state_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_response_streaming.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_shutdown.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_soak.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_socks_transport.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_ssl_context_network_proof.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_stream_resources.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_integration.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_proxysis.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_timeout_reference_differential.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_top_level_helpers_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_trace_detection.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_transports.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_uds_transport.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_url_query.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/compat/test_wsgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/native_api_manifest.json +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_async.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_auth.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_close_races.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_cookie_auth.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_cookies.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_http2.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_interpreter_shutdown.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_native_api.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_parity.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_proxy.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_python_typing_surface.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_redirects.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_response_compat.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_retry.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/test_streaming.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/compat_consumer.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/consumer.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/negative_sync_async_body.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/crates/eggfetch-python/tests/typing/negative_verify_and_return.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/__init__.pyi +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/_native.pyi +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/__init__.pyi +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_asgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_auth.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_diagnostics.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_exceptions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_headers.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_limits.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_mock.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_proxy.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_request.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_response.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_ssl_context.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_status_codes.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_stream.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_timeout.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_transports.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_urls.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx/_wsgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/__init__.pyi +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_alias.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_api.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_asgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_auth.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_client.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_config.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_cookies.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_exceptions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_headers.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_mock.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_request.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_response.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_sse.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_status_codes.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_stream.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_transports.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_urls.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/_wsgi.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/__init__.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_exceptions.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_ping.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/compat/httpx2/websockets/_transport.py +0 -0
- {eggfetch-0.2.0 → eggfetch-0.2.2}/python/eggfetch/py.typed +0 -0
|
@@ -495,13 +495,17 @@ dependencies = [
|
|
|
495
495
|
"futures-core",
|
|
496
496
|
"futures-util",
|
|
497
497
|
"http",
|
|
498
|
+
"http-body",
|
|
499
|
+
"http-body-util",
|
|
500
|
+
"hyper",
|
|
501
|
+
"hyper-util",
|
|
498
502
|
"tokio",
|
|
499
503
|
"url",
|
|
500
504
|
]
|
|
501
505
|
|
|
502
506
|
[[package]]
|
|
503
507
|
name = "eggfetch-cli"
|
|
504
|
-
version = "0.2.
|
|
508
|
+
version = "0.2.2"
|
|
505
509
|
dependencies = [
|
|
506
510
|
"anyhow",
|
|
507
511
|
"bytes",
|
|
@@ -518,7 +522,7 @@ dependencies = [
|
|
|
518
522
|
|
|
519
523
|
[[package]]
|
|
520
524
|
name = "eggfetch-core"
|
|
521
|
-
version = "0.2.
|
|
525
|
+
version = "0.2.2"
|
|
522
526
|
dependencies = [
|
|
523
527
|
"async-compression",
|
|
524
528
|
"base64 0.23.1",
|
|
@@ -564,7 +568,7 @@ dependencies = [
|
|
|
564
568
|
|
|
565
569
|
[[package]]
|
|
566
570
|
name = "eggfetch-ffi"
|
|
567
|
-
version = "0.2.
|
|
571
|
+
version = "0.2.2"
|
|
568
572
|
dependencies = [
|
|
569
573
|
"bytes",
|
|
570
574
|
"eggfetch-core",
|
|
@@ -576,7 +580,7 @@ dependencies = [
|
|
|
576
580
|
|
|
577
581
|
[[package]]
|
|
578
582
|
name = "eggfetch-http-connect"
|
|
579
|
-
version = "0.2.
|
|
583
|
+
version = "0.2.2"
|
|
580
584
|
dependencies = [
|
|
581
585
|
"base64 0.23.1",
|
|
582
586
|
"thiserror",
|
|
@@ -585,7 +589,7 @@ dependencies = [
|
|
|
585
589
|
|
|
586
590
|
[[package]]
|
|
587
591
|
name = "eggfetch-node"
|
|
588
|
-
version = "0.2.
|
|
592
|
+
version = "0.2.2"
|
|
589
593
|
dependencies = [
|
|
590
594
|
"eggfetch-ffi",
|
|
591
595
|
"napi",
|
|
@@ -597,7 +601,7 @@ dependencies = [
|
|
|
597
601
|
|
|
598
602
|
[[package]]
|
|
599
603
|
name = "eggfetch-python"
|
|
600
|
-
version = "0.2.
|
|
604
|
+
version = "0.2.2"
|
|
601
605
|
dependencies = [
|
|
602
606
|
"bytes",
|
|
603
607
|
"eggfetch-core",
|
|
@@ -928,6 +932,7 @@ dependencies = [
|
|
|
928
932
|
"http",
|
|
929
933
|
"http-body",
|
|
930
934
|
"httparse",
|
|
935
|
+
"httpdate",
|
|
931
936
|
"itoa",
|
|
932
937
|
"pin-project-lite",
|
|
933
938
|
"smallvec",
|
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);
|