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