[M5] mxaccess-asb-nettcp/asb: xmlns raw-string + xsi/xsd on Body

WIRE-FORMAT BREAKTHROUGH — our envelope is now valid NBFX/WCF.
ConnectRequest reaches the server's operation handler. Direct-port-808
Connect now returns a server-side fault (operation invocation
error from the placeholder DH key) rather than TCP RST. With a real
DH key, asb-subscribe gets all the way to "response is missing
required field ServicePublicKey/Data" — meaning our Connect request
processed end-to-end, the server returned a ConnectResponse, and
the only remaining issue is in our response-body decoder.

Fixes:

1. **`XmlnsAttribute` (0x09) value is a RAW length-prefixed string,
   not a text record**. Per `[MC-NBFX]` §2.2.3, xmlns attribute
   values are `[length][bytes]`, NOT `[text-record-byte][value]`.
   Our F21 was emitting `aa <id>` for dict-static values which the
   receiver misparsed as a 0xAA-length string. Same fix applies to
   `ShortXmlnsAttribute` (0x08). Encoder now picks raw-string for
   `Chars` value, raw-int31 (via 0x0B) for `DictionaryStatic` value;
   decoder reads raw string in both code paths.

2. **xmlns:xsi + xmlns:xsd on `<s:Body>`**. WCF declares these
   namespaces on Body before opening the operation request element.
   Our envelope encoder now emits both as raw-string xmlns attrs
   right after `<s:Body>` opens. Required for `xsi:type` annotations
   that appear inside DataContract-serialised body fields.

Combined wire-byte impact (verified via asb-relay side-by-side
diff):

* All header bytes match .NET byte-for-byte through the entire
  `<s:Header>` section (Action / ConnectionValidator / MessageID
  via UniqueIdText / ReplyTo / To).
* `<s:Body>` xmlns:xsi + xmlns:xsd declarations match .NET.
* `<ConnectRequest>` opens identically.
* `<ConnectionId>` / `<ConsumerPublicKey>` / `<Data>` element names
  match.
* The only known remaining diff in the request: .NET emits
  `xmlns="http://asb.contracts.data/20111111"` on the inner
  `<Data>` element (the PublicKey class's XmlType namespace);
  we don't. Likely an issue but apparently non-fatal — the server
  processed our request successfully past this point.

Live status:
* Direct port-808 connect with real DH key: server returns
  "response is missing required field ServicePublicKey/Data" —
  meaning we sent a valid Connect, server replied with a
  ConnectResponse, but our decoder can't find the field. Next
  iteration is response-side decode work.

Workspace: 702 tests pass; clippy + fmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 15:57:31 -04:00
parent c2222b16b0
commit 4c4177050c
2 changed files with 66 additions and 20 deletions
+14 -1
View File
@@ -300,11 +300,24 @@ pub fn encode_envelope(
tokens.push(NbfxToken::EndElement); // </s:Header>
// <s:Body>
// <s:Body xmlns:xsi="...XMLSchema-instance" xmlns:xsd="...XMLSchema">
// WCF emits xmlns:xsi + xmlns:xsd as raw-string xmlns attributes on
// the Body element (verified against .NET probe wire capture).
// These declarations are required for `xsi:type` annotations that
// appear inside DataContract-serialised body fields (e.g.
// ConnectRequest's nested PublicKey).
tokens.push(NbfxToken::Element {
prefix: Some("s".to_string()),
name: NbfxName::Static(ns::BODY),
});
tokens.push(NbfxToken::NamespaceDeclaration {
prefix: "xsi".to_string(),
value: NbfxText::Chars("http://www.w3.org/2001/XMLSchema-instance".to_string()),
});
tokens.push(NbfxToken::NamespaceDeclaration {
prefix: "xsd".to_string(),
value: NbfxText::Chars("http://www.w3.org/2001/XMLSchema".to_string()),
});
tokens.extend_from_slice(&envelope.body_tokens);
tokens.push(NbfxToken::EndElement); // </s:Body>