public async Task LoadNew(string subPath, string hash, CancellationToken cancellationToken, Action <long, long> progressCallback) { string filePath = GetSystemPath(subPath); // Check if the file exists if (File.Exists(filePath)) { // If the file exists and is correct, return without redownloading. if (hash != null && await SHA256.GetFileHashAsync(filePath) == hash) { // Update progress (probably unncessary) long FileSize = new FileInfo(filePath).Length; progressCallback(FileSize, FileSize); return; } // The hash didn't match; delete it. File.Delete(filePath); } // Ensure the necessary directory exists Directory.CreateDirectory(Path.GetDirectoryName(filePath)); // Since I can't await within a lock... await LockDownload(); using (var webClient = new WebClient()) { webClient.Proxy = null; webClient.DownloadProgressChanged += (o, args) => { progressCallback(args.BytesReceived, args.TotalBytesToReceive); }; new_host_selected: using (cancellationToken.Register(() => webClient.CancelAsync())) { RetryStrategy retryStrategy = new RetryStrategy(); try { await retryStrategy.Run(async() => { try { // Download file and wait until finished await webClient.DownloadFileTaskAsync(new Uri(Patcher.BaseURL + "/" + subPath), filePath); // File finished downoading successfully; allow next download to start and check hash UnlockDownload(); if (hash != null && await SHA256.GetFileHashAsync(filePath) != hash) { throw new HashMistmatchException(); // Hash mismatch; throw exception } return(null); } catch (WebException e) { cancellationToken.ThrowIfCancellationRequested(); return(e); } }); // Download successfully completed } catch (TooManyRetriesException) { // Try the next best host; throw an exception if there is none if (Patcher.PopHost() == null) { // Unlock download to leave in clean state. UnlockDownload(); throw new NoReliableHostException(); } // Proceed execution with next mirror goto new_host_selected; } catch (HashMistmatchException) { // Try the next best host; throw an exception if there is none if (Patcher.PopHost() == null) { throw new NoReliableHostException(); } // Reset progress and requeue download await LoadNew(subPath, hash, cancellationToken, progressCallback); } } } /* TODO * if (UseProxy) * { * request.Proxy = new WebProxy(ProxyServer + ":" + ProxyPort.ToString()); * if (ProxyUsername.Length > 0) * request.Proxy.Credentials = new NetworkCredential(ProxyUsername, ProxyPassword); * } * * WebResponse response = request.GetResponse(); * //result.MimeType = res.ContentType; * //result.LastModified = response.LastModified; * if (!resuming)//(Size == 0) * { * //resuming = false; * Size = (int)response.ContentLength; * SizeInKB = (int)Size / 1024; * } * acceptRanges = String.Compare(response.Headers["Accept-Ranges"], "bytes", true) == 0; * */ }
public async Task LoadNew(string subPath, string hash, CancellationToken cancellationToken, Action <long, long, byte> progressCallback) { string filePath = GetSystemPath(subPath); var guid = Guid.NewGuid(); // Check if the file exists if (File.Exists(filePath)) { // If the file exists and is correct, return without redownloading. if (hash != null && await Sha256.GetFileHashAsync(filePath) == hash) { // Update progress (probably unncessary) long fileSize = new FileInfo(filePath).Length; lock (_isDownloadingLock) { progressCallback(fileSize, fileSize, _downloadsRunning); } return; } // The hash didn't match; delete it. File.Delete(filePath); } // Ensure the necessary directory exists Directory.CreateDirectory(Path.GetDirectoryName(filePath)); // Since I can't await within a lock... await LockDownload(); using (var webClient = new MyWebClient()) { webClient.DownloadProgressChanged += (o, args) => { // Notify the RenX Updater window of a downloads progress lock (_isDownloadingLock) { progressCallback(args.BytesReceived, args.TotalBytesToReceive, _downloadsRunning); } // Notify our debug window of a downloads progress AXDebug.AxDebuggerHandler.Instance.UpdateDownload(guid, args.BytesReceived, args.TotalBytesToReceive); }; if (cancellationToken.IsCancellationRequested) { RxLogger.Logger.Instance.Write($"Web client for {subPath} starting to shutdown due to Cancellation Requested"); webClient.Dispose(); return; } // goto labels are the devil, you should be ashamed of using this, whoever you are. :P new_host_selected: using (cancellationToken.Register(() => webClient.CancelAsync())) { RetryStrategy retryStrategy = new RetryStrategy(); UpdateServerEntry thisPatchServer = null; try { await retryStrategy.Run(async() => { try { thisPatchServer = _patcher.UpdateServerSelector.GetNextAvailableServerEntry(); if (thisPatchServer == null) { throw new NoReliableHostException(); } // Mark this patch server as currently used (is active) thisPatchServer.IsUsed = true; // Download file and wait until finished RxLogger.Logger.Instance.Write($"Starting file transfer: {thisPatchServer.Uri.AbsoluteUri}/{_patcher.WebPatchPath}/{subPath}"); await webClient.DownloadFileTaskAsync(new Uri($"{thisPatchServer.Uri.AbsoluteUri}/{_patcher.WebPatchPath}/{subPath}"), filePath); RxLogger.Logger.Instance.Write(" > File Transfer Complete"); thisPatchServer.IsUsed = false; // File finished downoading successfully; allow next download to start and check hash UnlockDownload(); // Check our hash, if it's not the same we re-queue // todo: add a retry count to the file instruction, this is needed because if the servers file is actually broken you'll be in an infiniate download loop if (hash != null && await Sha256.GetFileHashAsync(filePath) != hash) { throw new HashMistmatchException(); // Hash mismatch; throw exception } return(null); } catch (WebException e) { RxLogger.Logger.Instance.Write( $"Error while attempting to transfer the file.\r\n{e.Message}\r\n{e.StackTrace}"); cancellationToken.ThrowIfCancellationRequested(); HttpWebResponse errorResponse = e.Response as HttpWebResponse; if (errorResponse != null && errorResponse.StatusCode >= (HttpStatusCode)400 && errorResponse.StatusCode < (HttpStatusCode)500) { // 400 class errors will never resolve; do not retry throw new TooManyRetriesException(new List <Exception> { e }); } return(e); } }); // Download successfully completed } catch (TooManyRetriesException tooManyRetriesException) { RxLogger.Logger.Instance.Write("Too many retries; caught exceptions: "); foreach (Exception ex in tooManyRetriesException.Exceptions) { RxLogger.Logger.Instance.Write(ex.Message + "\r\n" + ex.StackTrace); } // Mark current mirror failed if (thisPatchServer != null) { thisPatchServer.HasErrored = true; } // Try the next best host; throw an exception if there is none if (_patcher.PopHost() == null) { // Unlock download to leave in clean state. UnlockDownload(); throw new NoReliableHostException(); } // Proceed execution with next mirror goto new_host_selected; } catch (HashMistmatchException) { RxLogger.Logger.Instance.Write($"Invalid file hash for {subPath} - Expected hash {hash}, requeuing download"); // Mark current mirror failed if (thisPatchServer != null) { thisPatchServer.HasErrored = true; } // Try the next best host; throw an exception if there is none if (_patcher.PopHost() == null) { throw new NoReliableHostException(); } // Reset progress and requeue download await LoadNew(subPath, hash, cancellationToken, progressCallback); } } } }