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); }