- Create the sleeper's `Notified` under the map lock, so a wake that lands
just before the sleep starts is not lost. Count sleeps per destination
and forget the destination in a drop guard, which also runs on cancel.
- Map an SRV `.` target to `ConnectError`, as Python's resolver does.
- Call a custom parser's `finish` when its last write fails, and when the
request is cancelled mid-read.
- Check the path is ASCII after the backoff check, as Python does.
- `put_json` takes `rewrite_age_ts`, which `send_transaction` sets. Any
other `json_data_callback` sends through the Python client.
- Count attempts in the no-retry test, and test the request counters,
the proxy-worker fallback, shutdown and the server-up wake.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5b7Ruy4cgt27Mjq6Ptxbb
Add the `experimental_features.rust_federation_sending` level, `off` or
`client` for now. At `client`, `MatrixFederationHttpClient` sends its
JSON requests (`put_json`, `post_json`, `get_json`,
`get_json_with_headers`, `delete_json`, `send_direct_request`) through
the Rust client. Media downloads, and an instance that sends through a
proxy worker, stay on the Python path.
The facade keeps the tracing span around the call and injects its
headers. The request and response counters are bumped from the GIL hop
that returns the result. The limiter's background write is factored out
of `RetryDestinationLimiter` so both paths share it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5b7Ruy4cgt27Mjq6Ptxbb