Harden error reporting, retry bounds, and credential handling
Second hardening pass, over the code added in 23c660d.
Error reporting:
- Decode failures now carry an excerpt of the body that arrived. The
serde message alone ("expected value at line 1 column 1") gives no hint
that the response was an HTML error page from a proxy or a login
redirect from a wrong base URL. The excerpt is collapsed to one line
and truncated so a large body cannot become a large error.
- API errors with an empty or message-less body report the status reason
("Not Found") rather than rendering blank.
- `Error` is `#[non_exhaustive]`, so future variants are not breaking.
Retry bounds:
- `RetryPolicy::max_elapsed` (120s default) bounds the time one call may
spend across attempts. The request timeout is per attempt, so the
previous defaults could spend four minutes on a call that kept timing
out — a surprise for anyone reading `with_timeout(60)` as a bound on
the call.
- `backoff` uses saturating arithmetic; a large `max_delay` could
overflow the jitter span and panic in a debug build.
- Response bodies are drained before a retry so the connection returns to
the pool instead of the next attempt opening a fresh one.
Credentials:
- Test pinning that neither the token nor the password appears in `Debug`
output. `reqwest`'s `Client` prints its default headers, so the
sensitive flag set when building them is the only thing keeping the
token out of logs, and nothing enforced that.
Also drops `Option::is_none_or`, which raised the minimum toolchain to
1.82 for no benefit over a match.
91 tests, up from 80.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,9 @@ let client = InvoiceNinjaClient::hosted(token)?
|
||||
.with_retry_policy(RetryPolicy::new(5));
|
||||
```
|
||||
|
||||
Note that the timeout applies to each attempt, not to the call as a whole;
|
||||
`RetryPolicy::max_elapsed` (120 seconds by default) is what bounds the total.
|
||||
|
||||
Retrying is deliberately conservative about `POST`. A `POST` that fails with
|
||||
a 5xx or times out is **not** replayed, because the server may have applied
|
||||
it before the response was lost and a repeat would create a second invoice
|
||||
|
||||
Reference in New Issue
Block a user