protected override DownloadSummary DownloadCore(Uri uri, Stream outputStream, ProgressUpdateCallback progress,
                                                        CancellationToken cancellationToken, IComponent?component)
        {
            var summary = new DownloadSummary();

            using var webResponse = GetWebResponse(uri, ref summary, out var webRequest, cancellationToken);
            if (webResponse != null)
            {
                var registration1 = cancellationToken.Register(() => webResponse.Close());
                try
                {
                    using var responseStream = webResponse.GetResponseStream();
                    var header = webResponse.Headers["Content-Length"];
                    if (string.IsNullOrEmpty(header))
                    {
                        throw new IOException("Error: Content-Length is missing from response header.");
                    }
                    var totalStreamLength = (long)Convert.ToInt32(header);
                    if (totalStreamLength.Equals(0L))
                    {
                        throw new IOException("Error: Response stream length is 0.");
                    }
                    var streamReadError = false;
                    var totalBytesRead  = 0L;
                    var array           = new byte[Math.Max(1024L, Math.Min(totalStreamLength, 32768L))];
                    var registration2   = cancellationToken.Register(() => webRequest.Abort());
                    try
                    {
                        while (true)
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            var bytesRead = responseStream.Read(array, 0, array.Length);
                            streamReadError = bytesRead < 0;
                            if (bytesRead <= 0)
                            {
                                break;
                            }
                            totalBytesRead += bytesRead;
                            outputStream.Write(array, 0, bytesRead);
                            if (totalStreamLength < totalBytesRead)
                            {
                                totalStreamLength = totalBytesRead;
                            }
                            progress?.Invoke(new ProgressUpdateStatus(totalBytesRead, totalStreamLength, 0));
                        }
                    }
                    finally
                    {
                        registration2.Dispose();
                    }
                    cancellationToken.ThrowIfCancellationRequested();
                    if (streamReadError)
                    {
                        throw new IOException("Internal error while downloading the stream.");
                    }
                    summary.DownloadedSize = totalBytesRead;
                    return(summary);
                }
                catch (WebException ex)
                {
                    var message = cancellationToken.IsCancellationRequested
                        ? "DownloadCore failed along with a cancellation request."
                        : "DownloadCore failed";
                    if (cancellationToken.IsCancellationRequested)
                    {
                        Logger.Trace("WebClient error '" + ex.Status + "' with '" + uri.AbsoluteUri + "' - " +
                                     message);
                        cancellationToken.ThrowIfCancellationRequested();
                    }
                    else
                    {
                        Logger.Trace("WebClient error '" + ex.Status + "' with '" + uri.AbsoluteUri + "'.");
                        throw;
                    }
                }
                finally
                {
                    registration1.Dispose();
                }
            }

            return(summary);
        }
        private HttpWebResponse?GetWebResponse(Uri uri, ref DownloadSummary summary, out HttpWebRequest webRequest, CancellationToken cancellationToken)
        {
            var proxyResolution = ProxyResolution.Default;

            while (proxyResolution != ProxyResolution.Error)
            {
                HttpWebResponse?httpWebResponse = null;
                var             successful      = true;
                try
                {
                    webRequest = (HttpWebRequest)WebRequest.Create(uri);
                    webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
                    webRequest.Headers.Add("Accept-Encoding", "gzip,deflate");
                    webRequest.KeepAlive = true;
                    webRequest.Timeout   = 120000;

                    var requestCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
                    webRequest.CachePolicy = requestCachePolicy;

                    switch (proxyResolution)
                    {
                    case ProxyResolution.DefaultCredentialsOrNoAutoProxy:
                        webRequest.UseDefaultCredentials = true;
                        break;

                    case ProxyResolution.NetworkCredentials:
                        webRequest.UseDefaultCredentials = false;
                        webRequest.Proxy             = WebRequest.GetSystemWebProxy();
                        webRequest.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
                        break;

                    case ProxyResolution.DirectAccess:
                        webRequest.Proxy = null;
                        break;
                    }
                    var registerWebRequest = webRequest;
                    using (cancellationToken.Register(() => registerWebRequest.Abort()))
                        httpWebResponse = (HttpWebResponse)webRequest.GetResponse();

                    var responseUri = httpWebResponse.ResponseUri.ToString();
                    if (!string.IsNullOrEmpty(responseUri) &&
                        !uri.ToString().EndsWith(responseUri, StringComparison.InvariantCultureIgnoreCase))
                    {
                        summary.FinalUri = responseUri;
                        Logger.Trace($"Uri '{uri}' + redirected to '{responseUri}'");
                    }

                    switch (httpWebResponse.StatusCode)
                    {
                    case HttpStatusCode.OK:
                        summary.ProxyResolution = proxyResolution.ToString();
                        successful = false;
                        return(httpWebResponse);

                    case HttpStatusCode.UseProxy:
                    case HttpStatusCode.ProxyAuthenticationRequired:
                    case HttpStatusCode.GatewayTimeout:
                        ++proxyResolution;
                        if (proxyResolution == ProxyResolution.Error)
                        {
                            Logger?.Trace($"WebResponse error '{httpWebResponse.StatusCode}' with '{uri}'.");
                            _helper.ThrowWrappedWebException((int)httpWebResponse.StatusCode, "WebRequest.GetResponse", summary.FinalUri);
                            continue;
                        }
                        Logger.Trace($"WebResponse error '{httpWebResponse.StatusCode}' - '{uri.AbsoluteUri}'. Reattempt with proxy set to '{proxyResolution}'");
                        continue;

                    default:
                        proxyResolution = ProxyResolution.Error;
                        Logger.Trace($"WebResponse error '{httpWebResponse.StatusCode}'  - '{uri.AbsoluteUri}'.");
                        _helper.ThrowWrappedWebException((int)httpWebResponse.StatusCode, "WebRequest.GetResponse", summary.FinalUri);
                        continue;
                    }
                }
                catch (WrappedWebException ex)
                {
                    if (proxyResolution == ProxyResolution.Error)
                    {
                        Logger.Debug($"WebResponse exception '{ex.Status}' with '{uri}'.");
                        throw;
                    }
                }
                catch (WebException ex)
                {
                    var errorMessage = cancellationToken.IsCancellationRequested ? "GetWebResponse failed along with a cancellation request" : "GetWebResponse failed";
                    if (cancellationToken.IsCancellationRequested)
                    {
                        Logger.Trace("WebClient error '" + ex.Status + "' with '" + uri.AbsoluteUri + "' - " + errorMessage);
                        cancellationToken.ThrowIfCancellationRequested();
                    }
                    Logger.Trace("WebClient error '" + ex.Status + "' - proxy setting '" + proxyResolution + "' - '" + uri.AbsoluteUri + "'.");
                    switch (ex.Status)
                    {
                    case WebExceptionStatus.NameResolutionFailure:
                    case WebExceptionStatus.ConnectFailure:
                    case WebExceptionStatus.SendFailure:
                    case WebExceptionStatus.ProtocolError:
                    case WebExceptionStatus.ProxyNameResolutionFailure:
                        ++proxyResolution;
                        break;

                    default:
                        proxyResolution = ProxyResolution.Error;
                        break;
                    }
                    if (proxyResolution == ProxyResolution.Error)
                    {
                        Logger.Trace("WebClient failed in '" + uri.AbsoluteUri + "' with '" + ex.Message + "' - '" + uri.AbsoluteUri + "'.");
                        throw;
                    }
                }
                catch (Exception ex)
                {
                    Logger?.Debug(ex, "General exception error in web client.");
                    throw;
                }
                finally
                {
                    if (httpWebResponse != null && successful)
                    {
                        httpWebResponse.Close();
                    }
                }
            }
            webRequest = null;
            return(null);
        }