public async Task LoadMessagesAsync() { if (_options.Storage == null) { return; } await _semaphore.WaitAsync().ConfigureAwait(false); try { var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync(); _retainedMessages.Clear(); foreach (var retainedMessage in retainedMessages) { _retainedMessages[retainedMessage.Topic] = retainedMessage; } } catch (Exception exception) { _logger.Error <MqttRetainedMessagesManager>(exception, "Unhandled exception while loading retained messages."); } finally { _semaphore.Release(); } }
public async Task RunAsync(MqttApplicationMessage willMessage, IMqttChannelAdapter adapter) { if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } try { var cancellationTokenSource = new CancellationTokenSource(); _willMessage = willMessage; _adapter = adapter; _cancellationTokenSource = cancellationTokenSource; _pendingMessagesQueue.Start(adapter, cancellationTokenSource.Token); await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.Warning <MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId); } catch (Exception exception) { _logger.Error <MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); } }
public async Task LoadMessagesAsync() { if (_options.Storage == null) { return; } try { var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync().ConfigureAwait(false); if (retainedMessages?.Any() == true) { using (await _messagesLock.WaitAsync().ConfigureAwait(false)) { _messages.Clear(); foreach (var retainedMessage in retainedMessages) { _messages[retainedMessage.Topic] = retainedMessage; } } } } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while loading retained messages."); } }
async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false); if (clientSocket == null) { continue; } Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger); } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is SocketException socketException) { if (socketException.SocketErrorCode == SocketError.ConnectionAborted || socketException.SocketErrorCode == SocketError.OperationAborted) { continue; } } _logger.Error(exception, $"Error while accepting connection at TCP listener {_localEndPoint} TLS={_tlsCertificate != null}."); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { //todo: else branch can be used with min dependency NET46 #if NET452 || NET461 var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false); #else var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false); #endif var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer(), _logger); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); } catch (ObjectDisposedException) { // It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed. } catch (Exception exception) { if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted) { return; } _logger.Error <MqttServerAdapter>(exception, "Error while accepting connection at default endpoint."); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false); if (connectionState == ReconnectionResult.NotConnected) { _publishingCancellationToken?.Cancel(false); _publishingCancellationToken = null; await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false); continue; } if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed) { await PushSubscriptionsAsync(); _publishingCancellationToken = new CancellationTokenSource(); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(async() => await PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.Token).ConfigureAwait(false); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed continue; } if (connectionState == ReconnectionResult.StillConnected) { await Task.Delay(TimeSpan.FromSeconds(1), _connectionCancellationToken.Token).ConfigureAwait(false); } } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.Warning <ManagedMqttClient>(exception, "Communication exception while maintaining connection."); } catch (Exception exception) { _logger.Error <ManagedMqttClient>(exception, "Unhandled exception while maintaining connection."); } finally { await _mqttClient.DisconnectAsync().ConfigureAwait(false); _logger.Info <ManagedMqttClient>("Stopped"); } }
public async Task <MqttClientConnectResult> ConnectAsync(IMqttClientOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (options.ChannelOptions == null) { throw new ArgumentException("ChannelOptions are not set."); } ThrowIfConnected("It is not allowed to connect with a server after the connection is established."); try { _options = options; _cancellationTokenSource = new CancellationTokenSource(); _packetIdentifierProvider.Reset(); _packetDispatcher.Reset(); _adapter = _adapterFactory.CreateClientAdapter(options, _logger); _logger.Verbose <MqttClient>("Trying to connect with server."); await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); _logger.Verbose <MqttClient>("Connection with server established."); await StartReceivingPacketsAsync().ConfigureAwait(false); var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false); _logger.Verbose <MqttClient>("MQTT connection with server established."); _sendTracker.Restart(); if (_options.KeepAlivePeriod != TimeSpan.Zero) { StartSendingKeepAliveMessages(); } IsConnected = true; Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent)); _logger.Info <MqttClient>("Connected."); return(new MqttClientConnectResult(connectResponse.IsSessionPresent)); } catch (Exception exception) { _logger.Error <MqttClient>(exception, "Error while connecting with server."); await DisconnectInternalAsync(null, exception).ConfigureAwait(false); throw; } }
async void OnConnectionReceivedAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var clientHandler = ClientHandler; if (clientHandler != null) { X509Certificate2 clientCertificate = null; if (args.Socket.Control.ClientCertificate != null) { try { clientCertificate = new X509Certificate2(args.Socket.Control.ClientCertificate.GetCertificateBlob().ToArray()); } catch (Exception exception) { _logger.Warning(exception, "Unable to convert UWP certificate to X509Certificate2."); } } using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(), _logger)) { await clientHandler(clientAdapter).ConfigureAwait(false); } } } catch (Exception exception) { if (exception is ObjectDisposedException) { // It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed. return; } _logger.Error(exception, "Error while handling client connection."); } finally { try { args.Socket.Dispose(); } catch (Exception exception) { _logger.Error(exception, "Error while cleaning up client connection"); } } }
private async Task SendQueuedPacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await SendQueuedPacketAsync(adapter, cancellationToken); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error <MqttClientPendingMessagesQueue>(exception, "Unhandled exception while sending enqueued packet (ClientId: {0}).", _clientSession.ClientId); } }
public async Task SafeNotifyClientConnectedAsync(string clientId) { try { var handler = ClientConnectedHandler; if (handler == null) { return; } await handler.HandleClientConnectedAsync(new MqttServerClientConnectedEventArgs(clientId)).ConfigureAwait(false); } catch (Exception exception) { _logger.Error(exception, "Error while handling custom 'ClientConnected' event."); } }
async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { await TryProcessNextQueuedApplicationMessageAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing queued application messages."); } } }
public static void Forget(this Task task, IMqttNetLogger logger) { task?.ContinueWith(t => { logger.Error(t.Exception, "Unhandled exception."); }, TaskContinuationOptions.OnlyOnFaulted); }
public async Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) { if (connectPacket == null) { throw new ArgumentNullException(nameof(connectPacket)); } if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } try { var cancellationTokenSource = new CancellationTokenSource(); _willMessage = connectPacket.WillMessage; _adapter = adapter; _cancellationTokenSource = cancellationTokenSource; _pendingMessagesQueue.Start(adapter, cancellationTokenSource.Token); _lastPacketReceivedTracker.Restart(); _lastNonKeepAlivePacketReceivedTracker.Restart(); if (connectPacket.KeepAlivePeriod > 0) { StartCheckingKeepAliveTimeout(TimeSpan.FromSeconds(connectPacket.KeepAlivePeriod), cancellationTokenSource.Token); } await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.Warning <MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId); } catch (Exception exception) { _logger.Error <MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); } }
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer(), _logger); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); } catch (Exception exception) { _logger.Error <MqttServerAdapter>(exception, "Error while accepting connection at default endpoint."); } }
public async Task <bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) { if (connectPacket == null) { throw new ArgumentNullException(nameof(connectPacket)); } if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } try { var cancellationTokenSource = new CancellationTokenSource(); _wasCleanDisconnect = false; _willMessage = connectPacket.WillMessage; _adapter = adapter; _cancellationTokenSource = cancellationTokenSource; PendingMessagesQueue.Start(adapter, cancellationTokenSource.Token); KeepAliveMonitor.Start(connectPacket.KeepAlivePeriod, cancellationTokenSource.Token); await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.Warning <MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId); } catch (Exception exception) { _logger.Error <MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); } return(_wasCleanDisconnect); }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await TryMaintainConnectionAsync(cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error <ManagedMqttClient>(exception, "Unhandled exception while maintaining connection."); } finally { await _mqttClient.DisconnectAsync().ConfigureAwait(false); _logger.Info <ManagedMqttClient>("Stopped"); } }
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { //todo: else branch can be used with min dependency NET46 #if NET452 || NET461 var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false); #else var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false); #endif var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer(), _logger); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); } catch (Exception exception) { _logger.Error <MqttServerAdapter>(exception, "Error while accepting connection at default endpoint."); //excessive CPU consumed if in endless loop of socket errors await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
async Task SafeDisconnect(CancellationToken cancellationToken) { try { await _adapter.DisconnectAsync(_options.CommunicationTimeout, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { _logger.Error(exception, "Error while disconnecting."); } finally { _adapter.Dispose(); } }
async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken) { try { _lastPacketReceivedTracker.Restart(); while (!cancellationToken.IsCancellationRequested) { // Values described here: [MQTT-3.1.2-24]. // If the client sends 5 sec. the server will allow up to 7.5 seconds. // If the client sends 1 sec. the server will allow up to 1.5 seconds. if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds >= keepAlivePeriod * 1.5D) { _logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", _clientId); await _keepAliveElapsedCallback().ConfigureAwait(false); return; } // The server checks the keep alive timeout every 50 % of the overall keep alive timeout // because the server allows 1.5 times the keep alive value. This means that a value of 5 allows // up to 7.5 seconds. With an interval of 2.5 (5 / 2) the 7.5 is also affected. Waiting the whole // keep alive time will hit at 10 instead of 7.5 (but only one time instead of two times). await Task.Delay(TimeSpan.FromSeconds(keepAlivePeriod * 0.5D), cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientId); } finally { _logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientId); } }
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken) { try { _lastPacketReceivedTracker.Restart(); _lastNonKeepAlivePacketReceivedTracker.Restart(); while (!cancellationToken.IsCancellationRequested) { // Values described here: [MQTT-3.1.2-24]. if (_lastPacketReceivedTracker.Elapsed.TotalSeconds > keepAlivePeriod * 1.5D) { _logger.Warning <MqttClientSession>("Client '{0}': Did not receive any packet or keep alive signal.", _clientId); if (_timeoutCallback != null) { await _timeoutCallback().ConfigureAwait(false); } return; } await Task.Delay(keepAlivePeriod, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error <MqttClientSession>(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientId); } finally { _logger.Trace <MqttClientSession>("Client {0}: Stopped checking keep alive timeout.", _clientId); } }
public async Task <MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (options.ChannelOptions == null) { throw new ArgumentException("ChannelOptions are not set."); } ThrowIfConnected("It is not allowed to connect with a server after the connection is established."); ThrowIfDisposed(); MqttClientAuthenticateResult authenticateResult = null; try { Options = options; _packetIdentifierProvider.Reset(); _packetDispatcher.Reset(); _backgroundCancellationTokenSource = new CancellationTokenSource(); var backgroundCancellationToken = _backgroundCancellationTokenSource.Token; _disconnectGate = 0; var adapter = _adapterFactory.CreateClientAdapter(options, _logger); _adapter = adapter; using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken)) { _logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout})."); await _adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false); _logger.Verbose("Connection with server established."); _publishPacketReceiverQueue = new AsyncQueue <MqttPublishPacket>(); _publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken); _packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken); authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false); } _sendTracker.Restart(); _receiveTracker.Restart(); if (Options.KeepAlivePeriod != TimeSpan.Zero) { _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken); } IsConnected = true; _logger.Info("Connected."); var connectedHandler = ConnectedHandler; if (connectedHandler != null) { await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false); } return(authenticateResult); } catch (Exception exception) { _logger.Error(exception, "Error while connecting with server."); if (!DisconnectIsPending()) { await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false); } throw; } }
public async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken) { var clientId = string.Empty; MqttClientSession clientSession = null; try { if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket)) { throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1]."); } clientId = connectPacket.ClientId; // Switch to the required protocol version before sending any response. clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion; var connectReturnCode = ValidateConnection(connectPacket); if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted) { await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode }).ConfigureAwait(false); return; } var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false); clientSession = result.Session; await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode, IsSessionPresent = result.IsExistingSession }).ConfigureAwait(false); ClientConnectedCallback?.Invoke(new ConnectedMqttClient { ClientId = clientId, ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion }); await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false); } catch (Exception exception) { _logger.Error <MqttClientSessionsManager>(exception, exception.Message); } finally { try { await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false); } catch (Exception) { // ignored } ClientDisconnectedCallback?.Invoke(new ConnectedMqttClient { ClientId = clientId, ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion, PendingApplicationMessages = clientSession?.PendingMessagesQueue.Count ?? 0 }); } }
async Task RunInternalAsync(MqttConnectionValidatorContext connectionValidatorContext) { var disconnectType = MqttClientDisconnectType.NotClean; try { await _onStart(); _logger.Info("Client '{0}': Session started.", ClientId); _channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted; _channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted; Session.WillMessage = ConnectPacket.WillMessage; Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token).Forget(_logger); // TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor. _keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token); await SendAsync( _channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext) ).ConfigureAwait(false); Session.IsCleanSession = false; while (!_cancellationToken.IsCancellationRequested) { var packet = await _channelAdapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationToken.Token).ConfigureAwait(false); if (packet == null) { // The client has closed the connection gracefully. break; } Interlocked.Increment(ref _sentPacketsCount); _lastPacketReceivedTimestamp = DateTime.UtcNow; if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket)) { _lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp; } _keepAliveMonitor.PacketReceived(); if (packet is MqttPublishPacket publishPacket) { await HandleIncomingPublishPacketAsync(publishPacket).ConfigureAwait(false); continue; } if (packet is MqttPubRelPacket pubRelPacket) { var pubCompPacket = new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier, ReasonCode = MqttPubCompReasonCode.Success }; await SendAsync(pubCompPacket).ConfigureAwait(false); continue; } if (packet is MqttSubscribePacket subscribePacket) { await HandleIncomingSubscribePacketAsync(subscribePacket).ConfigureAwait(false); continue; } if (packet is MqttUnsubscribePacket unsubscribePacket) { await HandleIncomingUnsubscribePacketAsync(unsubscribePacket).ConfigureAwait(false); continue; } if (packet is MqttPingReqPacket) { await SendAsync(new MqttPingRespPacket()).ConfigureAwait(false); continue; } if (packet is MqttDisconnectPacket) { Session.WillMessage = null; disconnectType = MqttClientDisconnectType.Clean; StopInternal(); break; } _packetDispatcher.Dispatch(packet); } } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is MqttCommunicationException) { _logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId); } else { _logger.Error(exception, "Client '{0}': Error while receiving client packets.", ClientId); } StopInternal(); } finally { if (_isTakeover) { disconnectType = MqttClientDisconnectType.Takeover; } if (Session.WillMessage != null) { _sessionsManager.DispatchApplicationMessage(Session.WillMessage, this); Session.WillMessage = null; } _packetDispatcher.Reset(); _channelAdapter.ReadingPacketStartedCallback = null; _channelAdapter.ReadingPacketCompletedCallback = null; _logger.Info("Client '{0}': Connection stopped.", ClientId); _packageReceiverTask = null; try { await _onStop(disconnectType); } catch (Exception e) { _logger.Error(e, "client '{0}': Error while cleaning up", ClientId); } } }
private async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) { if (!eventArgs.ApplicationMessage.Topic.Contains(GetRequestTopic(null, null))) { return; } var message = eventArgs.ApplicationMessage; var methodName = message.GetUserProperty("Method"); var id = message.GetUserProperty("Id"); var timeout = message.GetUserProperty <int?>("Timeout"); var broadcast = message.GetUserProperty <bool?>("Broadcast") ?? false; var noResponse = message.GetUserProperty <bool?>("NoResponse") ?? false; var clientId = GetSource(message.Topic); using var serviceScope = _serviceProvider.CreateScope(); using var cts = timeout.HasValue ? new CancellationTokenSource(timeout.Value * 1000) : new CancellationTokenSource(); var responseBuilder = new MqttApplicationMessageBuilder() .WithContentType(DefaultSerializer.ContentType) .WithTopic(GetResponseTopic(GetSource(message.Topic))) .WithAtLeastOnceQoS(); if (broadcast) { responseBuilder.WithUserProperty("Broadcast", true.ToString()); } if (timeout > 0) { responseBuilder.WithMessageExpiryInterval((uint)timeout.Value); } try { if (!RpcMethodResolver.Methods.TryGetValue(methodName, out var method)) { if (string.IsNullOrEmpty(id) || noResponse) { return; } throw new EntryPointNotFoundException($"Method '{methodName}' not found."); } var parameters = method.GetParameters(); object[] args; switch (parameters.Length) { case 0: args = null; break; case 1: var parameterInfo = parameters.First(); args = parameterInfo.ParameterType == typeof(byte[]) ? new[] { (object)message.Payload } : new[] { DefaultSerializer.Deserialize(message.Payload, parameterInfo.ParameterType) }; break; default: _logger.Error(new NotImplementedException(), "Multiple parameters resolving has not been supported yet, please use a key-value object."); return; } var rpcService = (IRpcService)serviceScope.ServiceProvider.GetService(method.DeclaringType); rpcService.CurrentContext = new RpcContext { Topic = message.Topic, RemoteClientId = clientId }; var task = Task.Run(async() => { var returnValue = method.Invoke(rpcService, args); if (returnValue is Task t) { await t.ConfigureAwait(false); if (t.GetType().IsGenericType) { var resultProperty = t.GetType().GetProperty("Result"); Debug.Assert(resultProperty != null); returnValue = resultProperty.GetValue(t); } } return(returnValue); }, cts.Token); if (!string.IsNullOrEmpty(id)) { if (!_waitingCalls.TryAdd(id, new CancellableTask(task, cts))) { throw new InvalidOperationException(); } } else { _noIdCalls.TryAdd(cts, task); } var result = await task.ConfigureAwait(false); responseBuilder.WithUserProperty("Success", true.ToString()); if (!noResponse) { responseBuilder.WithUserProperty("Id", id) .WithPayload(DefaultSerializer.Serialize(result)); } } catch (Exception ex) { responseBuilder.WithUserProperty("Success", false.ToString()) .WithUserProperty("ErrorCode", ex.HResult.ToString()) .WithUserProperty("ErrorMessage", ex.Message); } finally { if (!string.IsNullOrEmpty(id)) { _waitingCalls.TryRemove(id, out _); } else { _noIdCalls.TryRemove(cts, out _); } if (!noResponse) { await _mqttClient.PublishAsync(responseBuilder.Build()).ConfigureAwait(false); } } }