feat(grpc-events): v8 OpenConnection serializer + native error decode (Path A disproven)
Builds the native 2023 R2 version-8 OpenConnection format, which (unlike v6) carries a ConnectionType byte (Event vs Process) — required because the 2023 R2 server returns event rows only on an Event connection. - HistorianOpen2Protocol.SerializeNativeOpenConnectionVersion8: reproduces the 302-byte v8 layout decoded from a live capture (version 8, markers, client-key GUID, username HString, length-prefixed credential token, ClientType / ConnectionType / flag / constant word / compact metadata / two empty strings; the tail reuses WriteClientCommonInfo). Golden-tested (Version8EventSerializerReproducesCapturedNativeStructure). - HistorianNativeHandshake.BuildEventOpenConnectionVersion8Request: ConnectionType= Event, zeroed credential token (mirroring how v6 zeros its credential block and relies on the separate ValidateClientCredential handshake). - HistorianGrpcHandshake.OpenSession: optional eventConnection switch; the OpenConnection failure path now decodes the native error (type/code/ASCII). Path A (reuse ValidateClientCredential + zeroed token) was live-tested and DISPROVEN: the server parses the v8 buffer but rejects it at the auth check with native 132/34 "EstablishConnection Failed to get client key" — the v8 path looks up the client key in the registry HistoryService.ExchangeKey (ECDH) populates, not the one ValidateClientCredential does. The event orchestrator is therefore reverted to the v6 session (gated test still pins the no-row throw). The v8 serializer/builder are retained for Path B (implement ExchangeKey). 323/323 offline tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -50,7 +50,8 @@ internal static class HistorianGrpcHandshake
|
||||
HistorianGrpcConnection connection,
|
||||
HistorianClientOptions options,
|
||||
CancellationToken cancellationToken,
|
||||
uint connectionMode = HistorianWcfAuthChainHelper.NativeIntegratedReadOnlyConnectionMode)
|
||||
uint connectionMode = HistorianWcfAuthChainHelper.NativeIntegratedReadOnlyConnectionMode,
|
||||
bool eventConnection = false)
|
||||
{
|
||||
DateTime Deadline() => DateTime.UtcNow.Add(options.RequestTimeout);
|
||||
|
||||
@@ -79,8 +80,12 @@ internal static class HistorianGrpcHandshake
|
||||
options,
|
||||
cancellationToken);
|
||||
|
||||
byte[] open2Request = HistorianNativeHandshake.BuildOpenConnection3Request(
|
||||
options.Host, contextKey, connectionMode);
|
||||
// Event reads require an Event-type connection (ConnectionType=Event), which only the native
|
||||
// v8 OpenConnection format carries — the v6 buffer has no such field. Reuse the same
|
||||
// ValidateClientCredential session; the v8 credential token is zeroed (auth already done).
|
||||
byte[] open2Request = eventConnection
|
||||
? HistorianNativeHandshake.BuildEventOpenConnectionVersion8Request(contextKey, options.UserName)
|
||||
: HistorianNativeHandshake.BuildOpenConnection3Request(options.Host, contextKey, connectionMode);
|
||||
|
||||
GrpcHistory.OpenConnectionResponse open2 = historyClient.OpenConnection(
|
||||
new GrpcHistory.OpenConnectionRequest { BtConnectionRequest = ByteString.CopyFrom(open2Request) },
|
||||
@@ -92,7 +97,11 @@ internal static class HistorianGrpcHandshake
|
||||
if (!(open2.Status?.BSuccess ?? false))
|
||||
{
|
||||
byte[] err = open2.Status?.BtError?.ToByteArray() ?? [];
|
||||
throw new InvalidOperationException($"gRPC OpenConnection failed (errorLen={err.Length}, responseLen={open2Response.Length}).");
|
||||
HistorianNativeError? decoded = HistorianOpen2Protocol.TryReadNativeError(err);
|
||||
string ascii = new(err.Where(b => b is >= 0x20 and < 0x7F).Select(b => (char)b).ToArray());
|
||||
throw new InvalidOperationException(
|
||||
$"gRPC OpenConnection failed (errorLen={err.Length}, responseLen={open2Response.Length}, " +
|
||||
$"native={decoded?.Type}/{decoded?.Code}{(decoded?.Name is { } n ? $" {n}" : "")}, ascii='{ascii}').");
|
||||
}
|
||||
|
||||
(uint clientHandle, Guid storageSessionId) = HistorianNativeHandshake.ParseOpenConnectionResponse(open2Response);
|
||||
|
||||
Reference in New Issue
Block a user