wabbajack/Wabbajack.Networking.Http/SingleThreadedDownloader.cs

162 lines
5.6 KiB
C#
Raw Permalink Normal View History

using System;
using System.Buffers;
2021-09-27 12:42:46 +00:00
using System.IO;
using System.Linq;
2021-09-27 12:42:46 +00:00
using System.Net.Http;
using System.Net.Http.Headers;
2021-09-27 12:42:46 +00:00
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Wabbajack 3.3.0.0 Update (#2416) * added more visible error messages to avoid user confusion added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link extended the error text for starting wabbajack in protected location removed debug code shortened error message to fit in text box * restored warning removed in error, updated changelog, removed debug includes * Update InstallerVM.cs * Update InstallerVM.cs * Update MainWindowViewModel.cs * added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title removed debug code change to pascal case and match existing code style update changelog * Fix manual downloads sometimes launching in browser * Fix manual downloads from secure servers * Remove duplicate user agent code * Create configuration project and performance settings * Bind new performance settings to UI * Use performance settings to limit maximum memory per download * Remove unused settings and related classes * Updated CHANGELOG.md * update CHANGELOG.md * moved the existing files popup to an error message , heralding the return of the overwrite install checkbox * added newline * reverted erroneous edit * gogID for fallout4 added * update CHANGELOG.md * Fix deadlock when loading new settings * change folder/directory check logic * update CHANGELOG.md * revert unnecessary change * update CHANGELOG.md * Bump Wabbajack to .NET 7 * Bump ReactiveUI packages & deps * Update GameFinder to 4.0.0 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com> Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com> Co-authored-by: UrbanCMC <UrbanCMC@web.de> Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 18:33:06 +00:00
using Wabbajack.Configuration;
2021-09-27 12:42:46 +00:00
using Wabbajack.Hashing.xxHash64;
using Wabbajack.Networking.Http.Interfaces;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter;
2021-10-23 16:51:17 +00:00
namespace Wabbajack.Networking.Http;
public class SingleThreadedDownloader : IHttpDownloader
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
private readonly HttpClient _client;
private readonly ILogger<SingleThreadedDownloader> _logger;
Wabbajack 3.3.0.0 Update (#2416) * added more visible error messages to avoid user confusion added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link extended the error text for starting wabbajack in protected location removed debug code shortened error message to fit in text box * restored warning removed in error, updated changelog, removed debug includes * Update InstallerVM.cs * Update InstallerVM.cs * Update MainWindowViewModel.cs * added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title removed debug code change to pascal case and match existing code style update changelog * Fix manual downloads sometimes launching in browser * Fix manual downloads from secure servers * Remove duplicate user agent code * Create configuration project and performance settings * Bind new performance settings to UI * Use performance settings to limit maximum memory per download * Remove unused settings and related classes * Updated CHANGELOG.md * update CHANGELOG.md * moved the existing files popup to an error message , heralding the return of the overwrite install checkbox * added newline * reverted erroneous edit * gogID for fallout4 added * update CHANGELOG.md * Fix deadlock when loading new settings * change folder/directory check logic * update CHANGELOG.md * revert unnecessary change * update CHANGELOG.md * Bump Wabbajack to .NET 7 * Bump ReactiveUI packages & deps * Update GameFinder to 4.0.0 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com> Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com> Co-authored-by: UrbanCMC <UrbanCMC@web.de> Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 18:33:06 +00:00
private readonly PerformanceSettings _settings;
2021-10-23 16:51:17 +00:00
Wabbajack 3.3.0.0 Update (#2416) * added more visible error messages to avoid user confusion added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link extended the error text for starting wabbajack in protected location removed debug code shortened error message to fit in text box * restored warning removed in error, updated changelog, removed debug includes * Update InstallerVM.cs * Update InstallerVM.cs * Update MainWindowViewModel.cs * added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title removed debug code change to pascal case and match existing code style update changelog * Fix manual downloads sometimes launching in browser * Fix manual downloads from secure servers * Remove duplicate user agent code * Create configuration project and performance settings * Bind new performance settings to UI * Use performance settings to limit maximum memory per download * Remove unused settings and related classes * Updated CHANGELOG.md * update CHANGELOG.md * moved the existing files popup to an error message , heralding the return of the overwrite install checkbox * added newline * reverted erroneous edit * gogID for fallout4 added * update CHANGELOG.md * Fix deadlock when loading new settings * change folder/directory check logic * update CHANGELOG.md * revert unnecessary change * update CHANGELOG.md * Bump Wabbajack to .NET 7 * Bump ReactiveUI packages & deps * Update GameFinder to 4.0.0 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com> Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com> Co-authored-by: UrbanCMC <UrbanCMC@web.de> Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 18:33:06 +00:00
public SingleThreadedDownloader(ILogger<SingleThreadedDownloader> logger, HttpClient client, MainSettings settings)
2021-10-23 16:51:17 +00:00
{
_logger = logger;
_client = client;
Wabbajack 3.3.0.0 Update (#2416) * added more visible error messages to avoid user confusion added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link extended the error text for starting wabbajack in protected location removed debug code shortened error message to fit in text box * restored warning removed in error, updated changelog, removed debug includes * Update InstallerVM.cs * Update InstallerVM.cs * Update MainWindowViewModel.cs * added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title removed debug code change to pascal case and match existing code style update changelog * Fix manual downloads sometimes launching in browser * Fix manual downloads from secure servers * Remove duplicate user agent code * Create configuration project and performance settings * Bind new performance settings to UI * Use performance settings to limit maximum memory per download * Remove unused settings and related classes * Updated CHANGELOG.md * update CHANGELOG.md * moved the existing files popup to an error message , heralding the return of the overwrite install checkbox * added newline * reverted erroneous edit * gogID for fallout4 added * update CHANGELOG.md * Fix deadlock when loading new settings * change folder/directory check logic * update CHANGELOG.md * revert unnecessary change * update CHANGELOG.md * Bump Wabbajack to .NET 7 * Bump ReactiveUI packages & deps * Update GameFinder to 4.0.0 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com> Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com> Co-authored-by: UrbanCMC <UrbanCMC@web.de> Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 18:33:06 +00:00
_settings = settings.PerformanceSettings;
2021-10-23 16:51:17 +00:00
}
public async Task<Hash> Download(HttpRequestMessage message, AbsolutePath outputPath, IJob job,
CancellationToken token)
2021-09-27 12:42:46 +00:00
{
Exception downloadError = null!;
Wabbajack 3.3.0.0 Update (#2416) * added more visible error messages to avoid user confusion added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link extended the error text for starting wabbajack in protected location removed debug code shortened error message to fit in text box * restored warning removed in error, updated changelog, removed debug includes * Update InstallerVM.cs * Update InstallerVM.cs * Update MainWindowViewModel.cs * added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title removed debug code change to pascal case and match existing code style update changelog * Fix manual downloads sometimes launching in browser * Fix manual downloads from secure servers * Remove duplicate user agent code * Create configuration project and performance settings * Bind new performance settings to UI * Use performance settings to limit maximum memory per download * Remove unused settings and related classes * Updated CHANGELOG.md * update CHANGELOG.md * moved the existing files popup to an error message , heralding the return of the overwrite install checkbox * added newline * reverted erroneous edit * gogID for fallout4 added * update CHANGELOG.md * Fix deadlock when loading new settings * change folder/directory check logic * update CHANGELOG.md * revert unnecessary change * update CHANGELOG.md * Bump Wabbajack to .NET 7 * Bump ReactiveUI packages & deps * Update GameFinder to 4.0.0 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com> Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com> Co-authored-by: UrbanCMC <UrbanCMC@web.de> Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 18:33:06 +00:00
var downloader = new ResumableDownloader(message, outputPath, job, _settings, _logger);
for (var i = 0; i < 3; i++)
{
try
{
return await downloader.Download(token);
}
catch (Exception ex)
{
downloadError = ex;
_logger.LogDebug("Download for '{name}' failed. Retrying...", outputPath.FileName.ToString());
}
}
_logger.LogError(downloadError, "Failed to download '{name}' after 3 tries.", outputPath.FileName.ToString());
return new Hash();
// using var response = await _client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, token);
// if (!response.IsSuccessStatusCode)
// throw new HttpException(response);
//
// if (job.Size == 0)
// job.Size = response.Content.Headers.ContentLength ?? 0;
//
// /* Need to make this mulitthreaded to be much use
// if ((response.Content.Headers.ContentLength ?? 0) != 0 &&
// response.Headers.AcceptRanges.FirstOrDefault() == "bytes")
// {
// return await ResettingDownloader(response, message, outputPath, job, token);
// }
// */
//
// await using var stream = await response.Content.ReadAsStreamAsync(token);
// await using var outputStream = outputPath.Open(FileMode.Create, FileAccess.Write);
// return await stream.HashingCopy(outputStream, token, job);
2021-09-27 12:42:46 +00:00
}
private const int CHUNK_SIZE = 1024 * 1024 * 8;
private async Task<Hash> ResettingDownloader(HttpResponseMessage response, HttpRequestMessage message, AbsolutePath outputPath, IJob job, CancellationToken token)
{
using var rented = MemoryPool<byte>.Shared.Rent(CHUNK_SIZE);
var buffer = rented.Memory;
var hasher = new xxHashAlgorithm(0);
var running = true;
ulong finalHash = 0;
var inputStream = await response.Content.ReadAsStreamAsync(token);
await using var outputStream = outputPath.Open(FileMode.Create, FileAccess.Write, FileShare.None);
long writePosition = 0;
while (running && !token.IsCancellationRequested)
{
var totalRead = 0;
while (totalRead != buffer.Length)
{
var read = await inputStream.ReadAsync(buffer.Slice(totalRead, buffer.Length - totalRead),
token);
if (read == 0)
{
running = false;
break;
}
if (job != null)
await job.Report(read, token);
totalRead += read;
}
var pendingWrite = outputStream.WriteAsync(buffer[..totalRead], token);
if (running)
{
hasher.TransformByteGroupsInternal(buffer.Span);
await pendingWrite;
}
else
{
var preSize = (totalRead >> 5) << 5;
if (preSize > 0)
{
hasher.TransformByteGroupsInternal(buffer[..preSize].Span);
finalHash = hasher.FinalizeHashValueInternal(buffer[preSize..totalRead].Span);
await pendingWrite;
break;
}
finalHash = hasher.FinalizeHashValueInternal(buffer[..totalRead].Span);
await pendingWrite;
break;
}
{
writePosition += totalRead;
if (job != null)
2022-10-07 20:41:45 +00:00
await job.Report(totalRead, token);
message = CloneMessage(message);
message.Headers.Range = new RangeHeaderValue(writePosition, writePosition + CHUNK_SIZE);
await inputStream.DisposeAsync();
response.Dispose();
response = await _client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, token);
HttpException.ThrowOnFailure(response);
inputStream = await response.Content.ReadAsStreamAsync(token);
}
}
await outputStream.FlushAsync(token);
return new Hash(finalHash);
}
private HttpRequestMessage CloneMessage(HttpRequestMessage message)
{
var newMsg = new HttpRequestMessage(message.Method, message.RequestUri);
foreach (var header in message.Headers)
{
newMsg.Headers.Add(header.Key, header.Value);
}
return newMsg;
}
2021-09-27 12:42:46 +00:00
}