v0.3: resumable downloads with HTTP Range, Polly retry, persistent state
This is the robustness layer needed for real 14 GB builds. A 99%-complete
download that gets interrupted no longer means re-downloading 14 GB.
DownloadManager
---------------
- Range: bytes={resumeFrom}- on every (re)attempt; reads resumeFrom from
the actual size of the .partial file so retries always resume from real
on-disk state, not from a remembered counter.
- If-Range: ETag (or Last-Modified fallback). When the server returns 200
instead of 206 we know the resource changed under us, so we discard
.partial and start fresh.
- Polly resilience pipeline: 6 retries, exponential 1-32s with jitter,
on HttpRequestException / IOException / TimeoutException / 5xx / 408 /
429. Each retry re-evaluates resumeFrom from disk, so the server is
asked only for what's actually missing.
- state.json persisted every 5s OR every 100 MiB, whichever comes first,
via atomic write-then-rename. Holds url, total, downloaded, sha256,
etag, last-modified, and the .partial path.
- Disk-space check happens once at fresh-start (1.05x expected size); a
resume doesn't redo it.
- On final success: SHA-256 of the assembled .partial verified, then
atomic rename to .zip and state.json deleted.
DownloadStateStore
------------------
- New IDownloadStateStore in PSLauncher.Core/Downloads.
- Stores under %LocalAppData%/PSLauncher/downloads/.
- Save / Load / Discard / ScanResumable. Tolerates malformed state files
by ignoring them.
UI hint
-------
VersionRowViewModel now has ResumableBytes; when > 0, the install button
label switches to "↻ Reprendre (X%)" computed from
ResumableBytes / Remote.Download.SizeBytes. MainViewModel.RebuildList
queries IDownloadManager.GetResumableState(version) for each remote-only
row and populates ResumableBytes. Both the featured hero card and the
compact rows bind to InstallButtonLabel.
API change
----------
IDownloadManager gains GetResumableState(version) and
DiscardResumableState(version) so callers (and the UI) can reason about
in-progress downloads without poking at the filesystem directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,7 @@ public partial class App : Application
|
||||
services.AddSingleton<IProcessLauncher, ProcessLauncher>();
|
||||
services.AddSingleton<IIntegrityService, IntegrityService>();
|
||||
services.AddSingleton<IZipInstaller, ZipInstaller>();
|
||||
services.AddSingleton<IDownloadStateStore, DownloadStateStore>();
|
||||
|
||||
services.AddSingleton<IManifestService>(sp =>
|
||||
new ManifestService(
|
||||
|
||||
Reference in New Issue
Block a user