internal static CosmosClientContext Create(
            CosmosClient cosmosClient,
            CosmosClientOptions clientOptions)
        {
            if (cosmosClient == null)
            {
                throw new ArgumentNullException(nameof(cosmosClient));
            }

            clientOptions = ClientContextCore.CreateOrCloneClientOptions(clientOptions);
            HttpMessageHandler httpMessageHandler = CosmosHttpClientCore.CreateHttpClientHandler(
                clientOptions.GatewayModeMaxConnectionLimit,
                clientOptions.WebProxy);

            DocumentClient documentClient = new DocumentClient(
                cosmosClient.Endpoint,
                cosmosClient.AuthorizationTokenProvider,
                apitype: clientOptions.ApiType,
                sendingRequestEventArgs: clientOptions.SendingRequestEventArgs,
                transportClientHandlerFactory: clientOptions.TransportClientHandlerFactory,
                connectionPolicy: clientOptions.GetConnectionPolicy(),
                enableCpuMonitor: clientOptions.EnableCpuMonitor,
                storeClientFactory: clientOptions.StoreClientFactory,
                desiredConsistencyLevel: clientOptions.GetDocumentsConsistencyLevel(),
                handler: httpMessageHandler,
                sessionContainer: clientOptions.SessionContainer);

            return(ClientContextCore.Create(
                       cosmosClient,
                       documentClient,
                       clientOptions));
        }
        public static CosmosHttpClient CreateWithConnectionPolicy(
            ApiType apiType,
            ICommunicationEventSource eventSource,
            ConnectionPolicy connectionPolicy,
            HttpMessageHandler httpMessageHandler,
            EventHandler <SendingRequestEventArgs> sendingRequestEventArgs,
            EventHandler <ReceivedResponseEventArgs> receivedResponseEventArgs)
        {
            if (connectionPolicy == null)
            {
                throw new ArgumentNullException(nameof(connectionPolicy));
            }

            Func <HttpClient> httpClientFactory = connectionPolicy.HttpClientFactory;

            if (httpClientFactory != null)
            {
                if (sendingRequestEventArgs != null &&
                    receivedResponseEventArgs != null)
                {
                    throw new InvalidOperationException($"{nameof(connectionPolicy.HttpClientFactory)} can not be set at the same time as {nameof(sendingRequestEventArgs)} or {nameof(ReceivedResponseEventArgs)}");
                }

                HttpClient userHttpClient = httpClientFactory.Invoke() ?? throw new ArgumentNullException($"{nameof(httpClientFactory)} returned null. {nameof(httpClientFactory)} must return a HttpClient instance.");
                return(CosmosHttpClientCore.CreateHelper(
                           httpClient: userHttpClient,
                           httpMessageHandler: httpMessageHandler,
                           requestTimeout: connectionPolicy.RequestTimeout,
                           userAgentContainer: connectionPolicy.UserAgentContainer,
                           apiType: apiType,
                           eventSource: eventSource));
            }

            if (httpMessageHandler == null)
            {
                httpMessageHandler = CosmosHttpClientCore.CreateHttpClientHandler(
                    gatewayModeMaxConnectionLimit: connectionPolicy.MaxConnectionLimit,
                    webProxy: null);
            }

            if (sendingRequestEventArgs != null ||
                receivedResponseEventArgs != null)
            {
                httpMessageHandler = CosmosHttpClientCore.CreateHttpMessageHandler(
                    httpMessageHandler,
                    sendingRequestEventArgs,
                    receivedResponseEventArgs);
            }

            HttpClient httpClient = new HttpClient(httpMessageHandler);

            return(CosmosHttpClientCore.CreateHelper(
                       httpClient: httpClient,
                       httpMessageHandler: httpMessageHandler,
                       requestTimeout: connectionPolicy.RequestTimeout,
                       userAgentContainer: connectionPolicy.UserAgentContainer,
                       apiType: apiType,
                       eventSource: eventSource));
        }
        public async Task DocumentClient_BuildHttpClientFactory_WithHandler()
        {
            HttpMessageHandler messageHandler   = new CustomMessageHandler();
            ConnectionPolicy   connectionPolicy = new ConnectionPolicy()
            {
                HttpClientFactory = () => new HttpClient(messageHandler)
            };

            CosmosHttpClient httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy(
                apiType: ApiType.None,
                eventSource: DocumentClientEventSource.Instance,
                connectionPolicy: connectionPolicy,
                httpMessageHandler: null,
                sendingRequestEventArgs: null,
                receivedResponseEventArgs: null);

            Assert.IsNotNull(httpClient);
            HttpResponseMessage response = await httpClient.GetAsync(
                uri : new Uri("https://localhost"),
                additionalHeaders : new DictionaryNameValueCollection(),
                resourceType : ResourceType.Document,
                diagnosticsContext : null,
                cancellationToken : default);
        private async Task <HttpResponseMessage> SendHttpHelperAsync(
            Func <ValueTask <HttpRequestMessage> > createRequestMessageAsync,
            ResourceType resourceType,
            HttpTimeoutPolicy timeoutPolicy,
            IClientSideRequestStatistics clientSideRequestStatistics,
            CancellationToken cancellationToken)
        {
            DateTime startDateTimeUtc = DateTime.UtcNow;
            IEnumerator <(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> timeoutEnumerator = timeoutPolicy.GetTimeoutEnumerator();

            timeoutEnumerator.MoveNext();
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();

                (TimeSpan requestTimeout, TimeSpan delayForNextRequest) = timeoutEnumerator.Current;
                using (HttpRequestMessage requestMessage = await createRequestMessageAsync())
                {
                    // If the default cancellation token is passed then use the timeout policy
                    using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                    cancellationTokenSource.CancelAfter(requestTimeout);
                    DateTime requestStartTime = DateTime.UtcNow;
                    try
                    {
                        HttpResponseMessage responseMessage = await this.ExecuteHttpHelperAsync(
                            requestMessage,
                            resourceType,
                            cancellationTokenSource.Token);

                        if (clientSideRequestStatistics is ClientSideRequestStatisticsTraceDatum datum)
                        {
                            datum.RecordHttpResponse(requestMessage, responseMessage, resourceType, requestStartTime);
                        }

                        if (!timeoutPolicy.ShouldRetryBasedOnResponse(requestMessage.Method, responseMessage))
                        {
                            return(responseMessage);
                        }

                        bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutPolicy, startDateTimeUtc, timeoutEnumerator);
                        if (isOutOfRetries)
                        {
                            return(responseMessage);
                        }
                    }
                    catch (Exception e)
                    {
                        if (clientSideRequestStatistics is ClientSideRequestStatisticsTraceDatum datum)
                        {
                            datum.RecordHttpException(requestMessage, e, resourceType, requestStartTime);
                        }
                        bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutPolicy, startDateTimeUtc, timeoutEnumerator);

                        switch (e)
                        {
                        case OperationCanceledException operationCanceledException:
                            // Throw if the user passed in cancellation was requested
                            if (cancellationToken.IsCancellationRequested)
                            {
                                throw;
                            }

                            // Convert OperationCanceledException to 408 when the HTTP client throws it. This makes it clear that the
                            // the request timed out and was not user canceled operation.
                            if (isOutOfRetries || !timeoutPolicy.IsSafeToRetry(requestMessage.Method))
                            {
                                // throw current exception (caught in transport handler)
                                string message =
                                    $"GatewayStoreClient Request Timeout. Start Time UTC:{startDateTimeUtc}; Total Duration:{(DateTime.UtcNow - startDateTimeUtc).TotalMilliseconds} Ms; Request Timeout {requestTimeout.TotalMilliseconds} Ms; Http Client Timeout:{this.httpClient.Timeout.TotalMilliseconds} Ms; Activity id: {System.Diagnostics.Trace.CorrelationManager.ActivityId};";
                                e.Data.Add("Message", message);
                                throw;
                            }

                            break;

                        case WebException webException:
                            if (isOutOfRetries || (!timeoutPolicy.IsSafeToRetry(requestMessage.Method) && !WebExceptionUtility.IsWebExceptionRetriable(webException)))
                            {
                                throw;
                            }

                            break;

                        case HttpRequestException httpRequestException:
                            if (isOutOfRetries || !timeoutPolicy.IsSafeToRetry(requestMessage.Method))
                            {
                                throw;
                            }

                            break;

                        default:
                            throw;
                        }
                    }
                }

                if (delayForNextRequest != TimeSpan.Zero)
                {
                    await Task.Delay(delayForNextRequest);
                }
            }
        }