fix(launcher): harden updater crash recovery
This commit is contained in:
parent
2d2a5b5046
commit
1955ca8ab5
27 changed files with 2714 additions and 544 deletions
128
src/AcDream.Launcher/LauncherUpdateComposition.cs
Normal file
128
src/AcDream.Launcher/LauncherUpdateComposition.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using System.Net;
|
||||
using System.Security;
|
||||
using System.Text.Json;
|
||||
using AcDream.Launcher.Core.Orchestration;
|
||||
using AcDream.Launcher.Core.Updates;
|
||||
using AcDream.Launcher.ViewModels;
|
||||
using AcDream.Platform;
|
||||
|
||||
namespace AcDream.Launcher;
|
||||
|
||||
/// <summary>
|
||||
/// Testable startup transaction for versioned-client/update services. Storage
|
||||
/// failures produce a fail-closed executable resolver and an unavailable UI
|
||||
/// projection; they do not abort profile/installer window construction.
|
||||
/// </summary>
|
||||
internal sealed class LauncherUpdateComposition : IDisposable
|
||||
{
|
||||
private readonly HttpClient? _artifactClient;
|
||||
private readonly ReleaseManifestClient? _manifestClient;
|
||||
|
||||
private LauncherUpdateComposition(
|
||||
ClientVersionStore versions,
|
||||
LauncherExecutableSet executables,
|
||||
ILauncherUpdater updater,
|
||||
HttpClient? artifactClient,
|
||||
ReleaseManifestClient? manifestClient)
|
||||
{
|
||||
Versions = versions;
|
||||
Executables = executables;
|
||||
Updater = updater;
|
||||
_artifactClient = artifactClient;
|
||||
_manifestClient = manifestClient;
|
||||
}
|
||||
|
||||
public ClientVersionStore Versions { get; }
|
||||
|
||||
public LauncherExecutableSet Executables { get; }
|
||||
|
||||
public ILauncherUpdater Updater { get; }
|
||||
|
||||
public static LauncherUpdateComposition Create(
|
||||
ApplicationPathSet paths,
|
||||
string rid,
|
||||
LauncherVersion launcherVersion,
|
||||
string launcherTargetDirectory,
|
||||
Func<bool> hasRunningSessions,
|
||||
Func<ClientVersionStore, string, ClientVersionResolution>? initialize = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(paths);
|
||||
ArgumentNullException.ThrowIfNull(launcherVersion);
|
||||
ArgumentNullException.ThrowIfNull(hasRunningSessions);
|
||||
var versions = new ClientVersionStore(paths);
|
||||
HttpClient? artifactClient = null;
|
||||
ReleaseManifestClient? manifestClient = null;
|
||||
try
|
||||
{
|
||||
_ = initialize is null
|
||||
? versions.LoadAndRecoverAsync(rid).GetAwaiter().GetResult()
|
||||
: initialize(versions, rid);
|
||||
artifactClient = new HttpClient(
|
||||
new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
UseCookies = false,
|
||||
AutomaticDecompression = DecompressionMethods.None,
|
||||
},
|
||||
disposeHandler: true)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(15),
|
||||
};
|
||||
artifactClient.DefaultRequestHeaders.UserAgent.ParseAdd("acdream-launcher/1");
|
||||
manifestClient = new ReleaseManifestClient(TimeSpan.FromSeconds(15));
|
||||
var selfUpdates = new LauncherSelfUpdateManager(paths, artifactClient);
|
||||
var updater = new LauncherUpdater(
|
||||
manifestClient,
|
||||
artifactClient,
|
||||
versions,
|
||||
selfUpdates,
|
||||
launcherVersion,
|
||||
rid,
|
||||
launcherTargetDirectory,
|
||||
hasRunningSessions);
|
||||
return new LauncherUpdateComposition(
|
||||
versions,
|
||||
LauncherExecutableSet.FromCurrentVersionStore(versions),
|
||||
updater,
|
||||
artifactClient,
|
||||
manifestClient);
|
||||
}
|
||||
catch (Exception ex) when (IsStorageFailure(ex))
|
||||
{
|
||||
manifestClient?.Dispose();
|
||||
artifactClient?.Dispose();
|
||||
string status = "Versioned client update storage is unavailable: "
|
||||
+ (string.IsNullOrWhiteSpace(ex.Message)
|
||||
? "the storage operation failed."
|
||||
: ex.Message);
|
||||
var resolution = new ClientVersionResolution(
|
||||
ClientVersionState.Invalid,
|
||||
status,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
return new LauncherUpdateComposition(
|
||||
versions,
|
||||
LauncherExecutableSet.Unavailable(status),
|
||||
new UnavailableLauncherUpdater(status, resolution),
|
||||
artifactClient: null,
|
||||
manifestClient: null);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_manifestClient?.Dispose();
|
||||
_artifactClient?.Dispose();
|
||||
}
|
||||
|
||||
private static bool IsStorageFailure(Exception exception) => exception is
|
||||
IOException
|
||||
or UnauthorizedAccessException
|
||||
or SecurityException
|
||||
or JsonException
|
||||
or FormatException
|
||||
or NotSupportedException
|
||||
or LauncherUpdateException;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue