eggfetch 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {eggfetch-0.1.3 → eggfetch-0.1.4}/Cargo.lock +7 -8
- eggfetch-0.1.4/PKG-INFO +240 -0
- eggfetch-0.1.4/README.md +215 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/Cargo.toml +29 -18
- eggfetch-0.1.4/crates/eggfetch-core/README.md +215 -0
- eggfetch-0.1.4/crates/eggfetch-core/examples/quickstart.rs +75 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/client.rs +213 -101
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/error.rs +22 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/lib.rs +3 -1
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/network_stream.rs +9 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/pipeline.rs +152 -14
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/request.rs +267 -1
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/response.rs +74 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/retry.rs +4 -1
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/tls.rs +41 -22
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/connect.rs +9 -18
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/direct.rs +2 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/direct_connector.rs +70 -17
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/http3.rs +3 -16
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/mod.rs +10 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/proxy.rs +3 -8
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/uds.rs +28 -3
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/direct_transport_tests.rs +127 -0
- eggfetch-0.1.4/crates/eggfetch-core/tests/native_json.rs +273 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/tls_integration.rs +25 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/Cargo.toml +2 -2
- eggfetch-0.1.4/crates/eggfetch-python/README.md +215 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/errors.rs +6 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/pyproject.toml +1 -1
- eggfetch-0.1.3/PKG-INFO +0 -392
- eggfetch-0.1.3/README.md +0 -367
- eggfetch-0.1.3/crates/eggfetch-core/README.md +0 -367
- eggfetch-0.1.3/crates/eggfetch-python/README.md +0 -367
- {eggfetch-0.1.3 → eggfetch-0.1.4}/Cargo.toml +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/auth.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/body.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/compression.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/config.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/cookie.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/h2_headers.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/headers.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/http_version.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/limits.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/multipart.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/pool.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/proxy.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/redact.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/redirect.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/response_decode.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/stream/mod.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/stream/read_timeout.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/stream/write_timeout.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/timeout.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/trace.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/alt_svc.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/connect_timeout.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/metrics.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/src/transport/socks.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/adverse_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/cross_feature_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/h3_alt_svc_discovery.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/h3_hardening.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/h3_integration.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/h3_interop_qualification.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/http2_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/http3_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/integration.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/network_stream_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/pool_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/proxy_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/resource_stabilization.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/retry_integration.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/test_server.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/timeout_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/tls_fixtures.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/trailer_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-core/tests/transport_metrics_tests.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/async_client.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/auth.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/client.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/conversion.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/cookies.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/extensions.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/headers.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/lib.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/limits.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/multipart.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/network_stream.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/proxy.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/response.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/retry.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/streaming.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/timeout.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/tls.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/src/trace_bridge.rs +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/conftest.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/fixtures.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/native_fixtures.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_asgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_auth.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_auth_input_normalization.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_auth_replay.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_backends.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_behavior_cases.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_client.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_client_mutability_and_state.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_client_stream_overrides.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_config_objects.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_cookie_scope_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_cookies.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_and_proxy_trust_safety.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_corrective_01_tls_network_proof.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_corrective_02_extensions_and_wire_metadata.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_corrective_03_network_stream_upgrade.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_corrective_kernel.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_downstream_portfolio.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_downstream_runner_negative.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_environment.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_exceptions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_extensions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_h2_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_headers.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_hook_auth_ordering.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_hook_cookie_auth_ordering.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_hooks.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx2_api_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx2_behavior.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx2_sse.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx2_websocket.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx_extras.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx_reference_pinning.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_httpx_required.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_imports.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_lifecycle.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_lossy_conversions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_merge_lossless.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_merge_matrix.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_mock_transport.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_mounts.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_multihop_cleanup.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_native_proxy_tls.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_native_timeout_classification.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_no_proxy_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_oracle_negative.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_protocol_and_unsupported_options.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_proxy_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_proxy_phase05_tracks.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_raw_stream_httpx_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_raw_stream_lifecycle.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_redirect_state_machine_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_request.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_request_construction_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_request_stream_state.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_request_streaming.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_resource_assertions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response_encoding_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response_metadata_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response_status_and_redirect_state.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response_stream_state_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_response_streaming.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_shutdown.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_soak.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_socks_transport.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_ssl_context_network_proof.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_ssl_context_translation.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_stream_resources.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_timeout_integration.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_timeout_proxysis.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_timeout_reference_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_top_level_helpers_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_trace_detection.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_transports.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_uds_transport.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_url_query.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/compat/test_wsgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/conftest.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_async.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_auth.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_close_races.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_cookie_auth.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_cookies.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_differential.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_http2.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_interpreter_shutdown.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_parity.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_proxy.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_redirects.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_response_compat.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_retry.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_streaming.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/crates/eggfetch-python/tests/test_sync.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_asgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_auth.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_client.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_cookies.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_diagnostics.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_exceptions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_headers.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_limits.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_mock.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_proxy.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_request.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_response.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_ssl_context.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_status_codes.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_stream.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_timeout.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_transports.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_urls.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx/_wsgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_alias.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_api.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_asgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_auth.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_client.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_config.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_cookies.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_exceptions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_headers.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_mock.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_request.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_response.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_sse.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_status_codes.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_stream.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_transports.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_urls.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/_wsgi.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/websockets/__init__.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/websockets/_api.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/websockets/_exceptions.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/websockets/_ping.py +0 -0
- {eggfetch-0.1.3 → eggfetch-0.1.4}/python/eggfetch/compat/httpx2/websockets/_transport.py +0 -0
|
@@ -509,7 +509,7 @@ dependencies = [
|
|
|
509
509
|
|
|
510
510
|
[[package]]
|
|
511
511
|
name = "eggfetch-cli"
|
|
512
|
-
version = "0.1.
|
|
512
|
+
version = "0.1.4"
|
|
513
513
|
dependencies = [
|
|
514
514
|
"anyhow",
|
|
515
515
|
"bytes",
|
|
@@ -526,7 +526,7 @@ dependencies = [
|
|
|
526
526
|
|
|
527
527
|
[[package]]
|
|
528
528
|
name = "eggfetch-core"
|
|
529
|
-
version = "0.1.
|
|
529
|
+
version = "0.1.4"
|
|
530
530
|
dependencies = [
|
|
531
531
|
"async-compression",
|
|
532
532
|
"base64",
|
|
@@ -544,7 +544,6 @@ dependencies = [
|
|
|
544
544
|
"http",
|
|
545
545
|
"http-body",
|
|
546
546
|
"http-body-util",
|
|
547
|
-
"httparse",
|
|
548
547
|
"httpdate",
|
|
549
548
|
"hyper",
|
|
550
549
|
"hyper-rustls",
|
|
@@ -557,6 +556,8 @@ dependencies = [
|
|
|
557
556
|
"rcgen",
|
|
558
557
|
"rustls",
|
|
559
558
|
"rustls-native-certs",
|
|
559
|
+
"serde",
|
|
560
|
+
"serde_json",
|
|
560
561
|
"tempfile",
|
|
561
562
|
"thiserror",
|
|
562
563
|
"tokio",
|
|
@@ -571,7 +572,7 @@ dependencies = [
|
|
|
571
572
|
|
|
572
573
|
[[package]]
|
|
573
574
|
name = "eggfetch-ffi"
|
|
574
|
-
version = "0.1.
|
|
575
|
+
version = "0.1.4"
|
|
575
576
|
dependencies = [
|
|
576
577
|
"bytes",
|
|
577
578
|
"eggfetch-core",
|
|
@@ -583,7 +584,7 @@ dependencies = [
|
|
|
583
584
|
|
|
584
585
|
[[package]]
|
|
585
586
|
name = "eggfetch-node"
|
|
586
|
-
version = "0.1.
|
|
587
|
+
version = "0.1.4"
|
|
587
588
|
dependencies = [
|
|
588
589
|
"eggfetch-ffi",
|
|
589
590
|
"napi",
|
|
@@ -595,7 +596,7 @@ dependencies = [
|
|
|
595
596
|
|
|
596
597
|
[[package]]
|
|
597
598
|
name = "eggfetch-python"
|
|
598
|
-
version = "0.1.
|
|
599
|
+
version = "0.1.4"
|
|
599
600
|
dependencies = [
|
|
600
601
|
"bytes",
|
|
601
602
|
"eggfetch-core",
|
|
@@ -954,11 +955,9 @@ dependencies = [
|
|
|
954
955
|
"hyper-util",
|
|
955
956
|
"log",
|
|
956
957
|
"rustls",
|
|
957
|
-
"rustls-native-certs",
|
|
958
958
|
"tokio",
|
|
959
959
|
"tokio-rustls",
|
|
960
960
|
"tower-service",
|
|
961
|
-
"webpki-roots 1.0.8",
|
|
962
961
|
]
|
|
963
962
|
|
|
964
963
|
[[package]]
|
eggfetch-0.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eggfetch
|
|
3
|
+
Version: 0.1.4
|
|
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 :: Rust
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Requires-Dist: pytest>=8.4 ; extra == 'test'
|
|
13
|
+
Requires-Dist: pytest-asyncio>=1.0 ; extra == 'test'
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Summary: Python bindings for eggfetch
|
|
16
|
+
Keywords: http,client,httpx,async,rust
|
|
17
|
+
Home-Page: https://github.com/eggstack/eggfetch
|
|
18
|
+
Author: eggfetch contributors
|
|
19
|
+
License: MIT
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
Project-URL: Homepage, https://github.com/eggstack/eggfetch
|
|
23
|
+
Project-URL: Repository, https://github.com/eggstack/eggfetch
|
|
24
|
+
|
|
25
|
+
# eggfetch
|
|
26
|
+
|
|
27
|
+
[](https://github.com/eggstack/eggfetch/actions/workflows/ci.yml)
|
|
28
|
+
[](https://crates.io/crates/eggfetch-core)
|
|
29
|
+
[](https://crates.io/crates/eggfetch-core)
|
|
30
|
+
[](https://pypi.org/project/eggfetch/)
|
|
31
|
+
[](https://pepy.tech/projects/eggfetch)
|
|
32
|
+
[](LICENSE-MIT)
|
|
33
|
+
|
|
34
|
+
eggfetch is a Rust-native async HTTP client engine (tokio + hyper) with Python bindings and a CLI. There is exactly one networking implementation, living entirely in `eggfetch-core`; the Python sync API blocks on the async engine while releasing the GIL, and the async API integrates with asyncio.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **HTTP/1.1, HTTP/2, HTTP/3** — ALPN negotiation; HTTP/3 over QUIC stays experimental ([graduation gate](docs/architecture/core-tls-proxy-protocols.md))
|
|
39
|
+
- **Streaming** — request/response bodies without eager buffering (`bytes_stream()`, `text_lines()`); trailers via `Response::trailers()` ([guide](docs/rust/guide.md))
|
|
40
|
+
- **Pooling, timeouts, observability** — per-origin in-flight limits, phase-aware timeouts (pool/connect/write/read/total), connector/DNS/TLS and H3 transport metrics ([pool/timeouts](docs/architecture/core-timeout-pool.md))
|
|
41
|
+
- **TLS** — rustls with custom CA bundles, mTLS client certs, version policy, verification toggle ([TLS](docs/concepts/tls.md))
|
|
42
|
+
- **Proxy** — HTTP forwarding, HTTPS CONNECT, proxy auth, per-request override, `NO_PROXY`; SOCKS5 and UDS routes ([proxy](docs/concepts/proxy.md))
|
|
43
|
+
- **Cookies, auth, multipart** — RFC 6265 jar, Basic/Bearer with redaction, streaming multipart uploads ([cookies](docs/concepts/cookies.md))
|
|
44
|
+
- **Retries and redirects** — policy-driven backoff with `Retry-After`, replayable-body redirect handling ([retry](docs/concepts/retry.md))
|
|
45
|
+
- **Compression** — feature-gated streaming gzip/brotli/zstd/deflate with zip-bomb limits ([compression](docs/concepts/compression.md))
|
|
46
|
+
- **Native Rust JSON (opt-in)** — replayable `RequestBuilder::json()`, single-consumption `Response::json()` via the `json` feature ([guide](docs/rust/guide.md))
|
|
47
|
+
- **Python API** — requests/HTTPX-compatible sync and async interfaces ([guide](docs/python/guide.md)), plus versioned `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0) facades ([compatibility](#httpx-compatibility))
|
|
48
|
+
- **Upgrades** — 101 responses expose an owned `network_stream` (WebSocket/SSE building blocks); CONNECT tunnels stay body-iterator only
|
|
49
|
+
- **CLI** — streaming output, machine-readable formats, shell completions ([guide](docs/cli/guide.md))
|
|
50
|
+
- **C ABI and Node.js prototype** — opaque-handle FFI plus an experimental N-API wrapper ([ffi-and-node](docs/architecture/ffi-and-node.md))
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
**Python:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install eggfetch
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Rust:**
|
|
61
|
+
|
|
62
|
+
```toml
|
|
63
|
+
[dependencies]
|
|
64
|
+
eggfetch-core = { version = "0.1", features = ["http1", "tls-rustls", "tls-native-roots"] }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See the [feature profile matrix](docs/architecture/feature-flags.md#supported-core-profiles) for minimal, deterministic, and embedded recipes.
|
|
68
|
+
|
|
69
|
+
**CLI:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cargo install eggfetch-cli
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Usage -- Python
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import eggfetch
|
|
79
|
+
|
|
80
|
+
r = eggfetch.get("https://httpbin.org/get")
|
|
81
|
+
print(r.status_code)
|
|
82
|
+
print(r.text)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import eggfetch
|
|
87
|
+
|
|
88
|
+
with eggfetch.Client(headers={"User-Agent": "my-app/1.0"}) as client:
|
|
89
|
+
r = client.get("https://httpbin.org/get")
|
|
90
|
+
print(r.json())
|
|
91
|
+
|
|
92
|
+
r = client.post("https://httpbin.org/post", json={"key": "value"})
|
|
93
|
+
print(r.status_code)
|
|
94
|
+
|
|
95
|
+
with client.stream("GET", "https://httpbin.org/stream-bytes/10000") as r:
|
|
96
|
+
for chunk in r.iter_bytes():
|
|
97
|
+
print(f"chunk: {len(chunk)} bytes")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
import eggfetch
|
|
103
|
+
|
|
104
|
+
async def main():
|
|
105
|
+
async with eggfetch.AsyncClient() as client:
|
|
106
|
+
r = await client.get("https://httpbin.org/get")
|
|
107
|
+
print(r.status_code)
|
|
108
|
+
|
|
109
|
+
responses = await asyncio.gather(
|
|
110
|
+
client.get("https://httpbin.org/get"),
|
|
111
|
+
client.get("https://httpbin.org/ip"),
|
|
112
|
+
)
|
|
113
|
+
for resp in responses:
|
|
114
|
+
print(resp.json())
|
|
115
|
+
|
|
116
|
+
asyncio.run(main())
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import eggfetch
|
|
121
|
+
|
|
122
|
+
client = eggfetch.Client(
|
|
123
|
+
timeout=10.0,
|
|
124
|
+
headers={"User-Agent": "my-app/1.0"},
|
|
125
|
+
limits=eggfetch.Limits(max_connections=100),
|
|
126
|
+
verify="/path/to/ca-bundle.pem", # custom CA bundle
|
|
127
|
+
cert=("/path/to/cert.pem", "/path/to/key.pem"), # mTLS
|
|
128
|
+
proxy="http://proxy:8080",
|
|
129
|
+
http2=True,
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Versioned HTTPX-compatible facades over the same engine:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from eggfetch.compat.httpx import Client # HTTPX 0.28.1 surface
|
|
137
|
+
from eggfetch.compat.httpx2 import Client as H2Client # httpx2 2.12.0 surface
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
See [`docs/python/guide.md`](docs/python/guide.md) for the full Python API reference.
|
|
141
|
+
|
|
142
|
+
## Usage -- Rust
|
|
143
|
+
|
|
144
|
+
```rust
|
|
145
|
+
use eggfetch_core::Client;
|
|
146
|
+
use futures_util::StreamExt;
|
|
147
|
+
|
|
148
|
+
#[tokio::main]
|
|
149
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
150
|
+
let client = Client::builder()
|
|
151
|
+
.timeout(eggfetch_core::Timeout::from_secs(30))
|
|
152
|
+
.follow_redirects(true)
|
|
153
|
+
.user_agent("my-app/1.0")
|
|
154
|
+
.build();
|
|
155
|
+
|
|
156
|
+
let mut resp = client.get("https://httpbin.org/get").send().await?;
|
|
157
|
+
println!("Status: {}", resp.status());
|
|
158
|
+
println!("Body: {}", resp.text().await?);
|
|
159
|
+
|
|
160
|
+
// Streaming body
|
|
161
|
+
let mut resp = client.get("https://httpbin.org/stream/3").send().await?;
|
|
162
|
+
let mut stream = resp.bytes_stream()?;
|
|
163
|
+
while let Some(chunk) = stream.next().await {
|
|
164
|
+
println!("chunk: {} bytes", chunk?.len());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Ok(())
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The opt-in `json` feature adds `RequestBuilder::json()` / `Response::json()` Serde helpers, and `resolved_addresses()` pins caller-validated destinations without a second DNS lookup. See [`docs/rust/guide.md`](docs/rust/guide.md) for the full Rust API reference.
|
|
172
|
+
|
|
173
|
+
## Usage -- CLI
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# GET request
|
|
177
|
+
eggfetch https://httpbin.org/get
|
|
178
|
+
|
|
179
|
+
# POST JSON
|
|
180
|
+
eggfetch -X POST https://httpbin.org/post --json '{"key": "value"}'
|
|
181
|
+
|
|
182
|
+
# With authentication
|
|
183
|
+
eggfetch --auth user:pass https://httpbin.org/basic-auth/user/pass
|
|
184
|
+
|
|
185
|
+
# Streaming download
|
|
186
|
+
eggfetch --output file.bin https://httpbin.org/stream-bytes/10000
|
|
187
|
+
|
|
188
|
+
# Machine-readable output
|
|
189
|
+
eggfetch --json-output https://httpbin.org/get
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
See [`docs/cli/guide.md`](docs/cli/guide.md) for the full CLI reference.
|
|
193
|
+
|
|
194
|
+
## Examples
|
|
195
|
+
|
|
196
|
+
Runnable starting points (each takes an optional base URL argument, default `https://httpbin.org`):
|
|
197
|
+
|
|
198
|
+
- [`crates/eggfetch-core/examples/quickstart.rs`](crates/eggfetch-core/examples/quickstart.rs) — core client: configured GET/POST, timeout override, auth, streaming (`cargo run -p eggfetch-core --example quickstart`)
|
|
199
|
+
- [`examples/python_sync.py`](examples/python_sync.py) — sync client, JSON POST, streaming download
|
|
200
|
+
- [`examples/python_async.py`](examples/python_async.py) — async client with concurrent requests
|
|
201
|
+
|
|
202
|
+
More patterns are in [`docs/cookbook/`](docs/cookbook/).
|
|
203
|
+
|
|
204
|
+
## HTTPX Compatibility
|
|
205
|
+
|
|
206
|
+
Two versioned, independent facades over the single Rust engine — `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0, adds `FunctionAuth`, `Origin`/`URL.origin`, `QUERY`, SSE, optional WebSocket). Both are Stage C qualified on frozen executable SHA `22a6f5c0dc0207c1356b6143c0eda3d4075063b0`; HTTPX 1.0 preview under `compat/httpx/1.0-preview/` is reconnaissance only.
|
|
207
|
+
|
|
208
|
+
See [`docs/reference/compatibility.md`](docs/reference/compatibility.md) for the full feature matrix and retained differences.
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
| Section | Description |
|
|
213
|
+
|---------|-------------|
|
|
214
|
+
| [getting-started/](docs/getting-started/) | Installation and quickstart guide |
|
|
215
|
+
| [concepts/](docs/concepts/) | Architecture, lifecycle, timeouts, streaming, cookies, auth, proxy, TLS |
|
|
216
|
+
| [rust/guide.md](docs/rust/guide.md) | Rust API guide with examples |
|
|
217
|
+
| [python/guide.md](docs/python/guide.md) | Python sync/async API guide |
|
|
218
|
+
| [cli/guide.md](docs/cli/guide.md) | CLI reference and usage guide |
|
|
219
|
+
| [migration/](docs/migration/) | Migration guides from requests and HTTPX |
|
|
220
|
+
| [cookbook/](docs/cookbook/) | Practical runnable examples |
|
|
221
|
+
| [reference/](docs/reference/) | Compatibility matrix, feature matrix, error reference |
|
|
222
|
+
| [security/](docs/security/) | Security guidelines and troubleshooting |
|
|
223
|
+
| [architecture/](docs/architecture/) | Internal architecture documentation |
|
|
224
|
+
| [ffi/](docs/ffi/) | C ABI and FFI binding guide |
|
|
225
|
+
|
|
226
|
+
## Security
|
|
227
|
+
|
|
228
|
+
- **Dependency auditing:** `cargo-deny` configured in `deny.toml`
|
|
229
|
+
- **Secret redaction:** all `Debug`/`Display`/error output redacts credentials, cookies, bearer tokens, and proxy passwords
|
|
230
|
+
- **Threat model:** see [docs/architecture/threat-model.md](docs/architecture/threat-model.md)
|
|
231
|
+
- **Vulnerability reporting:** see [SECURITY.md](SECURITY.md)
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
eggfetch is licensed under the [MIT License](LICENSE-MIT).
|
|
236
|
+
|
|
237
|
+
## MSRV
|
|
238
|
+
|
|
239
|
+
The minimum supported Rust version is **1.80**, declared in `workspace.package.rust-version` and checked in extended validation. `rust-toolchain.toml` pins the stable channel for development.
|
|
240
|
+
|
eggfetch-0.1.4/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
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/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. There is exactly one networking implementation, living entirely 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, HTTP/3** — ALPN negotiation; HTTP/3 over QUIC stays experimental ([graduation gate](docs/architecture/core-tls-proxy-protocols.md))
|
|
15
|
+
- **Streaming** — request/response bodies without eager buffering (`bytes_stream()`, `text_lines()`); trailers via `Response::trailers()` ([guide](docs/rust/guide.md))
|
|
16
|
+
- **Pooling, timeouts, observability** — per-origin in-flight limits, phase-aware timeouts (pool/connect/write/read/total), connector/DNS/TLS and H3 transport metrics ([pool/timeouts](docs/architecture/core-timeout-pool.md))
|
|
17
|
+
- **TLS** — rustls with custom CA bundles, mTLS client certs, version policy, verification toggle ([TLS](docs/concepts/tls.md))
|
|
18
|
+
- **Proxy** — HTTP forwarding, HTTPS CONNECT, proxy auth, per-request override, `NO_PROXY`; SOCKS5 and UDS routes ([proxy](docs/concepts/proxy.md))
|
|
19
|
+
- **Cookies, auth, multipart** — RFC 6265 jar, Basic/Bearer with redaction, streaming multipart uploads ([cookies](docs/concepts/cookies.md))
|
|
20
|
+
- **Retries and redirects** — policy-driven backoff with `Retry-After`, replayable-body redirect handling ([retry](docs/concepts/retry.md))
|
|
21
|
+
- **Compression** — feature-gated streaming gzip/brotli/zstd/deflate with zip-bomb limits ([compression](docs/concepts/compression.md))
|
|
22
|
+
- **Native Rust JSON (opt-in)** — replayable `RequestBuilder::json()`, single-consumption `Response::json()` via the `json` feature ([guide](docs/rust/guide.md))
|
|
23
|
+
- **Python API** — requests/HTTPX-compatible sync and async interfaces ([guide](docs/python/guide.md)), plus versioned `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0) facades ([compatibility](#httpx-compatibility))
|
|
24
|
+
- **Upgrades** — 101 responses expose an owned `network_stream` (WebSocket/SSE building blocks); CONNECT tunnels stay body-iterator only
|
|
25
|
+
- **CLI** — streaming output, machine-readable formats, shell completions ([guide](docs/cli/guide.md))
|
|
26
|
+
- **C ABI and Node.js prototype** — opaque-handle FFI plus an experimental N-API wrapper ([ffi-and-node](docs/architecture/ffi-and-node.md))
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
**Python:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install eggfetch
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Rust:**
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
[dependencies]
|
|
40
|
+
eggfetch-core = { version = "0.1", features = ["http1", "tls-rustls", "tls-native-roots"] }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
See the [feature profile matrix](docs/architecture/feature-flags.md#supported-core-profiles) for minimal, deterministic, and embedded recipes.
|
|
44
|
+
|
|
45
|
+
**CLI:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cargo install eggfetch-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage -- Python
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import eggfetch
|
|
55
|
+
|
|
56
|
+
r = eggfetch.get("https://httpbin.org/get")
|
|
57
|
+
print(r.status_code)
|
|
58
|
+
print(r.text)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import eggfetch
|
|
63
|
+
|
|
64
|
+
with eggfetch.Client(headers={"User-Agent": "my-app/1.0"}) as client:
|
|
65
|
+
r = client.get("https://httpbin.org/get")
|
|
66
|
+
print(r.json())
|
|
67
|
+
|
|
68
|
+
r = client.post("https://httpbin.org/post", json={"key": "value"})
|
|
69
|
+
print(r.status_code)
|
|
70
|
+
|
|
71
|
+
with client.stream("GET", "https://httpbin.org/stream-bytes/10000") as r:
|
|
72
|
+
for chunk in r.iter_bytes():
|
|
73
|
+
print(f"chunk: {len(chunk)} bytes")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import asyncio
|
|
78
|
+
import eggfetch
|
|
79
|
+
|
|
80
|
+
async def main():
|
|
81
|
+
async with eggfetch.AsyncClient() as client:
|
|
82
|
+
r = await client.get("https://httpbin.org/get")
|
|
83
|
+
print(r.status_code)
|
|
84
|
+
|
|
85
|
+
responses = await asyncio.gather(
|
|
86
|
+
client.get("https://httpbin.org/get"),
|
|
87
|
+
client.get("https://httpbin.org/ip"),
|
|
88
|
+
)
|
|
89
|
+
for resp in responses:
|
|
90
|
+
print(resp.json())
|
|
91
|
+
|
|
92
|
+
asyncio.run(main())
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import eggfetch
|
|
97
|
+
|
|
98
|
+
client = eggfetch.Client(
|
|
99
|
+
timeout=10.0,
|
|
100
|
+
headers={"User-Agent": "my-app/1.0"},
|
|
101
|
+
limits=eggfetch.Limits(max_connections=100),
|
|
102
|
+
verify="/path/to/ca-bundle.pem", # custom CA bundle
|
|
103
|
+
cert=("/path/to/cert.pem", "/path/to/key.pem"), # mTLS
|
|
104
|
+
proxy="http://proxy:8080",
|
|
105
|
+
http2=True,
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Versioned HTTPX-compatible facades over the same engine:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from eggfetch.compat.httpx import Client # HTTPX 0.28.1 surface
|
|
113
|
+
from eggfetch.compat.httpx2 import Client as H2Client # httpx2 2.12.0 surface
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
See [`docs/python/guide.md`](docs/python/guide.md) for the full Python API reference.
|
|
117
|
+
|
|
118
|
+
## Usage -- Rust
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
use eggfetch_core::Client;
|
|
122
|
+
use futures_util::StreamExt;
|
|
123
|
+
|
|
124
|
+
#[tokio::main]
|
|
125
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
126
|
+
let client = Client::builder()
|
|
127
|
+
.timeout(eggfetch_core::Timeout::from_secs(30))
|
|
128
|
+
.follow_redirects(true)
|
|
129
|
+
.user_agent("my-app/1.0")
|
|
130
|
+
.build();
|
|
131
|
+
|
|
132
|
+
let mut resp = client.get("https://httpbin.org/get").send().await?;
|
|
133
|
+
println!("Status: {}", resp.status());
|
|
134
|
+
println!("Body: {}", resp.text().await?);
|
|
135
|
+
|
|
136
|
+
// Streaming body
|
|
137
|
+
let mut resp = client.get("https://httpbin.org/stream/3").send().await?;
|
|
138
|
+
let mut stream = resp.bytes_stream()?;
|
|
139
|
+
while let Some(chunk) = stream.next().await {
|
|
140
|
+
println!("chunk: {} bytes", chunk?.len());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Ok(())
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The opt-in `json` feature adds `RequestBuilder::json()` / `Response::json()` Serde helpers, and `resolved_addresses()` pins caller-validated destinations without a second DNS lookup. See [`docs/rust/guide.md`](docs/rust/guide.md) for the full Rust API reference.
|
|
148
|
+
|
|
149
|
+
## Usage -- CLI
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# GET request
|
|
153
|
+
eggfetch https://httpbin.org/get
|
|
154
|
+
|
|
155
|
+
# POST JSON
|
|
156
|
+
eggfetch -X POST https://httpbin.org/post --json '{"key": "value"}'
|
|
157
|
+
|
|
158
|
+
# With authentication
|
|
159
|
+
eggfetch --auth user:pass https://httpbin.org/basic-auth/user/pass
|
|
160
|
+
|
|
161
|
+
# Streaming download
|
|
162
|
+
eggfetch --output file.bin https://httpbin.org/stream-bytes/10000
|
|
163
|
+
|
|
164
|
+
# Machine-readable output
|
|
165
|
+
eggfetch --json-output https://httpbin.org/get
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
See [`docs/cli/guide.md`](docs/cli/guide.md) for the full CLI reference.
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
Runnable starting points (each takes an optional base URL argument, default `https://httpbin.org`):
|
|
173
|
+
|
|
174
|
+
- [`crates/eggfetch-core/examples/quickstart.rs`](crates/eggfetch-core/examples/quickstart.rs) — core client: configured GET/POST, timeout override, auth, streaming (`cargo run -p eggfetch-core --example quickstart`)
|
|
175
|
+
- [`examples/python_sync.py`](examples/python_sync.py) — sync client, JSON POST, streaming download
|
|
176
|
+
- [`examples/python_async.py`](examples/python_async.py) — async client with concurrent requests
|
|
177
|
+
|
|
178
|
+
More patterns are in [`docs/cookbook/`](docs/cookbook/).
|
|
179
|
+
|
|
180
|
+
## HTTPX Compatibility
|
|
181
|
+
|
|
182
|
+
Two versioned, independent facades over the single Rust engine — `eggfetch.compat.httpx` (0.28.1) and `eggfetch.compat.httpx2` (2.12.0, adds `FunctionAuth`, `Origin`/`URL.origin`, `QUERY`, SSE, optional WebSocket). Both are Stage C qualified on frozen executable SHA `22a6f5c0dc0207c1356b6143c0eda3d4075063b0`; HTTPX 1.0 preview under `compat/httpx/1.0-preview/` is reconnaissance only.
|
|
183
|
+
|
|
184
|
+
See [`docs/reference/compatibility.md`](docs/reference/compatibility.md) for the full feature matrix and retained differences.
|
|
185
|
+
|
|
186
|
+
## Documentation
|
|
187
|
+
|
|
188
|
+
| Section | Description |
|
|
189
|
+
|---------|-------------|
|
|
190
|
+
| [getting-started/](docs/getting-started/) | Installation and quickstart guide |
|
|
191
|
+
| [concepts/](docs/concepts/) | Architecture, lifecycle, timeouts, streaming, cookies, auth, proxy, TLS |
|
|
192
|
+
| [rust/guide.md](docs/rust/guide.md) | Rust API guide with examples |
|
|
193
|
+
| [python/guide.md](docs/python/guide.md) | Python sync/async API guide |
|
|
194
|
+
| [cli/guide.md](docs/cli/guide.md) | CLI reference and usage guide |
|
|
195
|
+
| [migration/](docs/migration/) | Migration guides from requests and HTTPX |
|
|
196
|
+
| [cookbook/](docs/cookbook/) | Practical runnable examples |
|
|
197
|
+
| [reference/](docs/reference/) | Compatibility matrix, feature matrix, error reference |
|
|
198
|
+
| [security/](docs/security/) | Security guidelines and troubleshooting |
|
|
199
|
+
| [architecture/](docs/architecture/) | Internal architecture documentation |
|
|
200
|
+
| [ffi/](docs/ffi/) | C ABI and FFI binding guide |
|
|
201
|
+
|
|
202
|
+
## Security
|
|
203
|
+
|
|
204
|
+
- **Dependency auditing:** `cargo-deny` configured in `deny.toml`
|
|
205
|
+
- **Secret redaction:** all `Debug`/`Display`/error output redacts credentials, cookies, bearer tokens, and proxy passwords
|
|
206
|
+
- **Threat model:** see [docs/architecture/threat-model.md](docs/architecture/threat-model.md)
|
|
207
|
+
- **Vulnerability reporting:** see [SECURITY.md](SECURITY.md)
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
eggfetch is licensed under the [MIT License](LICENSE-MIT).
|
|
212
|
+
|
|
213
|
+
## MSRV
|
|
214
|
+
|
|
215
|
+
The minimum supported Rust version is **1.80**, declared in `workspace.package.rust-version` and checked in extended validation. `rust-toolchain.toml` pins the stable channel for development.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "eggfetch-core"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
edition.workspace = true
|
|
5
5
|
rust-version.workspace = true
|
|
6
6
|
license.workspace = true
|
|
@@ -16,18 +16,28 @@ categories = ["network-programming", "web-programming", "asynchronous"]
|
|
|
16
16
|
workspace = true
|
|
17
17
|
|
|
18
18
|
[features]
|
|
19
|
-
default = ["http1", "tls-rustls"]
|
|
20
|
-
http1 = ["hyper/http1", "hyper-util/http1", "hyper-rustls
|
|
21
|
-
http2 = ["dep:h2", "hyper/http2", "hyper-util/http2", "hyper-rustls
|
|
22
|
-
tls-rustls = [
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
default = ["http1", "tls-rustls", "tls-native-roots"]
|
|
20
|
+
http1 = ["hyper/http1", "hyper-util/http1", "hyper-rustls?/http1"]
|
|
21
|
+
http2 = ["dep:h2", "hyper/http2", "hyper-util/http2", "hyper-rustls?/http2"]
|
|
22
|
+
tls-rustls = [
|
|
23
|
+
"dep:hyper-rustls",
|
|
24
|
+
"dep:pem-rfc7468",
|
|
25
|
+
"dep:rustls",
|
|
26
|
+
"dep:tokio-rustls",
|
|
27
|
+
"dep:webpki-roots",
|
|
28
|
+
"hyper-rustls/ring",
|
|
29
|
+
"hyper-rustls/logging",
|
|
30
|
+
"hyper-rustls/tls12",
|
|
31
|
+
]
|
|
32
|
+
tls-native-roots = ["tls-rustls", "dep:rustls-native-certs"]
|
|
33
|
+
http3 = ["http1", "tls-rustls", "dep:quinn", "dep:h3", "dep:h3-quinn"]
|
|
34
|
+
json = ["dep:serde", "dep:serde_json"]
|
|
25
35
|
compression-gzip = ["dep:async-compression", "async-compression/gzip", "dep:tokio-util", "tokio/io-util", "dep:flate2"]
|
|
26
36
|
compression-brotli = ["dep:async-compression", "async-compression/brotli", "dep:tokio-util", "tokio/io-util", "dep:brotli"]
|
|
27
37
|
compression-zstd = ["dep:async-compression", "async-compression/zstd", "dep:tokio-util", "tokio/io-util", "dep:zstd"]
|
|
28
38
|
compression-deflate = ["dep:async-compression", "async-compression/deflate", "dep:tokio-util", "tokio/io-util", "dep:flate2"]
|
|
29
39
|
cookies = ["dep:cookie"]
|
|
30
|
-
proxy = ["tokio/io-util"]
|
|
40
|
+
proxy = ["http1", "tls-rustls", "tokio/io-util"]
|
|
31
41
|
tracing = ["dep:tracing"]
|
|
32
42
|
multipart = []
|
|
33
43
|
test-util = ["tokio/test-util"]
|
|
@@ -40,18 +50,20 @@ futures-util = { version = "0.3", default-features = false, features = ["alloc"]
|
|
|
40
50
|
http = "1"
|
|
41
51
|
http-body = "1"
|
|
42
52
|
http-body-util = "0.1"
|
|
43
|
-
hyper = { version = "1", features = ["client"
|
|
53
|
+
hyper = { version = "1", default-features = false, features = ["client"] }
|
|
44
54
|
h2 = { version = "0.4", optional = true }
|
|
45
|
-
hyper-util = { version = "0.1", features = ["client", "client-legacy", "tokio"
|
|
46
|
-
hyper-rustls = { version = "0.27", default-features = false,
|
|
55
|
+
hyper-util = { version = "0.1", default-features = false, features = ["client", "client-legacy", "tokio"] }
|
|
56
|
+
hyper-rustls = { version = "0.27", default-features = false, optional = true }
|
|
47
57
|
pin-project-lite = "0.2"
|
|
48
58
|
percent-encoding = "2"
|
|
49
|
-
rustls = { version = "0.23", default-features = false, features = ["std",
|
|
50
|
-
rustls-native-certs = "0.8"
|
|
51
|
-
tokio = { version = "1", features = ["rt", "net", "time", "sync", "macros", "
|
|
52
|
-
tokio-rustls = { version = "0.26", default-features = false,
|
|
59
|
+
rustls = { version = "0.23", default-features = false, features = ["std"], optional = true }
|
|
60
|
+
rustls-native-certs = { version = "0.8", optional = true }
|
|
61
|
+
tokio = { version = "1", default-features = false, features = ["rt", "net", "time", "sync", "macros", "io-util"] }
|
|
62
|
+
tokio-rustls = { version = "0.26", default-features = false, optional = true }
|
|
53
63
|
url = "2"
|
|
54
64
|
thiserror = "2"
|
|
65
|
+
serde = { version = "1", optional = true }
|
|
66
|
+
serde_json = { version = "1", optional = true }
|
|
55
67
|
|
|
56
68
|
tower-service = "0.3"
|
|
57
69
|
base64 = "0.22"
|
|
@@ -63,10 +75,9 @@ brotli = { version = "8", optional = true, default-features = false, features =
|
|
|
63
75
|
zstd = { version = "0.13", optional = true }
|
|
64
76
|
tracing = { version = "0.1", optional = true }
|
|
65
77
|
getrandom = "0.2"
|
|
66
|
-
httparse = "1"
|
|
67
78
|
httpdate = "1"
|
|
68
|
-
webpki-roots = "0.26"
|
|
69
|
-
pem-rfc7468 = { version = "0.1", features = ["alloc"] }
|
|
79
|
+
webpki-roots = { version = "0.26", optional = true }
|
|
80
|
+
pem-rfc7468 = { version = "0.1", features = ["alloc"], optional = true }
|
|
70
81
|
quinn = { version = "0.11", default-features = false, features = ["rustls", "ring", "runtime-tokio"], optional = true }
|
|
71
82
|
# Pinned h3 0.0.8: the `i-implement-...` feature only removes
|
|
72
83
|
# `#[non_exhaustive]` from error variants and exposes `ConnectionState`
|