public async Task RegistryManager_QueryDevicesInvalidServiceCertificateHttp_Fails()
        {
            using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate);
            IQuery query = rm.CreateQuery("select * from devices");
            IotHubCommunicationException exception = await Assert.ThrowsExceptionAsync <IotHubCommunicationException>(
                () => query.GetNextAsTwinAsync()).ConfigureAwait(false);

#if NET451 || NET472
            Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException));
#else
            Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));
#endif
        }
        /// <summary>
        /// Retry an async operation on encountering a transient operation. The retry strategy followed is an exponential backoff strategy.
        /// </summary>
        /// <param name="operationName">An identifier for the async operation to be executed. This is used for debugging purposes.</param>
        /// <param name="asyncOperation">The async operation to be retried.</param>
        /// <param name="shouldExecuteOperation">A function that determines if the operation should be executed.
        /// Eg.: for scenarios when we want to execute the operation only if the client is connected, this would be a function that returns if the client is currently connected.</param>
        /// <param name="logger">The <see cref="ILogger"/> instance to be used.</param>
        /// <param name="exceptionsToBeIgnored">An optional list of exceptions that can be ignored.</param>
        /// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
        internal static async Task RetryTransientExceptionsAsync(
            string operationName,
            Func <Task> asyncOperation,
            Func <bool> shouldExecuteOperation,
            ILogger logger,
            IDictionary <Type, string> exceptionsToBeIgnored = default,
            CancellationToken cancellationToken = default)
        {
            IRetryPolicy retryPolicy = new ExponentialBackoffTransientExceptionRetryPolicy(maxRetryCount: int.MaxValue, exceptionsToBeIgnored: exceptionsToBeIgnored);

            int  attempt = 0;
            bool shouldRetry;

            do
            {
                Exception lastException = new IotHubCommunicationException("Client is currently reconnecting internally; attempt the operation after some time.");
                try
                {
                    if (shouldExecuteOperation())
                    {
                        logger.LogInformation(FormatRetryOperationLogMessage(operationName, attempt, "executing."));

                        await asyncOperation();

                        break;
                    }
                    else
                    {
                        logger.LogWarning(FormatRetryOperationLogMessage(operationName, attempt, "operation is not ready to be executed. Attempt discarded."));
                    }
                }
                catch (Exception ex)
                {
                    logger.LogWarning(FormatRetryOperationLogMessage(operationName, attempt, $"encountered an exception while processing the request: {ex}"));
                    lastException = ex;
                }

                shouldRetry = retryPolicy.ShouldRetry(++attempt, lastException, out TimeSpan retryInterval);
                if (shouldRetry)
                {
                    logger.LogWarning(FormatRetryOperationLogMessage(operationName, attempt, $"caught a recoverable exception, will retry in {retryInterval}."));
                    await Task.Delay(retryInterval);
                }
                else
                {
                    logger.LogWarning(FormatRetryOperationLogMessage(operationName, attempt, $"retry policy determined that the operation should no longer be retried, stopping retries."));
                }
            }while (shouldRetry && !cancellationToken.IsCancellationRequested);
        }
        public async Task JobClient_ScheduleTwinUpdateInvalidServiceCertificateHttp_Fails()
        {
            using var jobClient = JobClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate);
            IotHubCommunicationException exception = await Assert.ThrowsExceptionAsync <IotHubCommunicationException>(
                () => jobClient.ScheduleTwinUpdateAsync(
                    "testDevice",
                    "DeviceId IN ['testDevice']",
                    new Shared.Twin(),
                    DateTime.UtcNow,
                    60)).ConfigureAwait(false);

#if NET451 || NET472
            Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException));
#else
            Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));
#endif
        }
        static async Task <IotHubBridge> CreateFromConnectionStringAsync(string deviceId, string connectionString,
                                                                         int connectionPoolSize, TimeSpan?connectionIdleTimeout, IotHubClientSettings settings, CancellationToken cancellationToken)
        {
            int maxPendingOutboundMessages = settings.MaxPendingOutboundMessages;
            var tcpSettings       = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only);
            var webSocketSettings = new AmqpTransportSettings(TransportType.Amqp_WebSocket_Only);

            webSocketSettings.PrefetchCount = tcpSettings.PrefetchCount = (uint)maxPendingOutboundMessages;
            if (connectionPoolSize > 0)
            {
                var amqpConnectionPoolSettings = new AmqpConnectionPoolSettings
                {
                    MaxPoolSize = unchecked ((uint)connectionPoolSize),
                    Pooling     = connectionPoolSize > 0
                };
                if (connectionIdleTimeout.HasValue)
                {
                    amqpConnectionPoolSettings.ConnectionIdleTimeout = connectionIdleTimeout.Value;
                }
                tcpSettings.AmqpConnectionPoolSettings       = amqpConnectionPoolSettings;
                webSocketSettings.AmqpConnectionPoolSettings = amqpConnectionPoolSettings;
            }
            var client = DeviceClient.CreateFromConnectionString(connectionString, new ITransportSettings[]
            {
                tcpSettings,
                webSocketSettings
            });

            client.SetRetryPolicy(DeviceClientRetryPolicy.Instance);
            var bridge = new IotHubBridge(client, deviceId, settings);

            client.SetConnectionStatusChangesHandler((status, reason) => {
                if (status != ConnectionStatus.Connected)
                {
                    var cause = new IotHubCommunicationException("Connection to IoT Hub is closed");
                    if (Interlocked.CompareExchange(ref bridge.closedCause, cause, null) == null)
                    {
                        bridge.messagingChannel?.Close(cause);
                    }
                }
            });

            // This helps in usage instrumentation at IotHub service.
            client.ProductInfo = $"protocolgateway/poolsize={connectionPoolSize}";

            try
            {
                await client.OpenAsync(cancellationToken);

                cancellationToken.ThrowIfCancellationRequested(); // in case SDK does not always honor cancellation token in async operations
            }
            catch (IotHubException ex)
            {
                client.Dispose();
                throw ex.ToMessagingException();
            }
            catch (Exception)
            {
                client.Dispose();
                throw;
            }
            return(bridge);
        }