diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs index 24b54ef..accbeae 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs @@ -150,6 +150,26 @@ public sealed partial class WebSocketHandlerTests Encoding.ASCII.GetString(bufs[2]).ShouldBe("third"); } + [Fact] // T:3084 + public void WSReadPartialFrameHeaderAtEndOfReadBuffer_ShouldSucceed() + { + var client = CreateWsClient(); + var readInfo = CreateReadInfo(); + + var first = CreateMaskedClientFrame(WsOpCode.Binary, 1, final: true, compressed: false, Encoding.ASCII.GetBytes("msg1")); + var second = CreateMaskedClientFrame(WsOpCode.Binary, 1, final: true, compressed: false, Encoding.ASCII.GetBytes("msg2")); + var source = first.Concat(second).ToArray(); + + var initial = source[..(first.Length + 1)]; + using var remainder = new MemoryStream(source[(first.Length + 1)..]); + + var bufs = client.WsRead(readInfo, remainder, initial); + + bufs.Count.ShouldBe(1); + Encoding.ASCII.GetString(bufs[0]).ShouldBe("msg1"); + remainder.Position.ShouldBe(5); + } + [Fact] // T:3085 public void WSReadPingFrame_ShouldSucceed() { @@ -202,6 +222,64 @@ public sealed partial class WebSocketHandlerTests Should.Throw(() => client.WsRead(readInfo, new MemoryStream(Array.Empty()), msg.Concat(close).ToArray())); } + [Fact] // T:3088 + public void WSReadControlFrameBetweebFragmentedFrames_ShouldSucceed() + { + var client = CreateWsClient(); + var readInfo = CreateReadInfo(); + + var frag1 = CreateMaskedClientFrame(WsOpCode.Binary, 1, final: false, compressed: false, Encoding.ASCII.GetBytes("first")); + var ctrl = CreateMaskedClientFrame(WsOpCode.Pong, 1, final: true, compressed: false, Array.Empty()); + var frag2 = CreateMaskedClientFrame(WsOpCode.Binary, 2, final: true, compressed: false, Encoding.ASCII.GetBytes("second")); + + var bufs = client.WsRead(readInfo, new MemoryStream(Array.Empty()), frag1.Concat(ctrl).Concat(frag2).ToArray()); + + bufs.Count.ShouldBe(2); + Encoding.ASCII.GetString(bufs[0]).ShouldBe("first"); + Encoding.ASCII.GetString(bufs[1]).ShouldBe("second"); + } + + [Fact] // T:3089 + public void WSCloseFrameWithPartialOrInvalid_ShouldSucceed() + { + var payloadText = Encoding.ASCII.GetBytes("hello"); + var payload = new byte[2 + payloadText.Length]; + BinaryPrimitives.WriteUInt16BigEndian(payload.AsSpan(0, 2), (ushort)WsConstants.CloseNormalClosure); + payloadText.CopyTo(payload.AsSpan(2)); + + var client = CreateWsClient(); + var readInfo = CreateReadInfo(); + var closeFrame = CreateMaskedClientFrame(WsOpCode.Close, 1, final: true, compressed: false, payload); + var initial = new[] { closeFrame[0] }; + using var remainder = new MemoryStream(closeFrame[1..]); + + Should.Throw(() => client.WsRead(readInfo, remainder, initial)); + lock (GetClientLock(client)) + { + var (chunks, _) = client.CollapsePtoNB(); + chunks.Count.ShouldBe(1); + chunks[0].Buffer.Length.ShouldBe(2 + 2 + payloadText.Length); + chunks[0].Buffer[0].ShouldBe((byte)((byte)WsOpCode.Close | WsConstants.FinalBit)); + BinaryPrimitives.ReadUInt16BigEndian(chunks[0].Buffer.AsSpan(2, 2)).ShouldBe((ushort)WsConstants.CloseNormalClosure); + chunks[0].Buffer.AsSpan(4).ToArray().ShouldBe(payloadText); + } + + client = CreateWsClient(); + readInfo = CreateReadInfo(); + closeFrame = CreateMaskedClientFrame(WsOpCode.Close, 1, final: true, compressed: false, payload[..1]); + var partialHeader = new[] { closeFrame[0] }; + using var invalidRemainder = new MemoryStream(closeFrame[1..]); + + Should.Throw(() => client.WsRead(readInfo, invalidRemainder, partialHeader)); + lock (GetClientLock(client)) + { + var (chunks, _) = client.CollapsePtoNB(); + chunks.Count.ShouldBe(1); + chunks[0].Buffer.Length.ShouldBe(2); + chunks[0].Buffer[0].ShouldBe((byte)((byte)WsOpCode.Close | WsConstants.FinalBit)); + } + } + [Fact] // T:3093 public void WSEnqueueCloseMsg_ShouldSucceed() { diff --git a/porting.db b/porting.db index baa47b0..0d6fe1f 100644 Binary files a/porting.db and b/porting.db differ