コード例 #1
0
        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;
             * */
        }
コード例 #2
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);
                    }
                }
            }
        }