eggfetch 0.2.1__tar.gz → 0.2.2__tar.gz

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