예제 #1
0
        private DownloadSummary DownloadWithRetry(IDownloadEngine[] engines, Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component = null, bool verify = false)
        {
            var failureList = new List <DownloadFailureInformation>();

            foreach (var engine in engines)
            {
                var position = outputStream.Position;
                var length   = outputStream.Length;
                try
                {
                    Logger.Trace($"Attempting download '{uri.AbsoluteUri}' using engine '{engine.Name}'");
                    var engineSummary = engine.Download(uri, outputStream, status =>
                    {
                        progress?.Invoke(new ProgressUpdateStatus(engine.Name, status.BytesRead, status.TotalBytes, status.BitRate));
                    }, cancellationToken,
                                                        component);
                    if (outputStream.Length == 0 && !UpdateConfiguration.Instance.AllowEmptyFileDownload)
                    {
                        var exception = new UpdaterException($"Empty file downloaded on '{uri}'.");
                        Logger?.Error(exception, exception.Message);
                        throw exception;
                    }

                    if (verify && outputStream.Length != 0)
                    {
                        if (component is null)
                        {
                            if (UpdateConfiguration.Instance.ValidationPolicy == ValidationPolicy.Enforce)
                            {
                                throw new ValidationFailedException(DownloadResult.MissingOrInvalidValidationContext,
                                                                    "Unable to get necessary validation data because download context is null.");
                            }
                        }
                        else
                        {
                            var componentValidationContext = component.OriginInfo?.ValidationContext;
                            var valid = componentValidationContext?.Verify();

                            if ((!valid.HasValue || !valid.Value) && UpdateConfiguration.Instance.ValidationPolicy == ValidationPolicy.Enforce)
                            {
                                throw new ValidationFailedException(DownloadResult.MissingOrInvalidValidationContext,
                                                                    $"Component '{component.Name}' is missing or has an invalid ValidationInfo");
                            }

                            if (valid.HasValue && valid.Value)
                            {
                                var validationResult = HashVerifier.Verify(outputStream, componentValidationContext);
                                engineSummary.ValidationResult = validationResult;
                                if (validationResult == ValidationResult.HashMismatch)
                                {
                                    var exception = new ValidationFailedException(DownloadResult.HashMismatch,
                                                                                  $"Hash on downloaded file '{uri.AbsoluteUri}' does not match expected value.");
                                    Logger?.Error(exception, exception.Message);
                                    throw exception;
                                }
                            }
                            else
                            {
                                Logger.Trace($"Skipping validation because validation context of Component {component.Name} is not valid.");
                            }
                        }
                    }

                    Logger?.Info($"Download of '{uri.AbsoluteUri}' succeeded using engine '{engine.Name}'");
                    PreferredDownloadEngines.Instance.LastSuccessfulEngineName = engine.Name;
                    engineSummary.DownloadEngine = engine.Name;
                    return(engineSummary);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    failureList.Add(new DownloadFailureInformation(ex, engine.Name));
                    Logger.Trace($"Download failed using {engine.Name} engine. {ex}");

                    if (engine.Equals(engines.LastOrDefault()))
                    {
                        throw new DownloadFailureException(failureList);
                    }

                    cancellationToken.ThrowIfCancellationRequested();
                    outputStream.SetLength(length);
                    outputStream.Seek(position, SeekOrigin.Begin);
                    var millisecondsTimeout = SleepDurationBetweenRetries;
                    if (millisecondsTimeout < 0)
                    {
                        millisecondsTimeout = 0;
                    }
                    Logger.Trace($"Sleeping {millisecondsTimeout} before retrying download.");
                    Thread.Sleep(millisecondsTimeout);
                }
            }
            return(null);
        }
        protected internal Task CalculateComponentStatusAsync(IComponent component)
        {
            Logger.Trace($"Check dependency if update required: {component}");

            var destination = component.Destination;

            Logger.Trace($"Dependency base path: {destination}");
            if (string.IsNullOrEmpty(destination))
            {
                return(Task.FromException(new InvalidOperationException()));
            }

            var filePath = component.GetFilePath();

            if (File.Exists(filePath))
            {
                var currentVersion = GetComponentVersion(component);
                if (currentVersion == null)
                {
                    Logger.Info($"Dependency marked to get updated: {component}");
                    component.CurrentState   = CurrentState.None;
                    component.RequiredAction = ComponentAction.Update;
                    return(Task.CompletedTask);
                }

                component.CurrentState   = CurrentState.Installed;
                component.CurrentVersion = currentVersion;
                component.DiskSize       = new FileInfo(filePath).Length;


                if (component.OriginInfo is null)
                {
                    return(Task.CompletedTask);
                }

                var newVersion = component.OriginInfo.Version;

                if (newVersion != null && newVersion != currentVersion)
                {
                    Logger.Info($"Dependency marked to get updated: {component}");
                    component.RequiredAction = ComponentAction.Update;
                    return(Task.CompletedTask);
                }

                if (component.OriginInfo.ValidationContext is null)
                {
                    Logger.Info($"Dependency marked to keep: {component}");
                    return(Task.CompletedTask);
                }

                var hashResult = HashVerifier.VerifyFile(filePath, component.OriginInfo.ValidationContext);
                if (hashResult == ValidationResult.HashMismatch)
                {
                    Logger.Info($"Dependency marked to get updated: {component}");
                    component.RequiredAction = ComponentAction.Update;
                    return(Task.CompletedTask);
                }

                Logger.Info($"Dependency marked to keep: {component}");
                return(Task.CompletedTask);
            }

            Logger.Info($"Dependency marked to get updated: {component}");
            component.RequiredAction = ComponentAction.Update;
            return(Task.CompletedTask);
        }