fix(driver-historian-wonderware-client): resolve Medium code-review finding (Driver.Historian.Wonderware.Client-005)

Replace the synchronous non-cancellable _stream.ReadByte() for the kind byte
in FrameReader.ReadFrameAsync with an async ReadExactAsync(new byte[1], ct)
call so the full frame read honours the EffectiveCallTimeout-linked token
and cannot wedge the call gate when the sidecar stalls mid-frame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 09:19:14 -04:00
parent 6bb971c040
commit 75580fb432
2 changed files with 9 additions and 5 deletions

View File

@@ -28,14 +28,18 @@ public sealed class FrameReader : IDisposable
if (length < 0 || length > Framing.MaxFrameBodyBytes)
throw new InvalidDataException($"Sidecar IPC frame length {length} out of range.");
var kindByte = _stream.ReadByte();
if (kindByte < 0) throw new EndOfStreamException("EOF after length prefix, before kind byte.");
// Read the kind byte asynchronously and cancellably — a synchronous ReadByte()
// blocks the thread-pool thread and cannot be interrupted by the call-timeout token
// if the peer stalls mid-frame (finding 005).
var kindBuffer = new byte[Framing.KindByteSize];
if (!await ReadExactAsync(kindBuffer, ct).ConfigureAwait(false))
throw new EndOfStreamException("EOF after length prefix, before kind byte.");
var body = new byte[length];
if (!await ReadExactAsync(body, ct).ConfigureAwait(false))
throw new EndOfStreamException("EOF mid-frame.");
return ((MessageKind)(byte)kindByte, body);
return ((MessageKind)kindBuffer[0], body);
}
public static T Deserialize<T>(byte[] body) => MessagePackSerializer.Deserialize<T>(body);