Beispiel #1
0
        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;
        }
Beispiel #2
0
        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);
        }
Beispiel #7
0
        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);
            }
        }
Beispiel #8
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);
                }
            }
        }
Beispiel #9
0
 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));
 }
Beispiel #14
0
        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);
        }