diff --git a/global.json b/global.json index 5bf3e71..08b8436 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.200", + "version": "8.0.411", "rollForward": "latestFeature" } } diff --git a/src/AspNetCore.SpaYarp/AspNetCore.SpaYarp.csproj b/src/AspNetCore.SpaYarp/AspNetCore.SpaYarp.csproj index 8cc9912..645963c 100644 --- a/src/AspNetCore.SpaYarp/AspNetCore.SpaYarp.csproj +++ b/src/AspNetCore.SpaYarp/AspNetCore.SpaYarp.csproj @@ -2,7 +2,7 @@ An alternative approach to the new ASP.NET Core SPA templates in .NET 6. It uses YARP as proxy to forward requests to the SPA dev server. - net6.0;net7.0 + net6.0;net7.0;net8.0 enable enable 11 @@ -24,7 +24,7 @@ - + diff --git a/src/AspNetCore.SpaYarp/SpaProxyLaunchManager.cs b/src/AspNetCore.SpaYarp/SpaProxyLaunchManager.cs index e74b5cc..bf9ca07 100644 --- a/src/AspNetCore.SpaYarp/SpaProxyLaunchManager.cs +++ b/src/AspNetCore.SpaYarp/SpaProxyLaunchManager.cs @@ -40,7 +40,7 @@ public void StartInBackground(CancellationToken cancellationToken) { if (_launchTask == null) { - _logger.LogInformation($"No SPA development server running at {_options.ClientUrl} found."); + _logger.LogInformation($"No SPA development server running at {_options.ClientUrl} found. {DateTime.Now}"); _launchTask = UpdateStatus(StartSpaProcessAndProbeForLiveness(cancellationToken)); } } @@ -137,9 +137,11 @@ private async Task StartSpaProcessAndProbeForLiveness(CancellationToken cancella var livenessProbeSucceeded = false; var maxTimeoutReached = false; var httpClient = CreateHttpClient(); + while (_spaProcess != null && !_spaProcess.HasExited && !maxTimeoutReached) { livenessProbeSucceeded = await ProbeSpaDevelopmentServerUrl(httpClient, cancellationToken); + if (livenessProbeSucceeded) { break; @@ -152,6 +154,7 @@ private async Task StartSpaProcessAndProbeForLiveness(CancellationToken cancella maxTimeoutReached = sw.Elapsed >= _options.MaxTimeout; await Task.Delay(1000, cancellationToken); + } if (_spaProcess == null || _spaProcess.HasExited) diff --git a/src/AspNetCore.SpaYarp/SpaProxyMiddleware.cs b/src/AspNetCore.SpaYarp/SpaProxyMiddleware.cs index 42110d9..78743e4 100644 --- a/src/AspNetCore.SpaYarp/SpaProxyMiddleware.cs +++ b/src/AspNetCore.SpaYarp/SpaProxyMiddleware.cs @@ -41,23 +41,35 @@ public SpaProxyMiddleware( public async Task Invoke(HttpContext context) { + if (!_spaClientRunning && !await _spaProxyLaunchManager.IsSpaClientRunning(context.RequestAborted)) { - _spaProxyLaunchManager.StartInBackground(_hostLifetime.ApplicationStopping); - _logger.LogInformation("SPA client is not ready. Returning temporary landing page."); - context.Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; - context.Response.ContentType = "text/html"; + if (_spaClientRunning) + { // catch race condition from pending invokes + _logger.LogWarning($"SPA client already started. {DateTime.Now}"); + } + else + { + _spaProxyLaunchManager.StartInBackground(_hostLifetime.ApplicationStopping); + _logger.LogInformation($"SPA client is not ready. Returning temporary landing page. {DateTime.Now}"); + context.Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; + context.Response.ContentType = "text/html"; - await using var writer = new StreamWriter(context.Response.Body, Encoding.UTF8); - await writer.WriteAsync(GenerateSpaLaunchPage(_options.Value)); + await using var writer = new StreamWriter(context.Response.Body, Encoding.UTF8); + await writer.WriteAsync(GenerateSpaLaunchPage(_options.Value)); + } } else { - _logger.LogInformation($"SPA client is ready."); - _spaClientRunning = true; + _spaClientRunning = true; // set flag before logger call + _logger.LogInformation($"SPA client is ready. {DateTime.Now}"); await _next(context); } + /** + * This page hasa refresh of 3 seconds, which causes Invoke to get called again + * as the server handles the request + */ string GenerateSpaLaunchPage(SpaDevelopmentServerOptions options) { return $@"