feat(mqtt): implement CONNECT/CONNACK/DISCONNECT packet handlers
Implement Task 3 of MQTT orchestration: - Create MqttPacketHandlers.cs with ParseConnect(), ProcessConnect(), EnqueueConnAck(), HandleDisconnect() - Wire CONNECT and DISCONNECT dispatch in MqttParser.cs - Parse CONNECT: protocol name/level, flags, keep-alive, client ID, will, auth - Send CONNACK (4-byte fixed packet with return code) - DISCONNECT clears will message and closes connection cleanly - Auto-generate client ID for empty ID + clean session - Validate reserved bit, will flags, username/password consistency - Add Reader field to MqttHandler for per-connection parsing - 11 unit tests for CONNECT parsing and processing - 1 end-to-end integration test: TCP → CONNECT → CONNACK over the wire
This commit is contained in:
@@ -56,16 +56,39 @@ public sealed class MqttParserTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_ConnectFirst_ShouldNotRejectAsNonConnect()
|
||||
public void Parse_ConnectFirst_ShouldAcceptConnect()
|
||||
{
|
||||
var c = CreateMqttClient();
|
||||
// CONNECT packet (minimal): type=0x10, remaining len=0
|
||||
// This will hit the "not yet implemented" but NOT the "first packet" error.
|
||||
var buf = new byte[] { MqttPacket.Connect, 0x00 };
|
||||
var err = MqttParser.Parse(c, buf, buf.Length);
|
||||
err.ShouldNotBeNull(); // Will be NotImplementedException
|
||||
err.ShouldBeOfType<NotImplementedException>();
|
||||
err.Message.ShouldContain("CONNECT not yet implemented");
|
||||
// Use a MemoryStream so CONNACK can be written.
|
||||
typeof(ClientConnection)
|
||||
.GetField("_nc", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
|
||||
.SetValue(c, new MemoryStream());
|
||||
|
||||
// Build a valid CONNECT packet.
|
||||
var payload = new List<byte>();
|
||||
payload.AddRange(new byte[] { 0x00, 0x04 }); // protocol name length
|
||||
payload.AddRange(System.Text.Encoding.UTF8.GetBytes("MQTT"));
|
||||
payload.Add(0x04); // level
|
||||
payload.Add(0x02); // flags: clean session
|
||||
payload.AddRange(new byte[] { 0x00, 0x3C }); // keep alive = 60
|
||||
payload.AddRange(new byte[] { 0x00, 0x04 }); // client id length
|
||||
payload.AddRange(System.Text.Encoding.UTF8.GetBytes("test"));
|
||||
|
||||
var buf = new List<byte> { MqttPacket.Connect };
|
||||
// Remaining length
|
||||
var remLen = payload.Count;
|
||||
do
|
||||
{
|
||||
var b = (byte)(remLen & 0x7F);
|
||||
remLen >>= 7;
|
||||
if (remLen > 0) b |= 0x80;
|
||||
buf.Add(b);
|
||||
} while (remLen > 0);
|
||||
buf.AddRange(payload);
|
||||
|
||||
var err = MqttParser.Parse(c, buf.ToArray(), buf.Count);
|
||||
err.ShouldBeNull("CONNECT should be accepted, not rejected as non-CONNECT");
|
||||
(c.Flags & ClientFlags.ConnectReceived).ShouldNotBe((ClientFlags)0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user