private async Task <Response> SendInternalAsync(Request request, WebRequestState state, TimeSpan?connectionTimeout, CancellationToken cancellationToken)
        {
            CancellationTokenRegistration registration;

            try
            {
                registration = cancellationToken.Register(state.CancelRequest);
            }
            catch (ObjectDisposedException)
            {
                return(Responses.Canceled);
            }

            using (registration)
                using (state)
                {
                    if (state.RequestCanceled)
                    {
                        return(Responses.Canceled);
                    }

                    state.Request = WebRequestFactory.Create(request, state.TimeRemaining, Settings, log);

                    HttpActionStatus status;

                    // Step 1: send request body if it was supplied in request.
                    if (state.RequestCanceled)
                    {
                        return(Responses.Canceled);
                    }

                    if (request.HasBody)
                    {
                        status = await connectTimeLimiter
                                 .LimitConnectTime(SendRequestBodyAsync(request, state, cancellationToken), request, state, connectionTimeout)
                                 .ConfigureAwait(false);

                        if (status != HttpActionStatus.Success)
                        {
                            return(responseFactory.BuildFailureResponse(status, state));
                        }
                    }

                    // Step 2: receive response from server.
                    if (state.RequestCanceled)
                    {
                        return(Responses.Canceled);
                    }

                    status = request.HasBody
                    ? await GetResponseAsync(request, state).ConfigureAwait(false)
                    : await connectTimeLimiter.LimitConnectTime(GetResponseAsync(request, state), request, state, connectionTimeout).ConfigureAwait(false);

                    if (status != HttpActionStatus.Success)
                    {
                        return(responseFactory.BuildFailureResponse(status, state));
                    }

                    // Step 3 - download request body if it exists.
                    if (!NeedToReadResponseBody(request, state))
                    {
                        return(responseFactory.BuildSuccessResponse(state));
                    }

                    if (ResponseBodyIsTooLarge(state))
                    {
                        state.CancelRequestAttempt();
                        return(responseFactory.BuildResponse(ResponseCode.InsufficientStorage, state));
                    }

                    if (state.RequestCanceled)
                    {
                        return(Responses.Canceled);
                    }

                    if (NeedToStreamResponseBody(state))
                    {
                        state.ReturnStreamDirectly = true;
                        state.PreventNextDispose();
                        return(responseFactory.BuildSuccessResponse(state));
                    }

                    status = await ReadResponseBodyAsync(request, state, cancellationToken)
                             .ConfigureAwait(false);

                    return(status == HttpActionStatus.Success
                    ? responseFactory.BuildSuccessResponse(state)
                    : responseFactory.BuildFailureResponse(status, state));
                }
        }