Example #1
0
        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);
        }
Example #2
0
        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?
            }
        }
Example #3
0
        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;
        }
Example #5
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);
                    }
                }
            }
        }