feat: implement full MQTT Go parity across 5 phases — binary protocol, auth/TLS, cross-protocol bridging, monitoring, and JetStream persistence

Phase 1: Binary MQTT 3.1.1 wire protocol with PipeReader-based parsing,
full packet type dispatch, and MQTT 3.1.1 compliance checks.

Phase 2: Auth pipeline routing MQTT CONNECT through AuthService,
TLS transport with SslStream wrapping, pinned cert validation.

Phase 3: IMessageRouter refactor (NatsClient → INatsClient),
MqttNatsClientAdapter for cross-protocol bridging, MqttTopicMapper
with full Go-parity topic/subject translation.

Phase 4: /connz mqtt_client field population, /varz actual MQTT port.

Phase 5: JetStream persistence — MqttStreamInitializer creates 5
internal streams, MqttConsumerManager for QoS 1/2 consumers,
subject-keyed session/retained lookups replacing linear scans.

All 503 MQTT tests and 1589 Core tests pass.
This commit is contained in:
Joseph Doherty
2026-03-13 10:09:40 -04:00
parent 0be321fa53
commit 845441b32c
34 changed files with 3194 additions and 126 deletions

View File

@@ -33,6 +33,119 @@ public static class MqttPacketWriter
return buffer;
}
/// <summary>
/// Writes a CONNACK packet. Go reference: mqtt.go mqttConnAck.
/// </summary>
/// <param name="sessionPresent">0x01 if resuming existing session, 0x00 otherwise.</param>
/// <param name="returnCode">CONNACK return code (0x00 = accepted).</param>
public static byte[] WriteConnAck(byte sessionPresent, byte returnCode)
{
ReadOnlySpan<byte> payload = [sessionPresent, returnCode];
return Write(MqttControlPacketType.ConnAck, payload);
}
/// <summary>
/// Writes a PUBACK packet (QoS 1 acknowledgment).
/// </summary>
public static byte[] WritePubAck(ushort packetId)
{
Span<byte> payload = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(payload, packetId);
return Write(MqttControlPacketType.PubAck, payload);
}
/// <summary>
/// Writes a SUBACK packet with granted QoS values per subscription filter.
/// </summary>
public static byte[] WriteSubAck(ushort packetId, ReadOnlySpan<byte> grantedQoS)
{
var payload = new byte[2 + grantedQoS.Length];
BinaryPrimitives.WriteUInt16BigEndian(payload.AsSpan(0, 2), packetId);
grantedQoS.CopyTo(payload.AsSpan(2));
return Write(MqttControlPacketType.SubAck, payload);
}
/// <summary>
/// Writes an UNSUBACK packet.
/// </summary>
public static byte[] WriteUnsubAck(ushort packetId)
{
Span<byte> payload = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(payload, packetId);
return Write(MqttControlPacketType.UnsubAck, payload);
}
/// <summary>
/// Writes a PINGRESP packet (no payload).
/// </summary>
public static byte[] WritePingResp()
=> Write(MqttControlPacketType.PingResp, []);
/// <summary>
/// Writes a PUBREC packet (QoS 2 step 1 response).
/// </summary>
public static byte[] WritePubRec(ushort packetId)
{
Span<byte> payload = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(payload, packetId);
return Write(MqttControlPacketType.PubRec, payload);
}
/// <summary>
/// Writes a PUBREL packet (QoS 2 step 2). Fixed-header flags must be 0x02 per MQTT spec.
/// </summary>
public static byte[] WritePubRel(ushort packetId)
{
Span<byte> payload = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(payload, packetId);
return Write(MqttControlPacketType.PubRel, payload, flags: 0x02);
}
/// <summary>
/// Writes a PUBCOMP packet (QoS 2 step 3 response).
/// </summary>
public static byte[] WritePubComp(ushort packetId)
{
Span<byte> payload = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(payload, packetId);
return Write(MqttControlPacketType.PubComp, payload);
}
/// <summary>
/// Writes an MQTT PUBLISH packet for delivery to a client.
/// </summary>
public static byte[] WritePublish(string topic, ReadOnlySpan<byte> payload, byte qos = 0,
bool retain = false, bool dup = false, ushort packetId = 0)
{
var topicBytes = Encoding.UTF8.GetBytes(topic);
var variableHeaderLen = 2 + topicBytes.Length + (qos > 0 ? 2 : 0);
var totalPayload = new byte[variableHeaderLen + payload.Length];
var pos = 0;
// Topic name (length-prefixed)
BinaryPrimitives.WriteUInt16BigEndian(totalPayload.AsSpan(pos, 2), (ushort)topicBytes.Length);
pos += 2;
topicBytes.CopyTo(totalPayload.AsSpan(pos));
pos += topicBytes.Length;
// Packet ID (only for QoS > 0)
if (qos > 0)
{
BinaryPrimitives.WriteUInt16BigEndian(totalPayload.AsSpan(pos, 2), packetId);
pos += 2;
}
// Application payload
payload.CopyTo(totalPayload.AsSpan(pos));
byte flags = 0;
if (dup) flags |= 0x08;
flags |= (byte)((qos & 0x03) << 1);
if (retain) flags |= 0x01;
return Write(MqttControlPacketType.Publish, totalPayload, flags);
}
internal static byte[] EncodeRemainingLength(int value)
{
if (value < 0 || value > MqttProtocolConstants.MaxPayloadSize)