/// <summary>
        /// Begins a stream copy operation.
        ///
        /// This method wraps the StartCopyStreamAsync method, presenting a different API for it.
        /// As we update the library to be more task-based, callers should gradually move to StartCopyStreamAsync.
        /// </summary>
        /// <param name="completedDelegate">Callback delegate</param>
        /// <param name="copyLength">Number of bytes to copy from source stream to destination stream. Cannot pass in both copyLength and maxLength.</param>
        /// <param name="maxLength">Maximum length of the source stream. Cannot pass in both copyLength and maxLength.</param>
        /// <param name="cancellationToken">The cancellation token for the operation.</param>
        public Task StartCopyStream(Action <ExecutionState <T> > completedDelegate, long?copyLength, long?maxLength, CancellationToken cancellationToken)
        {
            Task streamCopyTask = this.StartCopyStreamAsync(copyLength, maxLength, cancellationToken);

            streamCopyTask.ContinueWith(completedStreamCopyTask =>
            {
                this.state.CancelDelegate = this.previousCancellationDelegate;
                if (completedStreamCopyTask.IsFaulted)
                {
                    this.state.ExceptionRef = completedStreamCopyTask.Exception.InnerException;
                }
                else if (completedStreamCopyTask.IsCanceled)
                {
                    bool timedOut = false;
                    try
                    {
                        timedOut = !this.cancellationTokenSourceAbort.IsCancellationRequested;
                        if (!timedOut && this.cancellationTokenSourceTimeout != null)
                        {
                            // Free up the OS timer as soon as possible.
                            this.cancellationTokenSourceTimeout.Dispose();
                        }
                    }
                    catch (Exception)
                    {
                        // No-op, we want to propagate the cancellation/timeout exception, not whatever may have happened here.
                    }
                    try
                    {
                        if (state.Req != null)
                        {
                            try
                            {
                                state.ReqTimedOut = timedOut;

#if WINDOWS_DESKTOP || WINDOWS_PHONE
                                state.CancellationTokenSource.Cancel();
#endif
                            }
                            catch (Exception ex)
                            {
                                Logger.LogWarning(state.OperationContext, "Aborting the request failed with exception: {0}", ex);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        // No-op, we want to propagate the cancellation/timeout exception, not whatever may have happened here.
                    }

                    this.state.ExceptionRef = timedOut ?
                                              Exceptions.GenerateTimeoutException(state.Cmd != null ? state.Cmd.CurrentResult : null, null) :
                                              Exceptions.GenerateCancellationException(state.Cmd != null ? state.Cmd.CurrentResult : null, null);
                }

                try
                {
                    if (completedDelegate != null)
                    {
                        completedDelegate(this.state);
                    }
                }
                catch (Exception ex)
                {
                    this.state.ExceptionRef = ex;
                }

                this.Dispose();
            });

            return(streamCopyTask);
        }