From f32fafdf150a03f22101c5c85e151fbca677a6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Domeradzki?= Date: Sat, 12 Oct 2024 21:03:11 +0200 Subject: [PATCH] Resolve eternal TODO with ASF API update routine --- ArchiSteamFarm/Core/ASF.cs | 6 +++- .../IPC/Controllers/Api/ASFController.cs | 28 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ArchiSteamFarm/Core/ASF.cs b/ArchiSteamFarm/Core/ASF.cs index 0e28225260af5..52f6390b3da42 100644 --- a/ArchiSteamFarm/Core/ASF.cs +++ b/ArchiSteamFarm/Core/ASF.cs @@ -36,6 +36,7 @@ using System.Threading.Tasks; using ArchiSteamFarm.Helpers; using ArchiSteamFarm.IPC; +using ArchiSteamFarm.IPC.Controllers.Api; using ArchiSteamFarm.Localization; using ArchiSteamFarm.NLog; using ArchiSteamFarm.Plugins; @@ -894,11 +895,14 @@ private static async Task UpdateAndRestart() { if (kestrelWasRunning) { // We disable ArchiKestrel here as the update process moves the core files and might result in IPC crash - // TODO: It might fail if the update was triggered from the API, this should be something to improve in the future, by changing the structure into request -> return response -> finish update + ASFController.PendingVersionUpdate = newVersion; + try { await ArchiKestrel.Stop().ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + } finally { + ASFController.PendingVersionUpdate = null; } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs index 6cc6eb02d5981..dac637e7636ae 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs @@ -34,11 +34,22 @@ using ArchiSteamFarm.Steam.Interaction; using ArchiSteamFarm.Storage; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; namespace ArchiSteamFarm.IPC.Controllers.Api; [Route("Api/ASF")] public sealed class ASFController : ArchiController { + internal static Version? PendingVersionUpdate { get; set; } + + private readonly IHostApplicationLifetime ApplicationLifetime; + + public ASFController(IHostApplicationLifetime applicationLifetime) { + ArgumentNullException.ThrowIfNull(applicationLifetime); + + ApplicationLifetime = applicationLifetime; + } + /// /// Encrypts data with ASF encryption mechanisms using provided details. /// @@ -185,7 +196,22 @@ public async Task>> UpdatePost([FromBody] U return BadRequest(new GenericResponse(false, Strings.FormatErrorIsInvalid(nameof(request.Channel)))); } - (bool success, string? message, Version? version) = await Actions.Update(request.Channel, request.Forced).ConfigureAwait(false); + // Update process can result in kestrel shutdown request, just before patching the files + // In this case, we have very little opportunity to do anything, especially we will not have access to the return value of the action + // That's because update action will synchronously stop the kestrel, and wait for it before proceeding with an update, and that'll wait for us finishing the request, never happening + // Therefore, we'll allow this action to proceed while listening for application shutdown request, if it happens, we'll do our best by getting alternative signal that update is proceeding + bool success; + string? message = null; + Version? version; + + try { + (success, message, version) = await Task.Run(() => Actions.Update(request.Channel, request.Forced), ApplicationLifetime.ApplicationStopping).ConfigureAwait(false); + } catch (TaskCanceledException e) { + // It's almost guaranteed that this is the result of update process requesting kestrel shutdown + // However, we're still going to check PendingVersionUpdate, which should be set by the update process as alternative way to inform us about pending update + version = PendingVersionUpdate; + success = version != null; + } if (string.IsNullOrEmpty(message)) { message = success ? Strings.Success : Strings.WarningFailed;