You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

216 line
9.4 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.WebSockets;
  5. using System.Security.Authentication;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using IronPython.Runtime;
  9. using Microsoft.AspNetCore.Http;
  10. using Microsoft.Extensions.Logging;
  11. using MQTTnet.Adapter;
  12. using MQTTnet.AspNetCore;
  13. using MQTTnet.Implementations;
  14. using MQTTnet.Protocol;
  15. using MQTTnet.Server.Configuration;
  16. using MQTTnet.Server.Scripting;
  17. namespace MQTTnet.Server.Mqtt
  18. {
  19. public class MqttServerService
  20. {
  21. private readonly ILogger<MqttServerService> _logger;
  22. private readonly SettingsModel _settings;
  23. private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
  24. private readonly MqttServerStorage _mqttServerStorage;
  25. private readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
  26. private readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
  27. private readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
  28. private readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
  29. private readonly MqttConnectionValidator _mqttConnectionValidator;
  30. private readonly IMqttServer _mqttServer;
  31. private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
  32. private readonly PythonScriptHostService _pythonScriptHostService;
  33. private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
  34. public MqttServerService(
  35. SettingsModel settings,
  36. CustomMqttFactory mqttFactory,
  37. MqttClientConnectedHandler mqttClientConnectedHandler,
  38. MqttClientDisconnectedHandler mqttClientDisconnectedHandler,
  39. MqttClientSubscribedTopicHandler mqttClientSubscribedTopicHandler,
  40. MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
  41. MqttConnectionValidator mqttConnectionValidator,
  42. MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
  43. MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
  44. MqttServerStorage mqttServerStorage,
  45. PythonScriptHostService pythonScriptHostService,
  46. ILogger<MqttServerService> logger)
  47. {
  48. _settings = settings ?? throw new ArgumentNullException(nameof(settings));
  49. _mqttClientConnectedHandler = mqttClientConnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientConnectedHandler));
  50. _mqttClientDisconnectedHandler = mqttClientDisconnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientDisconnectedHandler));
  51. _mqttClientSubscribedTopicHandler = mqttClientSubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientSubscribedTopicHandler));
  52. _mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
  53. _mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
  54. _mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
  55. _mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
  56. _mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
  57. _pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
  58. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  59. _webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger());
  60. var adapters = new List<IMqttServerAdapter>
  61. {
  62. new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger()),
  63. _webSocketServerAdapter
  64. };
  65. _mqttServer = mqttFactory.CreateMqttServer(adapters);
  66. }
  67. public void Configure()
  68. {
  69. _pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
  70. _mqttServerStorage.Configure();
  71. _mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler;
  72. _mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler;
  73. _mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler;
  74. _mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler;
  75. _mqttServer.StartAsync(CreateMqttServerOptions()).GetAwaiter().GetResult();
  76. _logger.LogInformation("MQTT server started.");
  77. }
  78. public Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
  79. {
  80. return _webSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, httpContext);
  81. }
  82. private void Publish(PythonDictionary parameters)
  83. {
  84. try
  85. {
  86. var applicationMessageBuilder = new MqttApplicationMessageBuilder()
  87. .WithTopic((string)parameters.get("topic", null))
  88. .WithRetainFlag((bool)parameters.get("retain", false))
  89. .WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
  90. var payload = parameters.get("payload", null);
  91. byte[] binaryPayload;
  92. if (payload == null)
  93. {
  94. binaryPayload = new byte[0];
  95. }
  96. else if (payload is string stringPayload)
  97. {
  98. binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
  99. }
  100. else if (payload is ByteArray byteArray)
  101. {
  102. binaryPayload = byteArray.ToArray();
  103. }
  104. else if (payload is IEnumerable<int> intArray)
  105. {
  106. binaryPayload = intArray.Select(Convert.ToByte).ToArray();
  107. }
  108. else
  109. {
  110. throw new NotSupportedException("Payload type not supported.");
  111. }
  112. applicationMessageBuilder = applicationMessageBuilder
  113. .WithPayload(binaryPayload);
  114. var applicationMessage = applicationMessageBuilder.Build();
  115. _mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
  116. }
  117. catch (Exception exception)
  118. {
  119. _logger.LogError(exception, "Error while publishing application message from server.");
  120. }
  121. }
  122. private IMqttServerOptions CreateMqttServerOptions()
  123. {
  124. var options = new MqttServerOptionsBuilder()
  125. .WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
  126. .WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
  127. .WithConnectionValidator(_mqttConnectionValidator)
  128. .WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
  129. .WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
  130. .WithStorage(_mqttServerStorage);
  131. // Configure unencrypted connections
  132. if (_settings.TcpEndPoint.Enabled)
  133. {
  134. options.WithDefaultEndpoint();
  135. if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
  136. {
  137. options.WithDefaultEndpointBoundIPAddress(address4);
  138. }
  139. if (_settings.TcpEndPoint.TryReadIPv6(out var address6))
  140. {
  141. options.WithDefaultEndpointBoundIPV6Address(address6);
  142. }
  143. if (_settings.TcpEndPoint.Port > 0)
  144. {
  145. options.WithDefaultEndpointPort(_settings.TcpEndPoint.Port);
  146. }
  147. }
  148. else
  149. {
  150. options.WithoutDefaultEndpoint();
  151. }
  152. // Configure encrypted connections
  153. if (_settings.EncryptedTcpEndPoint.Enabled)
  154. {
  155. options
  156. .WithEncryptedEndpoint()
  157. .WithEncryptionSslProtocol(SslProtocols.Tls12)
  158. .WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.ReadCertificate());
  159. if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
  160. {
  161. options.WithEncryptedEndpointBoundIPAddress(address4);
  162. }
  163. if (_settings.EncryptedTcpEndPoint.TryReadIPv6(out var address6))
  164. {
  165. options.WithEncryptedEndpointBoundIPV6Address(address6);
  166. }
  167. if (_settings.EncryptedTcpEndPoint.Port > 0)
  168. {
  169. options.WithEncryptedEndpointPort(_settings.EncryptedTcpEndPoint.Port);
  170. }
  171. }
  172. else
  173. {
  174. options.WithoutEncryptedEndpoint();
  175. }
  176. if (_settings.ConnectionBacklog > 0)
  177. {
  178. options.WithConnectionBacklog(_settings.ConnectionBacklog);
  179. }
  180. if (_settings.EnablePersistentSessions)
  181. {
  182. options.WithPersistentSessions();
  183. }
  184. return options.Build();
  185. }
  186. }
  187. }