internal async Task <byte[]> ReadHttpResponseStreamAsync(HttpResponseMessage httpResponse, long?length, CancellationToken ct, bool saveToFile = false, string filename = null, string folderPath = null)
        {
            return(await Task.Run(async() =>
            {
                // Were we already canceled?
                ct.ThrowIfCancellationRequested();
                using (StreamReader sr = new StreamReader(await httpResponse.Content.ReadAsStreamAsync()))
                {
                    DownloadMetric metric = new DownloadMetric(length);
                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();
                    OnDownloadStart?.Invoke(metric);
                    if (!saveToFile)
                    {
                        return FromReaderToStream(sr, new MemoryStream((int)length), ref metric, ref stopwatch, ref length, ct);
                    }
                    else
                    {
                        filename = ToRelativeFilePath(httpResponse.RequestMessage.RequestUri, filename, folderPath);

                        return FromReaderToStream(sr, File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite),
                                                  ref metric, ref stopwatch, ref length, ct);
                    }
                }
            }, ct));
        }
        internal byte[] FromReaderToStream(StreamReader sr, Stream destinationStream,
                                           ref DownloadMetric metric, ref Stopwatch stopwatch, ref long?length, CancellationToken ct)
        {
            //int kb = metric.Speed > Globals.PageSize ? (int)metric.Speed : Globals.PageSize;
            int kb         = 1024 * 65;
            int toDownload = kb;
            var buffer     = new byte[toDownload];
            int bytesRead;

            while ((bytesRead = sr.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    destinationStream.SetLength(0);
                    destinationStream.Close();
                    if (destinationStream is FileStream)
                    {
                        var fs = destinationStream as FileStream;
                        if (File.Exists(fs.Name))
                        {
                            File.Delete(fs.Name);
                        }
                    }
                    ct.ThrowIfCancellationRequested();
                }

                destinationStream.Write(buffer, 0, bytesRead);
                metric.DownloadedBytes += bytesRead;
                metric.ElapsedTime      = stopwatch.Elapsed;
                OnDownloading?.Invoke(metric);

                length    -= bytesRead;
                toDownload = length >= kb ? kb : (int)length;
                buffer     = new byte[toDownload];
            }
            stopwatch.Stop();
            DownloadTrace.Invoke("Download finished!");
            DownloadTrace.Invoke("Returning results...");
            //DownloadCompleted?.Invoke(metric, destinationStream);
            //stopwatch.Reset();

            if (destinationStream is MemoryStream)
            {
                return(((MemoryStream)destinationStream).ToArray());
            }
            else
            {
                destinationStream.Flush();
                destinationStream.Close();
                destinationStream.Dispose();
                return(null);
            }
        }
        internal async Task <byte[]> ProcessDownloadAsync(Uri uri, Stream output, CancellationToken ct)
        {
            byte[] vs = Array.Empty <byte>();
            try
            {
                #region Get file size
                DownloadTrace?.Invoke("Getting file size...");
                WebRequest webRequest = WebRequest.Create(uri);
                webRequest.Method = "HEAD";
                Stopwatch      watch = new Stopwatch();
                DownloadMetric m     = new DownloadMetric();
                long?          bytes;
                watch.Start();
                using (WebResponse webResponse = webRequest.GetResponse())
                {
                    bytes = long.Parse(webResponse.Headers.Get("Content-Length"));
                    DownloadTrace?.Invoke($"File size: {bytes.Value.HumanizeBytes(2)}");
                    m.TotalBytes  = bytes.Value;
                    m.ElapsedTime = watch.Elapsed;
                }

                /*__________________________________________________________________________________
                 |                                                                                |
                 |  .NET runtime has a 2GB size limit for objects.                                |
                 |  ----------------------------------------------                                |
                 |  To adhere to this restriction, this module ONLY allows downloading files      |
                 |  less than 1GB. If the file is greater than 1GB, call DownloadToFile method    |
                 |  instead which downloads the file directly to disk or allow this application   |
                 |  to automatically save the file to disk for you.                               |
                 |  ----------------------------------------------                                |
                 |  1 GB = 1,000,000,000 (1 Billion Bytes).                                       |
                 |                                                                                |
                 *|________________________________________________________________________________|*/
                if (bytes == null)
                {
                    bytes = _maxDowloadSize;
                }
                else
                {
                    if (bytes.Value > _maxDowloadSize)
                    {
                        bytes = _maxDowloadSize;
                    }
                }
                #endregion

                #region Read Content Stream Asynchronously
                long?l = bytes;
                DownloadTrace?.Invoke("Download about to begin.");
                DownloadTrace.Invoke("On your marks...");
                HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest;
                req.Method = "GET";
                req.AddRange(0, bytes.Value);
                vs = await Task.Run(async() =>
                {
                    // Check if operation is already canceled?
                    ct.ThrowIfCancellationRequested();

                    using (StreamReader sr = new StreamReader((await req.GetResponseAsync()).GetResponseStream()))
                    {
                        DownloadTrace.Invoke("Download started!");
                        OnDownloadStart?.Invoke(m);
                        var a = FromReaderToStream(sr, output, ref m, ref watch, ref l, ct);
                        DownloadCompleted?.Invoke(m, null);
                        return(a);
                    }
                }, ct);

                #endregion
            }
            catch (OperationCanceledException)
            {
                string msg = "Download cancelled by user";
                DownloadTrace.Invoke(msg);
                OnError?.Invoke(new DownloadClientException(msg));
            }
            catch (Exception ex)
            {
                string msg = "An unexpected error occured.";
                DownloadTrace?.Invoke(msg);
                OnError?.Invoke(new DownloadClientException($"{msg}\n\nDownload failed. See inner exception for details.", ex));
            }
            return(vs);
        }