async Task <ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter) { var context = new ValidatingConnectionEventArgs(connectPacket, channelAdapter) { SessionItems = new ConcurrentDictionary <object, object>() }; await _eventContainer.ValidatingConnectionEvent.InvokeAsync(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); }
async Task <MqttClient> CreateClientConnection( MqttConnectPacket connectPacket, MqttConnAckPacket connAckPacket, IMqttChannelAdapter channelAdapter, ValidatingConnectionEventArgs validatingConnectionEventArgs) { MqttClient connection; bool sessionShouldPersist; if (validatingConnectionEventArgs.ProtocolVersion == MqttProtocolVersion.V500) { // MQTT 5.0 section 3.1.2.11.2 // The Client and Server MUST store the Session State after the Network Connection is closed if the Session Expiry Interval is greater than 0 [MQTT-3.1.2-23]. // // A Client that only wants to process messages while connected will set the Clean Start to 1 and set the Session Expiry Interval to 0. // It will not receive Application Messages published before it connected and has to subscribe afresh to any topics that it is interested // in each time it connects. // Persist if SessionExpiryInterval != 0, but may start with a clean session sessionShouldPersist = validatingConnectionEventArgs.SessionExpiryInterval != 0; } else { // MQTT 3.1.1 section 3.1.2.4: persist only if 'not CleanSession' // // If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one. // This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be // reused in any subsequent Session [MQTT-3.1.2-6]. sessionShouldPersist = !connectPacket.CleanSession; } using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false)) { MqttSession session; lock (_sessionsManagementLock) { if (!_sessions.TryGetValue(connectPacket.ClientId, out session)) { session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist); } else { if (connectPacket.CleanSession) { _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist); } else { _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); // Session persistence could change for MQTT 5 clients that reconnect with different SessionExpiryInterval session.IsPersistent = sessionShouldPersist; connAckPacket.IsSessionPresent = true; session.Recover(); } } _sessions[connectPacket.ClientId] = session; } if (!connAckPacket.IsSessionPresent) { // TODO: This event is not yet final. It can already be used but restoring sessions from storage will be added later! var preparingSessionEventArgs = new PreparingSessionEventArgs(); await _eventContainer.PreparingSessionEvent.InvokeAsync(preparingSessionEventArgs).ConfigureAwait(false); } MqttClient existing; lock (_clients) { _clients.TryGetValue(connectPacket.ClientId, out existing); connection = CreateConnection(connectPacket, channelAdapter, session); _clients[connectPacket.ClientId] = connection; } if (existing != null) { existing.IsTakenOver = true; await existing.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false); if (_eventContainer.ClientConnectedEvent.HasHandlers) { var eventArgs = new ClientDisconnectedEventArgs { ClientId = existing.Id, DisconnectType = MqttClientDisconnectType.Takeover, Endpoint = existing.Endpoint }; await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false); } } } return(connection); }