Files
mxaccess/rust/crates
Joseph Doherty 1e59249662 [M5] mxaccess-asb: F25 step 4 — AsbClient async network loop
The first slice of F25 that actually moves bytes across a transport.
Wraps every M5 framing layer (F19-F25.3) into a single async client
generic over `AsyncRead + AsyncWrite + Unpin + Send`. Tested in-memory
via `tokio::io::duplex` — no live ASB endpoint required.

API:
* `AsbClient::new(stream, authenticator, via_uri)` — wraps a Tokio
  transport + F23 authenticator into a ready client.
* `send_preamble()` — writes the canonical preamble (Version 1.0 →
  Duplex → Via → BinaryWithDictionary → PreambleEnd) and reads the
  peer's PreambleAck. Surfaces Fault as `ClientError::Fault(msg)`.
* `send_envelope(env)` — frames `SoapEnvelope` in a SizedEnvelope NMF
  record, writes, reads the response SizedEnvelope, decodes back to
  `DecodedEnvelope`.
* `send_signed_envelope(action, body, force_hmac)` — calls F23
  authenticator's `sign` on the unsigned body bytes, attaches a
  ConnectionValidator header (base64'd MAC + IV), sends.
* `register_items` / `unregister_items` — thin per-operation wrappers
  threading body builder + response decoder.
* `send_end()` — writes record 0x07 + shutdowns the stream.

Async record reader: streaming decode of the multibyte-int31 length
prefix for SizedEnvelope (0x06) / Fault (0x08), plus a fallback path
for Version / Mode / KnownEncoding / etc.

`ClientError` covers I/O, NMF, NBFX, Envelope, Operation, Auth, plus
PreambleNotSent / AlreadyClosed / Fault / PeerClosed /
UnexpectedRecord guards.

6 new tests via in-memory `tokio::io::duplex`:
* Preamble round-trip with synthetic peer returning PreambleAck.
* Fault propagation through preamble exchange.
* End-to-end RegisterItems request → response with a peer that
  drains preamble, replies PreambleAck, drains the SizedEnvelope,
  responds with a synthetic RegisterItemsResponse body containing a
  binary-encoded ItemStatus array. Client decodes and asserts the
  recovered ItemIdentity name.
* `send_envelope` before preamble fails with PreambleNotSent.
* `send_end` writes record 0x07 to the wire.
* PreambleMode re-export keeps shape parity with `nmf::NmfMode`.

Known limitation: the signing path currently hashes the NBFX-encoded
body; .NET hashes the XML-text `request.ToXml()`. Functionally
present (validator built and attached) but MAC bytes won't match
.NET's MAC for the same payload until the live-probe iteration
reconciles which canonical form to sign.

Stubbed for next F25 iteration:
* `AsbClient::connect` — DH `Connect` + `AuthenticateMe` handshake
  flow. Needs ConnectRequest/Response builders (regular WCF XML, not
  the IAsbCustomSerializableType fast-path) and the
  `AsbAuthenticator::create_authentication_data` integration.
* Read / Write / Subscription operation wrappers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:37:48 -04:00
..