/// <summary> /// <see cref="IDownloader.DownloadModules(NetFileCache, IEnumerable{CkanModule})"/> /// </summary> public void DownloadModules( NetFileCache cache, IEnumerable <CkanModule> modules ) { var unique_downloads = new Dictionary <Uri, CkanModule>(); // Walk through all our modules, but only keep the first of each // one that has a unique download path. foreach (CkanModule module in modules.Where(module => !unique_downloads.ContainsKey(module.download))) { unique_downloads[module.download] = module; } this.modules.AddRange(unique_downloads.Values); // Schedule us to process our modules on completion. onCompleted = (_uris, paths, errors) => ModuleDownloadsComplete(cache, _uris, paths, errors); // retrieve the expected download size for each mod List <KeyValuePair <Uri, long> > downloads_with_size = new List <KeyValuePair <Uri, long> >(); foreach (var item in unique_downloads) { downloads_with_size.Add(new KeyValuePair <Uri, long>(item.Key, item.Value.download_size)); } // Start the download! Download(downloads_with_size); log.Debug("Waiting for downloads to finish..."); complete_or_canceled.WaitOne(); var old_download_canceled = download_canceled; // Set up the inter-thread comms for next time. Can not be done at the start // of the method as the thread could pause on the opening line long enough for // a user to cancel. download_canceled = false; complete_or_canceled.Reset(); // If the user cancelled our progress, then signal that. // This *should* be harmless if we're using the curlsharp downloader, // which watches for downloadCanceled all by itself. :) if (old_download_canceled) { // Abort all our traditional downloads, if there are any. foreach (var download in downloads.ToList()) { download.agent.CancelAsync(); } // Abort all our curl downloads, if there are any. foreach (var thread in curl_threads.ToList()) { thread.Abort(); } // Signal to the caller that the user cancelled the download. throw new CancelledActionKraken("Download cancelled by user"); } // Check to see if we've had any errors. If so, then release the kraken! var exceptions = downloads .Select(x => x.error) .Where(ex => ex != null) .ToList(); // Let's check if any of these are certificate errors. If so, // we'll report that instead, as this is common (and user-fixable) // under Linux. if (exceptions.Any(ex => ex is WebException && Regex.IsMatch(ex.Message, "authentication or decryption has failed"))) { throw new MissingCertificateKraken(); } if (exceptions.Count > 0) { throw new DownloadErrorsKraken(exceptions); } // Yay! Everything worked! }
/// <summary> /// Downloads all the modules specified to the cache. /// Even if modules share download URLs, they will only be downloaded once. /// Blocks until the download is complete, cancelled, or errored. /// </summary> public void DownloadModules( NetFileCache cache, IEnumerable<CkanModule> modules ) { var unique_downloads = new Dictionary<Uri, CkanModule>(); // Walk through all our modules, but only keep the first of each // one that has a unique download path. foreach (CkanModule module in modules.Where(module => !unique_downloads.ContainsKey(module.download))) { unique_downloads[module.download] = module; } this.modules.AddRange(unique_downloads.Values); // Schedule us to process our modules on completion. onCompleted = (_uris, paths, errors) => ModuleDownloadsComplete(cache, _uris, paths, errors); // retrieve the expected download size for each mod List<KeyValuePair<Uri, long>> downloads_with_size = new List<KeyValuePair<Uri, long>>(); foreach(var item in unique_downloads) { downloads_with_size.Add(new KeyValuePair<Uri, long>(item.Key, item.Value.download_size)); } // Start the download! Download(downloads_with_size); log.Debug("Waiting for downloads to finish..."); complete_or_canceled.WaitOne(); var old_download_canceled = download_canceled; // Set up the inter-thread comms for next time. Can not be done at the start // of the method as the thread could pause on the opening line long enough for // a user to cancel. download_canceled = false; complete_or_canceled.Reset(); // If the user cancelled our progress, then signal that. // This *should* be harmless if we're using the curlsharp downloader, // which watches for downloadCanceled all by itself. :) if (old_download_canceled) { // Abort all our traditional downloads, if there are any. foreach (var download in downloads) { download.agent.CancelAsync(); } // Abort all our curl downloads, if there are any. foreach (var thread in curl_threads) { thread.Abort(); } // Signal to the caller that the user cancelled the download. throw new CancelledActionKraken("Download cancelled by user"); } // Check to see if we've had any errors. If so, then release the kraken! var exceptions = downloads .Select(x => x.error) .Where(ex => ex != null) .ToList(); // Let's check if any of these are certificate errors. If so, // we'll report that instead, as this is common (and user-fixable) // under Linux. if (exceptions.Any(ex => ex is WebException && Regex.IsMatch(ex.Message, "authentication or decryption has failed"))) { throw new MissingCertificateKraken(); } if (exceptions.Count > 0) { throw new DownloadErrorsKraken(exceptions); } // Yay! Everything worked! }
/// <summary> /// Downloads all the modules specified to the cache. /// Even if modules share download URLs, they will only be downloaded once. /// Blocks until the download is complete, cancelled, or errored. /// </summary> public void DownloadModules( NetFileCache cache, IEnumerable <CkanModule> modules ) { var unique_downloads = new Dictionary <Uri, CkanModule>(); // Walk through all our modules, but only keep the first of each // one that has a unique download path. foreach (CkanModule module in modules.Where(module => !unique_downloads.ContainsKey(module.download))) { unique_downloads[module.download] = module; } this.modules.AddRange(unique_downloads.Values); // Attach our progress report, if requested. onCompleted = (_uris, paths, errors) => ModuleDownloadsComplete(cache, _uris, paths, errors); // Start the download! Download(unique_downloads.Keys); // The Monitor.Wait function releases a lock, and then waits until it can re-acquire it. // Elsewhere, our downloading callback pulses the lock, which causes us to wake up and // continue. lock (download_complete_lock) { log.Debug("Waiting for downloads to finish..."); Monitor.Wait(download_complete_lock); } // If the user cancelled our progress, then signal that. if (downloadCanceled) { foreach (var download in downloads) { download.agent.CancelAsync(); } throw new CancelledActionKraken("Download cancelled by user"); } // Check to see if we've had any errors. If so, then release the kraken! List <Exception> exceptions = downloads .Select(x => x.error) .Where(ex => ex != null) .ToList(); // Let's check if any of these are certificate errors. If so, // we'll report that instead, as this is common (and user-fixable) // under Linux. if (exceptions.Any(ex => ex is WebException && Regex.IsMatch(ex.Message, "authentication or decryption has failed"))) { throw new MissingCertificateKraken(); } if (exceptions.Count > 0) { throw new DownloadErrorsKraken(exceptions); } // Yay! Everything worked! }