|
23 | 23 |
|
24 | 24 | using System; |
25 | 25 | using System.Net; |
| 26 | +using System.Threading; |
26 | 27 | using System.Threading.Tasks; |
27 | 28 | using ArchiSteamFarm.Core; |
28 | 29 | using ArchiSteamFarm.IPC.Requests; |
|
31 | 32 | using ArchiSteamFarm.Steam; |
32 | 33 | using ArchiSteamFarm.Storage; |
33 | 34 | using Microsoft.AspNetCore.Mvc; |
| 35 | +using Microsoft.Extensions.Hosting; |
34 | 36 |
|
35 | 37 | namespace ArchiSteamFarm.IPC.Controllers.Api; |
36 | 38 |
|
37 | 39 | [Route("Api/Command")] |
38 | 40 | public sealed class CommandController : ArchiController { |
| 41 | + private readonly IHostApplicationLifetime ApplicationLifetime; |
| 42 | + |
| 43 | + public CommandController(IHostApplicationLifetime applicationLifetime) { |
| 44 | + ArgumentNullException.ThrowIfNull(applicationLifetime); |
| 45 | + |
| 46 | + ApplicationLifetime = applicationLifetime; |
| 47 | + } |
| 48 | + |
39 | 49 | /// <summary> |
40 | 50 | /// Executes a command. |
41 | 51 | /// </summary> |
@@ -72,8 +82,28 @@ public async Task<ActionResult<GenericResponse>> CommandPost([FromBody] CommandR |
72 | 82 | command = command[commandPrefix.Length..]; |
73 | 83 | } |
74 | 84 |
|
75 | | - string? response = await targetBot.Commands.Response(EAccess.Owner, command).ConfigureAwait(false); |
| 85 | + // Update process can result in kestrel shutdown request, just before patching the files |
| 86 | + // In this case, we have very little opportunity to do anything, especially we will not have access to the return value of the command |
| 87 | + // That's because update command 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 |
| 88 | + // Therefore, we'll allow this command to proceed while listening for application shutdown request, if it happens, we'll do our best by getting alternative signal that update is proceeding |
| 89 | + TaskCompletionSource<bool> applicationStopping = new(); |
| 90 | + |
| 91 | + CancellationTokenRegistration applicationStoppingRegistration = ApplicationLifetime.ApplicationStopping.Register(() => applicationStopping.SetResult(true)); |
76 | 92 |
|
77 | | - return Ok(new GenericResponse<string>(response)); |
| 93 | + await using (applicationStoppingRegistration.ConfigureAwait(false)) { |
| 94 | + Task<string?> commandTask = targetBot.Commands.Response(EAccess.Owner, command); |
| 95 | + |
| 96 | + string? response; |
| 97 | + |
| 98 | + if (await Task.WhenAny(commandTask, applicationStopping.Task).ConfigureAwait(false) == commandTask) { |
| 99 | + response = await commandTask.ConfigureAwait(false); |
| 100 | + } else { |
| 101 | + // It's almost guaranteed that this is the result of update process requesting kestrel shutdown |
| 102 | + // 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 |
| 103 | + response = ASFController.PendingVersionUpdate != null ? Strings.PatchingFiles : Strings.Exiting; |
| 104 | + } |
| 105 | + |
| 106 | + return Ok(new GenericResponse<string>(response)); |
| 107 | + } |
78 | 108 | } |
79 | 109 | } |
0 commit comments