public MqttClientConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, MqttConnectionValidatorContext connectionValidatorContext, IMqttServerOptions serverOptions, MqttClientSessionsManager sessionsManager, IMqttRetainedMessagesManager retainedMessagesManager, IMqttNetLogger logger) { Session = session ?? throw new ArgumentNullException(nameof(session)); _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager)); _channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); _connectionValidatorContext = connectionValidatorContext ?? throw new ArgumentNullException(nameof(connectionValidatorContext)); _dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter; _endpoint = _channelAdapter.Endpoint; ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger.CreateScopedLogger(nameof(MqttClientConnection)); _connectedTimestamp = DateTime.UtcNow; LastPacketReceivedTimestamp = _connectedTimestamp; _lastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp; }
async Task <MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter) { var context = new MqttConnectionValidatorContext(connectPacket, channelAdapter, new ConcurrentDictionary <object, object>()); var connectionValidator = _options.ConnectionValidator; if (connectionValidator == null) { context.ReasonCode = MqttConnectReasonCode.Success; return(context); } await connectionValidator.ValidateConnectionAsync(context).ConfigureAwait(false); // Check the client ID and set a random one if supported. if (string.IsNullOrEmpty(connectPacket.ClientId) && channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500) { connectPacket.ClientId = context.AssignedClientIdentifier; } if (string.IsNullOrEmpty(connectPacket.ClientId)) { context.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid; } return(context); }
MqttClientSession CreateSession(string clientId, MqttConnectionValidatorContext connectionValidatorContext) { return(new MqttClientSession( clientId, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger)); }
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket) { if (_options.ConnectionValidator == null) { return(MqttConnectReturnCode.ConnectionAccepted); } var context = new MqttConnectionValidatorContext( connectPacket.ClientId, connectPacket.Username, connectPacket.Password, connectPacket.WillMessage); _options.ConnectionValidator(context); return(context.ReturnCode); }
MqttClientConnection CreateClientConnection(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter) { lock (_createConnectionSyncRoot) { MqttClientSession session; lock (_sessions) { if (!_sessions.TryGetValue(connectPacket.ClientId, out session)) { _logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, connectionValidatorContext); } else { if (connectPacket.CleanSession) { _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, connectionValidatorContext); } else { _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); } } _sessions[connectPacket.ClientId] = session; } MqttClientConnection existingConnection; MqttClientConnection connection; lock (_connections) { _connections.TryGetValue(connectPacket.ClientId, out existingConnection); connection = CreateConnection(connectPacket, channelAdapter, session, connectionValidatorContext); _connections[connectPacket.ClientId] = connection; } existingConnection?.StopAsync(MqttClientDisconnectReason.SessionTakenOver).GetAwaiter().GetResult(); return(connection); } }
private async Task <MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter) { var context = new MqttConnectionValidatorContext( connectPacket.ClientId, connectPacket.Username, connectPacket.Password, connectPacket.WillMessage, clientAdapter.Endpoint, clientAdapter.IsSecureConnection); var connectionValidator = _options.ConnectionValidator; if (connectionValidator == null) { context.ReturnCode = MqttConnectReturnCode.ConnectionAccepted; return(context); } await connectionValidator.ValidateConnectionAsync(context).ConfigureAwait(false); return(context); }
async Task <MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter, Func <Task> onStart, Func <MqttClientDisconnectType, Task> onStop) { using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false)) { var isSessionPresent = _sessions.TryGetValue(connectPacket.ClientId, out var session); var isConnectionPresent = _connections.TryGetValue(connectPacket.ClientId, out var existingConnection); if (isConnectionPresent) { await existingConnection.StopAsync(true).ConfigureAwait(false); } if (isSessionPresent) { if (connectPacket.CleanSession) { session = null; _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); } else { _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); } } if (session == null) { session = new MqttClientSession(connectPacket.ClientId, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _logger); _logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId); } var connection = new MqttClientConnection(connectPacket, channelAdapter, session, _options, this, _retainedMessagesManager, onStart, onStop, _logger); _connections[connection.ClientId] = connection; _sessions[session.ClientId] = session; return(connection); } }
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); } } }
public Task RunAsync(MqttConnectionValidatorContext connectionValidatorContext) { _packageReceiverTask = RunInternalAsync(connectionValidatorContext); return(_packageReceiverTask); }
MqttClientConnection CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, MqttConnectionValidatorContext connectionValidatorContext) { return(new MqttClientConnection( connectPacket, channelAdapter, session, connectionValidatorContext, _options, this, _retainedMessagesManager, _rootLogger)); }
async Task <MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter) { MqttClientConnection existingConnection; MqttClientConnection connection; using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false)) { MqttClientSession session; lock (_sessions) { if (!_sessions.TryGetValue(connectPacket.ClientId, out session)) { _logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, connectionValidatorContext); } else { if (connectPacket.CleanSession) { _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, connectionValidatorContext); } else { _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); } } _sessions[connectPacket.ClientId] = session; } lock (_connections) { _connections.TryGetValue(connectPacket.ClientId, out existingConnection); connection = CreateConnection(connectPacket, channelAdapter, session, connectionValidatorContext); _connections[connectPacket.ClientId] = connection; } if (existingConnection != null) { await existingConnection.StopAsync(MqttClientDisconnectReason.SessionTakenOver).ConfigureAwait(false); } } return(connection); }
public Validator(MqttConnectionValidatorContext context) { this.context = context; }
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context) { return(_callback(context)); }
async Task <MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter) { MqttClientConnection connection; MqttClientConnection existingConnection = null; using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false)) { var session = _sessions.AddOrUpdate( connectPacket.ClientId, key => { _logger.Verbose("Created a new session for client '{0}'.", key); return(new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger)); }, (key, existingSession) => { if (connectPacket.CleanSession) { _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); return(new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger)); } _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); return(existingSession); }); connection = new MqttClientConnection(connectPacket, channelAdapter, session, connectionValidatorContext, _options, this, _retainedMessagesManager, _rootLogger); _connections.AddOrUpdate( connectPacket.ClientId, key => connection, (key, tempExistingConnection) => { existingConnection = tempExistingConnection; return(connection); }); } // Disconnect the client outside of the lock so that new clients can still connect while a single // one is being disconnected. if (existingConnection != null) { await existingConnection.StopAsync(true).ConfigureAwait(false); } return(connection); }