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;
///
/// 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.
///
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 hasRunningSessions,
Func? 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;
}