From da3cf33ef3a72d051d261445242be49c9bd2fbd0 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Fri, 16 Jul 2021 09:59:56 -0700 Subject: [PATCH 1/3] [Spa] Try additional hooks to kill the application --- .../Spa/SpaProxy/src/SpaProxyLaunchManager.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs index 336f4e1b2299..06e94ad6a03c 100644 --- a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs +++ b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs @@ -1,15 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; using System.Diagnostics; -using System.IO; -using System.Net.Http; using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.SpaProxy { @@ -25,10 +21,12 @@ internal class SpaProxyLaunchManager : IDisposable public SpaProxyLaunchManager( ILogger logger, + IHostApplicationLifetime appLifetime, IOptions options) { _options = options.Value; _logger = logger; + appLifetime.ApplicationStopping.Register(() => Dispose(true)); } public void StartInBackground(CancellationToken cancellationToken) @@ -199,7 +197,7 @@ private void LaunchDevelopmentProxy() public Task StopAsync(CancellationToken cancellationToken) { - // We don't need to do anything here since Dispose will take care of cleaning up the process if necessary. + Dispose(true); return Task.CompletedTask; } From 44416eda5419acc1d4720bcd390f24f499bd209d Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 17 Aug 2021 09:14:10 -0700 Subject: [PATCH 2/3] Use powershell script to monitor the .NET process from outside and to kill the SPA process if when it ends abruptly --- .../Spa/SpaProxy/src/SpaProxyLaunchManager.cs | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs index 06e94ad6a03c..5fd2187a9e32 100644 --- a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs +++ b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs @@ -31,8 +31,6 @@ public SpaProxyLaunchManager( public void StartInBackground(CancellationToken cancellationToken) { - _logger.LogInformation($"No SPA development server running at {_options.ServerUrl} found."); - // We are not waiting for the SPA proxy to launch, instead we are going to rely on a piece of // middleware to display an HTML document while the SPA proxy is not ready, refresh every three // seconds and redirect to the SPA proxy url once it is ready. @@ -43,6 +41,7 @@ public void StartInBackground(CancellationToken cancellationToken) { if (_launchTask == null) { + _logger.LogInformation($"No SPA development server running at {_options.ServerUrl} found."); _launchTask = UpdateStatus(StartSpaProcessAndProbeForLiveness(cancellationToken)); } } @@ -188,6 +187,46 @@ private void LaunchDevelopmentProxy() WorkingDirectory = Path.Combine(AppContext.BaseDirectory, _options.WorkingDirectory) }; _spaProcess = Process.Start(info); + if (OperatingSystem.IsWindows() && _spaProcess != null) + { + var stopScript = $@"do{{ + try + {{ + $processId = Get-Process -PID {Environment.ProcessId} -ErrorAction Stop; + }}catch + {{ + $processId = $null; + }} + Start-Sleep -Seconds 1; +}}while($processId -ne $null); + +try +{{ + taskkill /T /F /PID {_spaProcess.Id}; +}} +catch +{{ +}}"; + var stopScriptInfo = new ProcessStartInfo( + "powershell.exe", + string.Join(" ", "-NoProfile", "-C", stopScript)) + { + CreateNoWindow = true, + WorkingDirectory = Path.Combine(AppContext.BaseDirectory, _options.WorkingDirectory) + }; + + var stopProcess = Process.Start(stopScriptInfo); + if (stopProcess == null || stopProcess.HasExited) + { + _logger.LogWarning($"SPA process shutdown script '{stopProcess?.Id}' failed to start. The SPA proxy might" + + $" remain open if the dotnet process is terminated abruptly. Use the operating system command to kill" + + $"the process tree for {_spaProcess.Id}"); + } + else + { + _logger.LogDebug($"Watch process '{stopProcess}' started."); + } + } } catch (Exception exception) { From 8c3291a6c5ad21fa9e7108c0d3785be1fa48f5a4 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 17 Aug 2021 09:27:19 -0700 Subject: [PATCH 3/3] Fix missing header --- src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs index 5fd2187a9e32..25be597f6675 100644 --- a/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs +++ b/src/Middleware/Spa/SpaProxy/src/SpaProxyLaunchManager.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Net.Http; using System.Net.Http.Headers; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging;