TimeSpan DeriveKeepAliveTimeout(ConnectPacket packet)
        {
            TimeSpan timeout = TimeSpan.FromSeconds(packet.KeepAliveInSeconds * 1.5);
            TimeSpan? maxTimeout = this.settings.MaxKeepAliveTimeout;
            if (maxTimeout.HasValue && (timeout > maxTimeout.Value || timeout == TimeSpan.Zero))
            {
                if (MqttIotHubAdapterEventSource.Log.IsVerboseEnabled)
                {
                    MqttIotHubAdapterEventSource.Log.Verbose(string.Format("Requested Keep Alive timeout is longer than the max allowed. Limiting to max value of {0}.", maxTimeout.Value), null);
                }
                return maxTimeout.Value;
            }

            return timeout;
        }
        async void Connect(IChannelHandlerContext context)
        {

            var connectPacket = new ConnectPacket
            {
                ClientId = this.deviceId,
                HasUsername = true,
                Username = this.iotHubHostName + "/" + this.deviceId,
                HasPassword = true,
                Password = this.password,
                KeepAliveInSeconds = this.mqttTransportSettings.KeepAliveInSeconds,
                CleanSession = this.mqttTransportSettings.CleanSession,
                HasWill = this.mqttTransportSettings.HasWill
            };
            if (connectPacket.HasWill)
            {
                Message message = this.willMessage.Message;
                QualityOfService publishToServerQoS = this.mqttTransportSettings.PublishToServerQoS;
                string topicName = string.Format(TelemetryTopicFormat, this.deviceId);
                PublishPacket will = await Util.ComposePublishPacketAsync(context, message, publishToServerQoS, topicName);

                connectPacket.WillMessage = will.Payload;
                connectPacket.WillQualityOfService = this.willMessage.QoS;
                connectPacket.WillRetain = false;
                connectPacket.WillTopicName = will.TopicName;
            }
            this.stateFlags = StateFlags.Connecting;

            await Util.WriteMessageAsync(context, connectPacket, ShutdownOnWriteErrorHandler);
            this.lastChannelActivityTime = DateTime.UtcNow;
            this.ScheduleKeepConnectionAlive(context);

            this.ScheduleCheckConnectTimeout(context);
        }
        /// <summary>
        ///     Performs complete initialization of <see cref="MqttIotHubAdapter" /> based on received CONNECT packet.
        /// </summary>
        /// <param name="context"><see cref="IChannelHandlerContext" /> instance.</param>
        /// <param name="packet">CONNECT packet.</param>
        async void Connect(IChannelHandlerContext context, ConnectPacket packet)
        {
            bool connAckSent = false;

            Exception exception = null;
            try
            {
                if (!this.IsInState(StateFlags.WaitingForConnect))
                {
                    ShutdownOnError(context, "CONNECT has been received in current session already. Only one CONNECT is expected per session.");
                    return;
                }

                this.stateFlags = StateFlags.ProcessingConnect;
                AuthenticationResult authResult = await this.authProvider.AuthenticateAsync(packet.ClientId,
                    packet.Username, packet.Password, context.Channel.RemoteAddress);
                if (!authResult.IsSuccessful)
                {
                    connAckSent = true;
                    await Util.WriteMessageAsync(context, new ConnAckPacket
                    {
                        ReturnCode = ConnectReturnCode.RefusedNotAuthorized
                    });
                    PerformanceCounters.ConnectionFailedAuthPerSecond.Increment();
                    ShutdownOnError(context, "Authentication failed.");
                    return;
                }

                this.deviceId = authResult.DeviceId;

                this.iotHubClient = await this.deviceClientFactory(authResult);

                bool sessionPresent = await this.EstablishSessionStateAsync(this.deviceId, packet.CleanSession);

                this.keepAliveTimeout = this.DeriveKeepAliveTimeout(packet);

                if (packet.HasWill)
                {
                    var will = new PublishPacket(packet.WillQualityOfService, false, packet.WillRetain);
                    will.TopicName = packet.WillTopicName;
                    will.Payload = packet.WillMessage;
                    this.willPacket = will;
                }

                this.sessionContext = new Dictionary<string, string>
                {
                    { DeviceIdParam, this.deviceId }
                };

                this.StartReceiving(context);

                connAckSent = true;
                await Util.WriteMessageAsync(context, new ConnAckPacket
                {
                    SessionPresent = sessionPresent,
                    ReturnCode = ConnectReturnCode.Accepted
                });

                this.CompleteConnect(context);
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            if (exception != null)
            {
                if (!connAckSent)
                {
                    try
                    {
                        await Util.WriteMessageAsync(context, new ConnAckPacket
                        {
                            ReturnCode = ConnectReturnCode.RefusedServerUnavailable
                        });
                    }
                    catch (Exception ex)
                    {
                        if (MqttIotHubAdapterEventSource.Log.IsVerboseEnabled)
                        {
                            MqttIotHubAdapterEventSource.Log.Verbose("Error sending 'Server Unavailable' CONNACK.", ex.ToString());
                        }
                    }
                }

                ShutdownOnError(context, "CONNECT", exception);
            }
        }