IoTSharp / MQTTnet.AspNetCore.Routing

Easily create Controllers and Actions to process incoming MQTT messages using a familiar paradigm and MQTTnet

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dependency Injection Restrictions Preventing Use of MQTT Server Options

Phenek opened this issue · comments

commented

Describe the bug

I am unable to add the server options as mentioned below because my options require dependency injection. Therefore, I can't use the given example. Moreover, I've had to create my own background service to instantiate the MQTT server.
That gave me some null reference exceptions.

To Reproduce

Steps to reproduce the behavior:

  1. Try adding server options with dependencies injected.
  2. Notice that the below example can't be used:
startup
    services
        .AddHostedMqttServerWithServices(s =>
        {
            //Can't get ICertificateVaultManagerFactory here from dependency injection, because they are not injected yet. 
            s.WithoutDefaultEndpoint();
        })
        .AddMqttConnectionHandler()
        .AddConnections();

Expected behavior

I should be able to add server options even when there are dependencies that need to be injected.

Code example

To workaround this, I've created my own background service:

    public class MqttBroker : BackgroundService
    {
        private readonly IServiceProvider _services;
        private readonly ILogger<MqttBroker> _logger;
        private ICertificateRepository _certificateRepository;
        private CertVaultManager _mqttCertVault;

        public MqttBroker(IServiceProvider services, ILogger<MqttBroker> logger)
        {
            _services = services;
            _logger = logger;
        }

        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            using var scope = _services.CreateScope();
            _certificateRepository = scope.ServiceProvider.GetRequiredService<ICertificateRepository>();
            _mqttCertVault = scope.ServiceProvider.GetRequiredService<ICertVaultManagerFactory>().MqttCertVault();

            _logger.LogInformation("Starting service");
            await StartMqttServer();
            _logger.LogInformation("Service started");
            await base.StartAsync(cancellationToken);
        }
        
        private async Task StartMqttServer()
        {
          // Certificate setup
          var serverCertificate = ...; // Retrieve server certificate from dependency injection (vault & repository)
          var trustedCaCertificates = ...; // Retrieve trusted CA certificates from dependency injection (vault & repository)

          var optionsBuilder = new MqttServerOptionsBuilder()
              .WithEncryptedEndpoint()
              .WithEncryptedEndpointPort(/*specific port*/)
              .WithEncryptionCertificate(serverCertificate)
              .WithEncryptionSslProtocol(SslProtocols.Tls12)
              .WithClientCertificate(ValidateClientCertificate);

          var mqttServer = new MqttFactory().CreateMqttServer(optionsBuilder.Build());
          mqttServer.WithAttributeRouting(_services, true);
          // Additional setup methods...

          await mqttServer.StartAsync();
}

However, I've had to rely on WithAttributeRouting, which is marked as obsolete now, but it's indispensable in my scenario.

Additionally, when using a custom background service created after the service initialization, the following is always null because it as never been resolved in startup services:

var server = app.ApplicationServices.GetRequiredService<MqttServer>()

I'd like to shed light on two main points:

  1. I've started making modifications so that the library works only in my case. HERE the PR
    I removed unecessary
var server = app.ApplicationServices.GetRequiredService<MqttServer>();
  1. By doing this it's open a new interogation about singleton MqttRouter
    It would be beneficial to remove this, too make mqttRouter working great with multi mqttServer
services.AddSingleton<MqttRouter>();

because I added server property in MqttRouter class

public MqttServer Server { get; set; }

It means one MqttRouter per MqttServer.

The reason being, I am planning to have multiple instances of mqttServer on a single machine.
For instance, managing two MQTT ports: 8883 and 8884, both with TLS.

Because I might want to run

  • port: 8884 with an new CA certificate,
  • port: 8883 with a the old Ca certificate

making two distinct server certificates coexist on a single machine.

What do you think about this?

Yeah i need someone to think about this multi/custom hosted mqttServer :P
I hope you will share your thoughts. See you soon.

It's a great job, thank you.