public ParallelHttpClient(bool IgnoreCompression = false)
        {
            httpClient = new HttpClient(
                new HttpClientHandler()
            {
                AutomaticDecompression = IgnoreCompression ?
                                         DecompressionMethods.None :
                                         DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.None,
                UseCookies = true,
                MaxConnectionsPerServer = 16,
                AllowAutoRedirect       = true
            });

            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", App.UserAgent);
            Status = ParallelHttpClientStatus.Idle;
        }
        public bool DownloadFile(string input, string output, string customMessage = "", long startOffset = -1, long endOffset = -1, CancellationToken token = new CancellationToken())
        {
            if (string.IsNullOrEmpty(customMessage))
            {
                customMessage = $"Downloading {Path.GetFileName(output)}";
            }
            Status = ParallelHttpClientStatus.Downloading;
            bool ret;

            while (!(ret = GetRemoteStreamResponse(input, output, startOffset, endOffset, customMessage, token, false)))
            {
                                #if DEBUG
                Console.WriteLine("Retrying...");
                                #endif
                Thread.Sleep(1000);
            }

            return(ret);
        }
        public bool DownloadStream(string input, MemoryStream output, CancellationToken token = new CancellationToken(), long startOffset = -1, long endOffset = -1, string customMessage = "")
        {
            if (string.IsNullOrEmpty(customMessage))
            {
                customMessage = $"Downloading to stream";
            }
            Status = ParallelHttpClientStatus.Downloading;
            bool ret;

            localStream = output;

            while (!(ret = GetRemoteStreamResponse(input, @"buffer", startOffset, endOffset, customMessage, token, true)))
            {
                                #if DEBUG
                Console.WriteLine("Retrying...");
                                #endif
                Thread.Sleep(1000);
            }

            return(ret);
        }
        public void MergePartialChunks()
        {
            if (CheckExistingPartialChunksSize() != downloadPartialSize)
            {
                                #if DEBUG
                Console.WriteLine("Download is not completed yet! Please do DownloadFileMultipleSession() and wait it for finish!");
                                #endif
                return;
            }

            if (downloadPartialToken.IsCancellationRequested)
            {
                                #if DEBUG
                Console.WriteLine("Cannot do merging since token is cancelled!");
                                #endif
                return;
            }

            Status = ParallelHttpClientStatus.Merging;
            Task.Run(() => GetPartialDownloadEvents());

            string     chunkFileName;
            FileStream chunkFile;
            byte[]     buffer = new byte[67108864];

            int  read;
            long totalRead = 0;
            var  sw        = Stopwatch.StartNew();

            FileInfo fileInfo = new FileInfo($"{downloadPartialOutputPath}_tmp");

            using (FileStream fs = fileInfo.Create())
            {
                for (int i = 0; i < downloadThread; i++)
                {
                    chunkFileName = string.Format("{0}.{1:000}", downloadPartialOutputPath, i + 1);
                    using (chunkFile = new FileStream(chunkFileName, FileMode.Open, FileAccess.Read))
                    {
                        while ((read = chunkFile.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            downloadPartialToken.ThrowIfCancellationRequested();
                            fs.Write(buffer, 0, read);
                            totalRead += read;
                            PartialOnProgressChanged(new PartialDownloadProgressChanged(totalRead, 0, downloadPartialSize, sw.Elapsed.TotalSeconds)
                            {
                                Message = "Merging Chunks", CurrentReceived = read
                            });
                        }
                        chunkFile.Dispose();
                    }

                    File.Delete(chunkFileName);
                }
            }

            if (File.Exists(downloadPartialOutputPath))
            {
                File.Delete(downloadPartialOutputPath);
            }

            fileInfo.MoveTo(downloadPartialOutputPath);

            sw.Stop();
            Status = ParallelHttpClientStatus.Idle;
        }
        public void DownloadFileMultipleSession(string input, string output, string customMessage = "", int threads = 1, CancellationToken token = new CancellationToken())
        {
            downloadThread            = threads;
            downloadPartialToken      = token;
            downloadPartialInputPath  = input;
            downloadPartialOutputPath = output;

            OnCompleted(new DownloadProgressCompleted()
            {
                DownloadCompleted = false
            });
            downloadPartialSize = GetContentLength(downloadPartialInputPath) ?? 0;
            long startContent, endContent;

            segmentDownloadTask       = new List <Task>();
            segmentDownloadProperties = new List <SegmentDownloadProperties>();

            List <ChunkRanges> chunkRanges = new List <ChunkRanges>();

            long partitionSize = (long)Math.Ceiling((double)downloadPartialSize / downloadThread);

            for (int i = 0; i < downloadThread; i++)
            {
                startContent = i * (downloadPartialSize / downloadThread);
                endContent   = i + 1 == downloadThread ? downloadPartialSize : ((i + 1) * (downloadPartialSize / downloadThread)) - 1;

                segmentDownloadProperties.Add(new SegmentDownloadProperties(0, 0, 0, 0)
                {
                    CurrentReceived = 0, StartRange = startContent, EndRange = endContent, PartRange = i
                });
            }

            OnResumabilityChanged(new DownloadStatusChanged(true));
                        #if DEBUG
            Console.WriteLine($"\r\nStarting Partial Download!\r\n\tTotal Size: {BpUtility.ToBytesCount(downloadPartialSize)} ({downloadPartialSize} bytes)\r\n\tThreads/Chunks: {downloadThread}");
                        #endif
            Status = ParallelHttpClientStatus.Downloading;

            try
            {
                foreach (SegmentDownloadProperties j in segmentDownloadProperties)
                {
                    segmentDownloadTask.Add(Task.Run(async() =>
                    {
                        while (!await GetPartialSessionStream(j))
                        {
                                                        #if DEBUG
                            Console.WriteLine($"Retrying to connect for chunk no: {j.PartRange + 1}...");
                                                        #endif
                            Thread.Sleep(1000);
                        }
                    }, downloadPartialToken));
                }

                Task.Run(() => GetPartialDownloadEvents());
                Task.WhenAll(segmentDownloadTask).GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                                #if DEBUG
                Console.WriteLine($"{ex}");
                                #endif
            }

            if (!downloadPartialToken.IsCancellationRequested)
            {
                try
                {
                    MergePartialChunks();
                }
                catch (OperationCanceledException ex)
                {
                                        #if DEBUG
                    Console.WriteLine("Merging cancelled!");
                                        #endif
                    throw new OperationCanceledException("", ex);
                }
            }
            else
            {
                segmentDownloadTask.Clear();
                                #if DEBUG
                Console.WriteLine("Download cancelled!");
                                #endif
                throw new OperationCanceledException();
            }

            segmentDownloadTask.Clear();
                        #if DEBUG
            Console.WriteLine(" Done!");
                        #endif
            Status = ParallelHttpClientStatus.Idle;
        }