public UpdateServerEntry PopHost() { // Lock Hosts queue and dequeue next host to BaseURL. if (UpdateServerSelector != null && UpdateServerSelector.Hosts.Count != 0) { lock (UpdateServerSelector.Hosts) UpdateServer = UpdateServerSelector.Hosts.Dequeue(); } // Lock Hosts queue and dequeue next host to BaseURL. return(UpdateServer); }
public async Task ApplyPatchFromWebDownloadTask(UpdateServerEntry baseUrl, string targetPath, string applicationDirPath, IProgress <DirectoryPatcherProgressReport> progress, CancellationToken cancellationToken, string instructionsHash) { UpdateServer = baseUrl; var backupPath = CreateBackupPath(applicationDirPath); var downloadPath = CreateDownloadPath(applicationDirPath); var tempPath = CreateTempPath(applicationDirPath); using (var patchSource = new WebPatchSource(this, downloadPath)) { var patcher = new DirectoryPatcher(new XdeltaPatcher(XdeltaPatchSystemFactory.Preferred), targetPath, backupPath, tempPath, patchSource); await patcher.ApplyPatchAsync(progress, cancellationToken, instructionsHash); DirectoryEx.DeleteContents(downloadPath); DirectoryEx.DeleteContents(tempPath); // delete backup? } }
public async Task <bool> QueryHost(UpdateServerEntry hostObject) { RxLogger.Logger.Instance.Write($"Attempting to contact host {hostObject.Uri.AbsoluteUri}"); System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(hostObject.Uri.AbsoluteUri + TestFile); request.Method = "GET"; //Default to "not found" System.Net.HttpStatusCode response = System.Net.HttpStatusCode.NotFound; try { response = ((System.Net.HttpWebResponse) await request.GetResponseAsync()).StatusCode; } catch { hostObject.HasErrored = true; RxLogger.Logger.Instance.Write($"The host {hostObject.Uri.AbsoluteUri} seems to be offline"); } // Push host to queue if valid if (response == System.Net.HttpStatusCode.OK) { lock (Hosts) { Hosts.Enqueue(hostObject); // Only add the top 4 hosts into this list if (CurrentHostsList.Count < 4) { CurrentHostsList.Add(new UpdateServerSelectorObject(hostObject)); } } RxLogger.Logger.Instance.Write($"Added host {hostObject.Uri.AbsoluteUri} to the hosts queue"); return(true); } return(false); }
public int ConnectionCount; //Stores the amount of times this server was used, it's used for selecting the next best server public UpdateServerSelectorObject(UpdateServerEntry updateServerEntry) { UpdateServer = updateServerEntry; }
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); } } } }