From 18faf4ad7ac4ad01f4cf421403f08964510ff004 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Tue, 7 May 2019 12:03:11 -0700 Subject: [PATCH] Make inprocess not set CaptureStartupErrors and let ANCM handle it. (#9859) --- src/Hosting/Hosting/src/Internal/WebHost.cs | 6 ++- src/Hosting/Hosting/test/WebHostTests.cs | 40 +++++++++++++++++++ .../IIS/src/WebHostBuilderIISExtensions.cs | 2 - .../Inprocess/StartupTests.cs | 24 +++++++++++ .../InProcessWebSite/InProcessWebSite.csproj | 1 + .../testassets/InProcessWebSite/Program.cs | 36 +++++++++++++++++ .../InProcessWebSite/ThrowingStartup.cs | 17 ++++++++ 7 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/Servers/IIS/IIS/test/testassets/InProcessWebSite/ThrowingStartup.cs diff --git a/src/Hosting/Hosting/src/Internal/WebHost.cs b/src/Hosting/Hosting/src/Internal/WebHost.cs index 1aa779ee37..20f9ff7b0b 100644 --- a/src/Hosting/Hosting/src/Internal/WebHost.cs +++ b/src/Hosting/Hosting/src/Internal/WebHost.cs @@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal private ILogger _logger; private bool _stopped; + private bool _startedServer; // Used for testing only internal WebHostOptions Options => _options; @@ -151,6 +152,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal var httpContextFactory = _applicationServices.GetRequiredService(); var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory); await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); + _startedServer = true; // Fire IApplicationLifetime.Started _applicationLifetime?.NotifyStarted(); @@ -330,13 +332,13 @@ namespace Microsoft.AspNetCore.Hosting.Internal // Fire IApplicationLifetime.Stopping _applicationLifetime?.StopApplication(); - if (Server != null) + if (Server != null && _startedServer) { await Server.StopAsync(cancellationToken).ConfigureAwait(false); } // Fire the IHostedService.Stop - if (_hostedServiceExecutor != null) + if (_hostedServiceExecutor != null && _startedServer) { await _hostedServiceExecutor.StopAsync(cancellationToken).ConfigureAwait(false); } diff --git a/src/Hosting/Hosting/test/WebHostTests.cs b/src/Hosting/Hosting/test/WebHostTests.cs index 74e456dff6..5d3e7139c7 100644 --- a/src/Hosting/Hosting/test/WebHostTests.cs +++ b/src/Hosting/Hosting/test/WebHostTests.cs @@ -1024,6 +1024,46 @@ namespace Microsoft.AspNetCore.Hosting providerMock.Verify(c => c.Dispose(), Times.AtLeastOnce()); } + [Fact] + public async Task DoesNotCallServerStopIfServerStartHasNotBeenCalled() + { + var server = new Mock(); + + using (var host = CreateBuilder() + .ConfigureServices(services => + { + services.AddSingleton(server.Object); + }) + .UseStartup("Microsoft.AspNetCore.Hosting.Tests") + .Build()) + { + await host.StopAsync(); + } + + server.VerifyNoOtherCalls(); + } + + [Fact] + public async Task DoesNotCallServerStopIfServerStartHasNotBeenCalledIHostedService() + { + var server = new Mock(); + + using (var host = CreateBuilder() + .ConfigureServices(services => + { + services.AddSingleton(server.Object); + services.AddSingleton(); + }) + .UseStartup("Microsoft.AspNetCore.Hosting.Tests") + .Build()) + { + var svc = (TestHostedService)host.Services.GetRequiredService(); + await svc.StopAsync(default); + } + + server.VerifyNoOtherCalls(); + } + public class BadConfigureServicesStartup { public void ConfigureServices(IServiceCollection services, int gunk) { } diff --git a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs index d4d012454f..1f1480b1d3 100644 --- a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs +++ b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs @@ -30,8 +30,6 @@ namespace Microsoft.AspNetCore.Hosting // Check if in process if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && NativeMethods.IsAspNetCoreModuleLoaded()) { - hostBuilder.CaptureStartupErrors(true); - var iisConfigData = NativeMethods.HttpGetApplicationProperties(); // Trim trailing slash to be consistent with other servers var contentRoot = iisConfigData.pwzFullApplicationPath.TrimEnd(Path.DirectorySeparatorChar); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index d67b8eae44..5f2a0f70aa 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -604,6 +604,30 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests VerifyDotnetRuntimeEventLog(deploymentResult); } + [ConditionalTheory] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + [RequiresNewHandler] + [InlineData("ThrowInStartup")] + [InlineData("ThrowInStartupGenericHost")] + public async Task ExceptionIsLoggedToEventLogAndPutInResponseDuringHostingStartupProcess(string startupType) + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.TransformArguments((a, _) => $"{a} {startupType}"); + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(result.IsSuccessStatusCode); + + var content = await result.Content.ReadAsStringAsync(); + Assert.Contains("InvalidOperationException", content); + Assert.Contains("TestSite.Program.Main", content); + Assert.Contains("From Configure", content); + + StopServer(); + + VerifyDotnetRuntimeEventLog(deploymentResult); + } + [ConditionalFact] [RequiresIIS(IISCapability.PoolEnvironmentVariables)] [RequiresNewHandler] diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj index 2e364ba379..3faf04f7e4 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj @@ -25,6 +25,7 @@ + diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs index 3ece63ede5..db543f266f 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace TestSite @@ -121,6 +122,41 @@ namespace TestSite break; } #endif + case "ThrowInStartup": + { + var host = new WebHostBuilder() + .ConfigureLogging((_, factory) => + { + factory.AddConsole(); + factory.AddFilter("Console", level => level >= LogLevel.Information); + }) + .UseIIS() + .UseStartup() + .Build(); + + host.Run(); + } + + return 0; + case "ThrowInStartupGenericHost": + { +#if !FORWARDCOMPAT + var host = new HostBuilder().ConfigureWebHost((c) => + { + c.ConfigureLogging((_, factory) => + { + factory.AddConsole(); + factory.AddFilter("Console", level => level >= LogLevel.Information); + }) + .UseIIS() + .UseStartup(); + }); + + + host.Build().Run(); +#endif + } + return 0; default: return StartServer(); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/ThrowingStartup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/ThrowingStartup.cs new file mode 100644 index 0000000000..9247ce3aa8 --- /dev/null +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/ThrowingStartup.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace TestSite +{ + public class ThrowingStartup + { + public void Configure(IApplicationBuilder app) + { + throw new InvalidOperationException("From Configure"); + } + } +}