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:
@@ -141,7 +141,7 @@ counters under one lock acquisition.
|
|||||||
| Severity | Medium |
|
| Severity | Medium |
|
||||||
| Category | Error handling & resilience |
|
| Category | Error handling & resilience |
|
||||||
| Location | `Ipc/FrameReader.cs:31-32` |
|
| Location | `Ipc/FrameReader.cs:31-32` |
|
||||||
| Status | Open |
|
| Status | Resolved |
|
||||||
|
|
||||||
**Description:** After reading the 4-byte length prefix, `ReadFrameAsync` reads the kind
|
**Description:** After reading the 4-byte length prefix, `ReadFrameAsync` reads the kind
|
||||||
byte with the synchronous, blocking `_stream.ReadByte()` and ignores the
|
byte with the synchronous, blocking `_stream.ReadByte()` and ignores the
|
||||||
@@ -158,7 +158,7 @@ prefix read to 5 bytes, or do a second `ReadExactAsync(new byte[1], ct)`. This m
|
|||||||
whole frame read honor the call-timeout token and matches the async style of the rest of
|
whole frame read honor the call-timeout token and matches the async style of the rest of
|
||||||
the reader.
|
the reader.
|
||||||
|
|
||||||
**Resolution:** _(open)_
|
**Resolution:** Resolved 2026-05-22 — replaced the synchronous, non-cancellable `_stream.ReadByte()` for the kind byte with an async `ReadExactAsync(new byte[1], ct)` call so the full frame read honours the call-timeout token and cannot wedge the channel on a stalled peer.
|
||||||
|
|
||||||
### Driver.Historian.Wonderware.Client-006
|
### Driver.Historian.Wonderware.Client-006
|
||||||
|
|
||||||
|
|||||||
@@ -28,14 +28,18 @@ public sealed class FrameReader : IDisposable
|
|||||||
if (length < 0 || length > Framing.MaxFrameBodyBytes)
|
if (length < 0 || length > Framing.MaxFrameBodyBytes)
|
||||||
throw new InvalidDataException($"Sidecar IPC frame length {length} out of range.");
|
throw new InvalidDataException($"Sidecar IPC frame length {length} out of range.");
|
||||||
|
|
||||||
var kindByte = _stream.ReadByte();
|
// Read the kind byte asynchronously and cancellably — a synchronous ReadByte()
|
||||||
if (kindByte < 0) throw new EndOfStreamException("EOF after length prefix, before kind byte.");
|
// 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];
|
var body = new byte[length];
|
||||||
if (!await ReadExactAsync(body, ct).ConfigureAwait(false))
|
if (!await ReadExactAsync(body, ct).ConfigureAwait(false))
|
||||||
throw new EndOfStreamException("EOF mid-frame.");
|
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);
|
public static T Deserialize<T>(byte[] body) => MessagePackSerializer.Deserialize<T>(body);
|
||||||
|
|||||||
Reference in New Issue
Block a user