feat: wire MQTT end-to-end through NATS SubList for cross-protocol messaging

- MqttListener accepts IMessageRouter + delegates for client ID allocation
  and account resolution (Phase 1-2)
- MqttConnection creates MqttNatsClientAdapter on CONNECT, registers with
  SubList for cross-protocol delivery (Phase 2)
- PUBLISH routes through ProcessMessage() when router available, falls back
  to MQTT-only fan-out for test compatibility (Phase 3)
- SUBSCRIBE creates real SubList entries via adapter, enabling NATS→MQTT
  delivery with topic↔subject translation (Phase 4)
- PUBREL now delivers stored QoS 2 messages before ack (Phase 5)
- ConnzHandler includes MQTT adapters in /connz output (Phase 6)
- MQTTnet E2E tests: MQTT pub/sub, MQTT→NATS, NATS→MQTT, QoS 1 (Phase 7)
This commit is contained in:
Joseph Doherty
2026-03-13 11:38:52 -04:00
parent a1fc600d84
commit a5592ed533
5 changed files with 383 additions and 15 deletions

View File

@@ -122,6 +122,12 @@ public sealed class NatsServer : IMessageRouter, ISubListAccess, IDisposable
/// </summary>
public int? MqttListenerPort => _mqttListener?.Port;
/// <summary>
/// Returns all active MQTT client adapters for monitoring (/connz).
/// </summary>
public IEnumerable<Mqtt.MqttNatsClientAdapter> GetMqttAdapters()
=> _mqttListener?.GetMqttAdapters() ?? [];
public Account SystemAccount => _systemAccount;
public string ServerNKey { get; }
public InternalEventSystem? EventSystem => _eventSystem;
@@ -937,7 +943,10 @@ public sealed class NatsServer : IMessageRouter, ISubListAccess, IDisposable
_authService,
mqttOptions,
mqttStreamInit,
mqttConsumerMgr);
mqttConsumerMgr,
router: this);
_mqttListener.AllocateClientId = () => Interlocked.Increment(ref _nextClientId);
_mqttListener.ResolveAccount = name => GetOrCreateAccount(name ?? Auth.Account.GlobalAccountName);
await _mqttListener.StartAsync(linked.Token);
}
if (_jetStreamService != null)