DownloadManager - Parallel multi-segment download (default 8 connections, configurable up to 16) with per-segment Range requests. Bypasses OVH/Apache per-connection bandwidth throttling — typical speed-up ×4 to ×8 vs single connection. - Reporter task on a dedicated Task with Interlocked aggregate counter (no lock contention with workers). Reports speed/ETA every 250ms even mid-download, fixes the "speed only shows at install transition" bug. - Sparse file pre-allocation via FSCTL_SET_SPARSE before SetLength: no zero-fill, no SeManageVolumePrivilege, instant on any disk type. Removes 5-30s of preparation lag on HDD. - HEAD probe skipped (trust manifest size, signed Ed25519). Falls back to single-segment if first segment returns 200 instead of 206. - Resume URL comparison fixed: ignores HMAC querystring (?exp=&sig=) which changes per request, compares only host+path. Previously every resume started fresh because the old URL never matched the freshly signed one. - Auto-refresh signed URL on 403/410 mid-DL: SemaphoreSlim with 5s debounce so 8 simultaneous segment expirations trigger a single /api/download-url/ call. Slow-connection users (1 Mbps, 30+ hours for 14 GB) keep downloading transparently across multiple TTL cycles. - Per-version hashAlgorithm:none in manifest skips client SHA-256 verification (still relying on Ed25519 manifest signature + HMAC URL). - DangerButton style (red) added to Theme.xaml for the new cancel-resume action. IntegrityService - 16 MiB buffer (was 1 MiB), FileOptions.SequentialScan + Asynchronous, IncrementalHash (uses SHA-NI hardware extensions on .NET 8), double-buffering to overlap CPU and I/O. Typical 14 GB hash verification: 60-180s → 15-40s. HttpClient - MaxConnectionsPerServer=16, EnableMultipleHttp2Connections, HTTP/2 preferred, AutomaticDecompression=None (ZIPs are already compressed), 5min pooled connection lifetime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
1.9 KiB
C#
51 lines
1.9 KiB
C#
using PSLauncher.Models;
|
|
|
|
namespace PSLauncher.Core.Downloads;
|
|
|
|
public interface IDownloadManager
|
|
{
|
|
Task<string> DownloadAsync(
|
|
DownloadJob job,
|
|
IProgress<DownloadProgress>? progress,
|
|
CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Variante avec callback de progression dédié pour la phase de vérification SHA-256.
|
|
/// La phase verify est CPU/IO-bound et peut prendre plusieurs minutes sur un build 14 Go ;
|
|
/// la UI doit pouvoir l'afficher distinctement du DL.
|
|
/// </summary>
|
|
Task<string> DownloadAsync(
|
|
DownloadJob job,
|
|
IProgress<DownloadProgress>? progress,
|
|
IProgress<double>? hashProgress,
|
|
CancellationToken ct);
|
|
|
|
/// <summary>Retourne l'état de DL persistant pour cette version, ou null s'il n'y en a pas.</summary>
|
|
DownloadState? GetResumableState(string version);
|
|
|
|
/// <summary>Supprime l'état persistant et le fichier partiel associé.</summary>
|
|
void DiscardResumableState(string version);
|
|
}
|
|
|
|
public sealed record DownloadJob(
|
|
string Version,
|
|
Uri Url,
|
|
long ExpectedSize,
|
|
string ExpectedSha256)
|
|
{
|
|
/// <summary>
|
|
/// Algo de vérif d'intégrité — "sha256" (défaut) ou "none" pour sauter la vérif.
|
|
/// Pris depuis <see cref="PSLauncher.Models.DownloadDescriptor.HashAlgorithm"/>.
|
|
/// </summary>
|
|
public string? HashAlgorithm { get; init; }
|
|
|
|
/// <summary>
|
|
/// Callback pour obtenir une URL HMAC-signée fraîche en cas d'expiration mid-DL
|
|
/// (le DL d'un 14 Go sur connexion lente peut dépasser le TTL serveur de 6 h).
|
|
/// Le DownloadManager appelle ce callback quand un segment reçoit un 403/410
|
|
/// (indices d'URL expirée), récupère une URL fraîche, et reprend transparemment
|
|
/// le DL sans l'interrompre. Si null, pas d'auto-refresh : le DL fail proprement.
|
|
/// </summary>
|
|
public Func<CancellationToken, Task<Uri?>>? RefreshUrlAsync { get; init; }
|
|
}
|