Priority: P3
Describe the issue
Rust wreq installs a default retry policy for protocol-level negative acknowledgements. The default currently permits up to two retries per logical request, but wreq-ruby neither documents that behavior nor lets a Ruby caller disable or bound it explicitly.
The binding should expose the retry controls that Rust wreq actually implements. It should not turn this issue into a general HTTP retry framework: status lists, delayed exponential backoff, jitter, and Retry-After handling are not part of the current native retry layer and are better implemented as Ruby middleware when needed.
Proposed first API
The minimum useful API is an explicit off switch:
client = Wreq::Client.new(retry_policy: :never)
If the native limit and budget are also exposed, prefer a small value object over a large speculative option hash:
policy = Wreq::RetryPolicy.protocol_nacks(
max_retries: 2,
max_extra_load: 0.2
)
client = Wreq::Client.new(retry_policy: policy)
Wreq::RetryPolicy.never
# equivalent to retry_policy: :never
Keep the existing default when retry_policy: is omitted. The option is deliberately not named retry:: retry is a Ruby control-flow keyword, so an explicit Ruby wrapper cannot read a retry: local variable normally. retries: would imply a count rather than a policy. retry_policy: is both wrapper-friendly and accurate for values such as :never or Wreq::RetryPolicy.
Required semantics
retry_policy: :never binds Rust wreq::retry::Policy::never().
- Omitted configuration retains the current Rust wreq default and documents what that default classifies.
- Other symbols and arbitrary truthy/falsey values are rejected rather than silently selecting a policy.
max_retries maps to the native per-request maximum and is a non-negative Integer.
max_extra_load, if exposed, maps to the native shared retry budget, validates its range in Ruby, and cannot reach a panicking Rust assertion.
- Client construction rejects unknown retry keys and invalid values before network I/O.
- The documentation explains that retries may make more than one wire attempt for one Ruby request.
- Request-body replay follows Rust wreq's native rules; this issue must not claim replay support for Ruby IO/Enumerable bodies unless it is actually guaranteed.
Host-scoped native policies can be considered later. Arbitrary Ruby classifier callbacks should not be invoked from native worker threads without a separate design for the GVL, lifetime, cancellation, and callback exceptions.
Deliberately out of scope
The following are application/middleware retry features, not requirements for this binding issue:
- retrying configured HTTP statuses such as 429/503;
- exponential backoff, jitter, or sleep scheduling;
- parsing and honoring
Retry-After;
- deciding whether POST/PATCH is application-idempotent;
- retry telemetry or retaining attempt histories; and
- a Ruby callback that classifies every response.
If a future Rust wreq release adds those primitives, they can be evaluated independently rather than promised now.
Acceptance criteria
retry_policy: :never disables Rust wreq's native protocol retries.
- The current default behavior and native maximum are documented with the Rust wreq version they describe.
- Any exposed policy object is immutable, validates values in Ruby, and maps only to real Rust policy controls.
- Invalid values cannot panic Rust or silently fall back to defaults.
- Tests distinguish one logical request from observed wire attempts and cover default, disabled, zero, and bounded native retry behavior.
- Documentation states explicitly that this API is not a general HTTP-status/backoff retry framework.
Ecosystem and binding context
- The underlying
wreq::retry::Policy exposes never, a retry budget, per-request maximums, scopes, and native classifiers. Its default classifier handles protocol NACKs.
wreq-python does not currently expose a public retry policy, which reinforces keeping the first Ruby binding small.
- HTTPX and Faraday provide broader retries through plugins or middleware; that is a different layer from exposing Rust wreq's built-in protocol behavior.
Relevant source
Priority: P3
Describe the issue
Rust wreq installs a default retry policy for protocol-level negative acknowledgements. The default currently permits up to two retries per logical request, but wreq-ruby neither documents that behavior nor lets a Ruby caller disable or bound it explicitly.
The binding should expose the retry controls that Rust wreq actually implements. It should not turn this issue into a general HTTP retry framework: status lists, delayed exponential backoff, jitter, and
Retry-Afterhandling are not part of the current native retry layer and are better implemented as Ruby middleware when needed.Proposed first API
The minimum useful API is an explicit off switch:
If the native limit and budget are also exposed, prefer a small value object over a large speculative option hash:
Keep the existing default when
retry_policy:is omitted. The option is deliberately not namedretry::retryis a Ruby control-flow keyword, so an explicit Ruby wrapper cannot read aretry:local variable normally.retries:would imply a count rather than a policy.retry_policy:is both wrapper-friendly and accurate for values such as:neverorWreq::RetryPolicy.Required semantics
retry_policy: :neverbinds Rustwreq::retry::Policy::never().max_retriesmaps to the native per-request maximum and is a non-negative Integer.max_extra_load, if exposed, maps to the native shared retry budget, validates its range in Ruby, and cannot reach a panicking Rust assertion.Host-scoped native policies can be considered later. Arbitrary Ruby classifier callbacks should not be invoked from native worker threads without a separate design for the GVL, lifetime, cancellation, and callback exceptions.
Deliberately out of scope
The following are application/middleware retry features, not requirements for this binding issue:
Retry-After;If a future Rust wreq release adds those primitives, they can be evaluated independently rather than promised now.
Acceptance criteria
retry_policy: :neverdisables Rust wreq's native protocol retries.Ecosystem and binding context
wreq::retry::Policyexposesnever, a retry budget, per-request maximums, scopes, and native classifiers. Its default classifier handles protocol NACKs.wreq-pythondoes not currently expose a public retry policy, which reinforces keeping the first Ruby binding small.Relevant source
src/client.rs